aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/sceneentity.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/sceneentity.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/sceneentity.cpp')
-rw-r--r--sp/src/game/server/sceneentity.cpp11312
1 files changed, 5656 insertions, 5656 deletions
diff --git a/sp/src/game/server/sceneentity.cpp b/sp/src/game/server/sceneentity.cpp
index 69fd2f95..495aa004 100644
--- a/sp/src/game/server/sceneentity.cpp
+++ b/sp/src/game/server/sceneentity.cpp
@@ -1,5656 +1,5656 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include <stdarg.h>
-#include "baseflex.h"
-#include "entitylist.h"
-#include "choreoevent.h"
-#include "choreoactor.h"
-#include "choreochannel.h"
-#include "choreoscene.h"
-#include "studio.h"
-#include "networkstringtable_gamedll.h"
-#include "ai_basenpc.h"
-#include "engine/IEngineSound.h"
-#include "ai_navigator.h"
-#include "saverestore_utlvector.h"
-#include "ai_baseactor.h"
-#include "AI_Criteria.h"
-#include "tier1/strtools.h"
-#include "checksum_crc.h"
-#include "SoundEmitterSystem/isoundemittersystembase.h"
-#include "utlbuffer.h"
-#include "tier0/icommandline.h"
-#include "sceneentity.h"
-#include "datacache/idatacache.h"
-#include "dt_utlvector_send.h"
-#include "ichoreoeventcallback.h"
-#include "scenefilecache/ISceneFileCache.h"
-#include "SceneCache.h"
-#include "scripted.h"
-#include "env_debughistory.h"
-
-#ifdef HL2_EPISODIC
-#include "npc_alyx_episodic.h"
-#endif // HL2_EPISODIC
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-extern ISoundEmitterSystemBase *soundemitterbase;
-extern ISceneFileCache *scenefilecache;
-
-class CSceneEntity;
-class CBaseFlex;
-
-// VCDS are loaded from their compiled/binary format (much faster)
-// Requies vcds be saved as compiled assets
-//#define COMPILED_VCDS 1
-
-static ConVar scene_forcecombined( "scene_forcecombined", "0", 0, "When playing back, force use of combined .wav files even in english." );
-static ConVar scene_maxcaptionradius( "scene_maxcaptionradius", "1200", 0, "Only show closed captions if recipient is within this many units of speaking actor (0==disabled)." );
-
-// Assume sound system is 100 msec lagged (only used if we can't find snd_mixahead cvar!)
-#define SOUND_SYSTEM_LATENCY_DEFAULT ( 0.1f )
-
-// Think every 50 msec (FIXME: Try 10hz?)
-#define SCENE_THINK_INTERVAL 0.001 // FIXME: make scene's think in concert with their npc's
-
-#define FINDNAMEDENTITY_MAX_ENTITIES 32 // max number of entities to be considered for random entity selection in FindNamedEntity
-
-// List of the last 5 lines of speech from NPCs for bug reports
-static recentNPCSpeech_t speechListSounds[ SPEECH_LIST_MAX_SOUNDS ] = { { 0, "", "" }, { 0, "", "" }, { 0, "", "" }, { 0, "", "" }, { 0, "", "" } };
-static int speechListIndex = 0;
-
-// Only allow scenes to change their pitch within a range of values
-#define SCENE_MIN_PITCH 0.25f
-#define SCENE_MAX_PITCH 2.5f
-
-//===========================================================================================================
-// SCENE LIST MANAGER
-//===========================================================================================================
-#define SCENE_LIST_MANAGER_MAX_SCENES 16
-
-//-----------------------------------------------------------------------------
-// Purpose: Entity that manages a list of scenes
-//-----------------------------------------------------------------------------
-class CSceneListManager : public CLogicalEntity
-{
- DECLARE_CLASS( CSceneListManager, CLogicalEntity );
-public:
- DECLARE_DATADESC();
-
- virtual void Activate( void );
-
- void ShutdownList( void );
- void SceneStarted( CBaseEntity *pSceneOrManager );
- void AddListManager( CSceneListManager *pManager );
- void RemoveScene( int iIndex );
-
- // Inputs
- void InputShutdown( inputdata_t &inputdata );
-
-private:
- CUtlVector< CHandle< CSceneListManager > > m_hListManagers;
- string_t m_iszScenes[SCENE_LIST_MANAGER_MAX_SCENES];
- EHANDLE m_hScenes[SCENE_LIST_MANAGER_MAX_SCENES];
-};
-
-//-----------------------------------------------------------------------------
-// Purpose: This class exists solely to call think on all scene entities in a deterministic order
-//-----------------------------------------------------------------------------
-class CSceneManager : public CBaseEntity
-{
- DECLARE_CLASS( CSceneManager, CBaseEntity );
- DECLARE_DATADESC();
-
-public:
- virtual void Spawn()
- {
- BaseClass::Spawn();
- SetNextThink( gpGlobals->curtime );
- }
-
- virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_DONT_SAVE; }
-
- virtual void Think();
-
- void ClearAllScenes();
-
- void AddSceneEntity( CSceneEntity *scene );
- void RemoveSceneEntity( CSceneEntity *scene );
-
- void QueueRestoredSound( CBaseFlex *actor, char const *soundname, soundlevel_t soundlevel, float time_in_past );
-
- void OnClientActive( CBasePlayer *player );
-
- void RemoveActorFromScenes( CBaseFlex *pActor, bool bInstancedOnly, bool bNonIdleOnly, const char *pszThisSceneOnly );
- void RemoveScenesInvolvingActor( CBaseFlex *pActor );
- void PauseActorsScenes( CBaseFlex *pActor, bool bInstancedOnly );
- bool IsInInterruptableScenes( CBaseFlex *pActor );
- void ResumeActorsScenes( CBaseFlex *pActor, bool bInstancedOnly );
- void QueueActorsScenesToResume( CBaseFlex *pActor, bool bInstancedOnly );
- bool IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes );
- bool IsRunningScriptedSceneAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes );
- bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes );
- bool IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes );
-
-
-private:
-
- struct CRestoreSceneSound
- {
- CRestoreSceneSound()
- {
- actor = NULL;
- soundname[ 0 ] = NULL;
- soundlevel = SNDLVL_NORM;
- time_in_past = 0.0f;
- }
-
- CHandle< CBaseFlex > actor;
- char soundname[ 128 ];
- soundlevel_t soundlevel;
- float time_in_past;
- };
-
- CUtlVector< CHandle< CSceneEntity > > m_ActiveScenes;
-
- CUtlVector< CRestoreSceneSound > m_QueuedSceneSounds;
-};
-
-//---------------------------------------------------------
-// Save/Restore
-//---------------------------------------------------------
-BEGIN_DATADESC( CSceneManager )
-
- DEFINE_UTLVECTOR( m_ActiveScenes, FIELD_EHANDLE ),
- // DEFINE_FIELD( m_QueuedSceneSounds, CUtlVector < CRestoreSceneSound > ), // Don't save/restore this, it's created and used by OnRestore only
-
-END_DATADESC()
-
-#ifdef DISABLE_DEBUG_HISTORY
-#define LocalScene_Printf Scene_Printf
-#else
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pFormat -
-// ... -
-// Output : static void
-//-----------------------------------------------------------------------------
-void LocalScene_Printf( const char *pFormat, ... )
-{
- va_list marker;
- char msg[8192];
-
- va_start(marker, pFormat);
- Q_vsnprintf(msg, sizeof(msg), pFormat, marker);
- va_end(marker);
-
- Scene_Printf( "%s", msg );
- ADD_DEBUG_HISTORY( HISTORY_SCENE_PRINT, UTIL_VarArgs( "(%0.2f) %s", gpGlobals->curtime, msg ) );
-}
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *filename -
-// **buffer -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CopySceneFileIntoMemory( char const *pFilename, void **pBuffer, int *pSize )
-{
- size_t bufSize = scenefilecache->GetSceneBufferSize( pFilename );
- if ( bufSize > 0 )
- {
- *pBuffer = new byte[bufSize];
- *pSize = bufSize;
- return scenefilecache->GetSceneData( pFilename, (byte *)(*pBuffer), bufSize );
- }
-
- *pBuffer = 0;
- *pSize = 0;
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void FreeSceneFileMemory( void *buffer )
-{
- delete[] (byte*) buffer;
-}
-
-//-----------------------------------------------------------------------------
-// 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;
-
-//-----------------------------------------------------------------------------
-// Purpose: Singleton scene manager. Created by first placed scene or recreated it it's deleted for some unknown reason
-// Output : CSceneManager
-//-----------------------------------------------------------------------------
-CSceneManager *GetSceneManager()
-{
- // Create it if it doesn't exist
- static CHandle< CSceneManager > s_SceneManager;
- if ( s_SceneManager == NULL )
- {
- s_SceneManager = ( CSceneManager * )CreateEntityByName( "scene_manager" );
- Assert( s_SceneManager );
- if ( s_SceneManager )
- {
- s_SceneManager->Spawn();
- }
- }
-
- Assert( s_SceneManager );
- return s_SceneManager;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *player -
-//-----------------------------------------------------------------------------
-void SceneManager_ClientActive( CBasePlayer *player )
-{
- Assert( GetSceneManager() );
-
- if ( GetSceneManager() )
- {
- GetSceneManager()->OnClientActive( player );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: FIXME, need to deal with save/restore
-//-----------------------------------------------------------------------------
-class CSceneEntity : public CPointEntity, public IChoreoEventCallback
-{
- friend class CInstancedSceneEntity;
-public:
-
- enum
- {
- SCENE_ACTION_UNKNOWN = 0,
- SCENE_ACTION_CANCEL,
- SCENE_ACTION_RESUME,
- };
-
- enum
- {
- SCENE_BUSYACTOR_DEFAULT = 0,
- SCENE_BUSYACTOR_WAIT,
- SCENE_BUSYACTOR_INTERRUPT,
- SCENE_BUSYACTOR_INTERRUPT_CANCEL,
- };
-
-
-
-
- DECLARE_CLASS( CSceneEntity, CPointEntity );
- DECLARE_SERVERCLASS();
-
- CSceneEntity( void );
- ~CSceneEntity( void );
-
- // From IChoreoEventCallback
- virtual void StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
- virtual void EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
- virtual void ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
- virtual bool CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
-
-
- virtual int UpdateTransmitState();
- virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
-
- void SetRecipientFilter( IRecipientFilter *filter );
-
- virtual void Activate();
-
- virtual void Precache( void );
- virtual void Spawn( void );
- virtual void UpdateOnRemove( void );
-
- virtual void OnRestore();
- virtual void OnLoaded();
-
- DECLARE_DATADESC();
-
- virtual void OnSceneFinished( bool canceled, bool fireoutput );
-
- virtual void DoThink( float frametime );
- virtual void PauseThink( void );
-
- bool IsPlayingBack() const { return m_bIsPlayingBack; }
- bool IsPaused() const { return m_bPaused; }
- bool IsMultiplayer() const { return m_bMultiplayer; }
-
- bool IsInterruptable();
- virtual void ClearInterrupt();
- virtual void CheckInterruptCompletion();
-
- virtual bool InterruptThisScene( CSceneEntity *otherScene );
- void RequestCompletionNotification( CSceneEntity *otherScene );
-
- virtual void NotifyOfCompletion( CSceneEntity *interruptor );
-
- void AddListManager( CSceneListManager *pManager );
-
- void ClearActivatorTargets( void );
-
- void SetBreakOnNonIdle( bool bBreakOnNonIdle ) { m_bBreakOnNonIdle = bBreakOnNonIdle; }
- bool ShouldBreakOnNonIdle( void ) { return m_bBreakOnNonIdle; }
-
- // Inputs
- void InputStartPlayback( inputdata_t &inputdata );
- void InputPausePlayback( inputdata_t &inputdata );
- void InputResumePlayback( inputdata_t &inputdata );
- void InputCancelPlayback( inputdata_t &inputdata );
- void InputCancelAtNextInterrupt( inputdata_t &inputdata );
- void InputPitchShiftPlayback( inputdata_t &inputdata );
- void InputTriggerEvent( inputdata_t &inputdata );
-
- // If the scene is playing, finds an actor in the scene who can respond to the specified concept token
- void InputInterjectResponse( inputdata_t &inputdata );
-
- // If this scene is waiting on an actor, give up and quit trying.
- void InputStopWaitingForActor( inputdata_t &inputdata );
-
- virtual void StartPlayback( void );
- virtual void PausePlayback( void );
- virtual void ResumePlayback( void );
- virtual void CancelPlayback( void );
- virtual void PitchShiftPlayback( float fPitch );
- virtual void QueueResumePlayback( void );
-
- bool ValidScene() const;
-
- // Scene load/unload
- static CChoreoScene *LoadScene( const char *filename, IChoreoEventCallback *pCallback );
-
- void UnloadScene( void );
-
- struct SpeakEventSound_t
- {
- CUtlSymbol m_Symbol;
- float m_flStartTime;
- };
-
- static bool SpeakEventSoundLessFunc( const SpeakEventSound_t& lhs, const SpeakEventSound_t& rhs );
-
- bool GetSoundNameForPlayer( CChoreoEvent *event, CBasePlayer *player, char *buf, size_t buflen, CBaseEntity *pActor );
-
- void BuildSortedSpeakEventSoundsPrefetchList(
- CChoreoScene *scene,
- CUtlSymbolTable& table,
- CUtlRBTree< SpeakEventSound_t >& soundnames,
- float timeOffset );
- void PrefetchSpeakEventSounds( CUtlSymbolTable& table, CUtlRBTree< SpeakEventSound_t >& soundnames );
-
- // Event handlers
- virtual void DispatchStartExpression( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchEndExpression( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartFlexAnimation( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchEndFlexAnimation( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartGesture( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchEndGesture( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartLookAt( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event );
- virtual void DispatchEndLookAt( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartMoveTo( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event );
- virtual void DispatchEndMoveTo( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartSpeak( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event, soundlevel_t iSoundlevel );
- virtual void DispatchEndSpeak( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartFace( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event );
- virtual void DispatchEndFace( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartSubScene( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchStartInterrupt( CChoreoScene *scene, CChoreoEvent *event );
- virtual void DispatchEndInterrupt( CChoreoScene *scene, CChoreoEvent *event );
- virtual void DispatchStartGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchEndGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
-
- // NPC can play interstitial vcds (such as responding to the player doing something during a scene)
- virtual void DispatchStartPermitResponses( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
- virtual void DispatchEndPermitResponses( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
-
-
- // Global events
- virtual void DispatchProcessLoop( CChoreoScene *scene, CChoreoEvent *event );
- virtual void DispatchPauseScene( CChoreoScene *scene, const char *parameters );
- virtual void DispatchStopPoint( CChoreoScene *scene, const char *parameters );
-
- virtual float EstimateLength( void );
-
- void CancelIfSceneInvolvesActor( CBaseEntity *pActor );
- bool InvolvesActor( CBaseEntity *pActor ); // NOTE: returns false if scene hasn't loaded yet
-
- void GenerateSoundScene( CBaseFlex *pActor, const char *soundname );
-
- virtual float GetPostSpeakDelay() { return 1.0; }
-
- bool HasUnplayedSpeech( void );
- bool HasFlexAnimation( void );
-
- void SetCurrentTime( float t, bool forceClientSync );
-
- void InputScriptPlayerDeath( inputdata_t &inputdata );
-
-// Data
-public:
- string_t m_iszSceneFile;
-
- string_t m_iszResumeSceneFile;
- EHANDLE m_hWaitingForThisResumeScene;
- bool m_bWaitingForResumeScene;
-
- string_t m_iszTarget1;
- string_t m_iszTarget2;
- string_t m_iszTarget3;
- string_t m_iszTarget4;
- string_t m_iszTarget5;
- string_t m_iszTarget6;
- string_t m_iszTarget7;
- string_t m_iszTarget8;
-
- EHANDLE m_hTarget1;
- EHANDLE m_hTarget2;
- EHANDLE m_hTarget3;
- EHANDLE m_hTarget4;
- EHANDLE m_hTarget5;
- EHANDLE m_hTarget6;
- EHANDLE m_hTarget7;
- EHANDLE m_hTarget8;
-
- CNetworkVar( bool, m_bIsPlayingBack );
- CNetworkVar( bool, m_bPaused );
- CNetworkVar( bool, m_bMultiplayer );
- CNetworkVar( float, m_flForceClientTime );
-
- float m_flCurrentTime;
- float m_flFrameTime;
- bool m_bCancelAtNextInterrupt;
-
- float m_fPitch;
-
- bool m_bAutomated;
- int m_nAutomatedAction;
- float m_flAutomationDelay;
- float m_flAutomationTime;
-
- // A pause from an input requires another input to unpause (it's a hard pause)
- bool m_bPausedViaInput;
-
- // Waiting for the actor to be able to speak.
- bool m_bWaitingForActor;
-
- // Waiting for a point at which we can interrupt our actors
- bool m_bWaitingForInterrupt;
- bool m_bInterruptedActorsScenes;
-
- bool m_bBreakOnNonIdle;
-
-public:
- virtual CBaseFlex *FindNamedActor( int index );
- virtual CBaseFlex *FindNamedActor( CChoreoActor *pChoreoActor );
- virtual CBaseFlex *FindNamedActor( const char *name );
- virtual CBaseEntity *FindNamedEntity( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false );
- CBaseEntity *FindNamedTarget( string_t iszTarget, bool bBaseFlexOnly = false );
- virtual CBaseEntity *FindNamedEntityClosest( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false, const char *pszSecondary = NULL );
-
-private:
-
- CUtlVector< CHandle< CBaseFlex > > m_hActorList;
- CUtlVector< CHandle< CBaseEntity > > m_hRemoveActorList;
-
-private:
-
- inline void SetRestoring( bool bRestoring );
-
- // Prevent derived classed from using this!
- virtual void Think( void ) {};
-
-
- void ClearSceneEvents( CChoreoScene *scene, bool canceled );
- void ClearSchedules( CChoreoScene *scene );
-
- float GetSoundSystemLatency( void );
- void PrecacheScene( CChoreoScene *scene );
-
- CChoreoScene *GenerateSceneForSound( CBaseFlex *pFlexActor, const char *soundname );
-
- bool CheckActors();
-
- void PrefetchAnimBlocks( CChoreoScene *scene );
-
- bool ShouldNetwork() const;
- // Set if we tried to async the scene but the FS returned that the data was not loadable
- bool m_bSceneMissing;
-
- CChoreoScene *m_pScene;
- CNetworkVar( int, m_nSceneStringIndex );
-
- static const ConVar *m_pcvSndMixahead;
-
- COutputEvent m_OnStart;
- COutputEvent m_OnCompletion;
- COutputEvent m_OnCanceled;
- COutputEvent m_OnTrigger1;
- COutputEvent m_OnTrigger2;
- COutputEvent m_OnTrigger3;
- COutputEvent m_OnTrigger4;
- COutputEvent m_OnTrigger5;
- COutputEvent m_OnTrigger6;
- COutputEvent m_OnTrigger7;
- COutputEvent m_OnTrigger8;
- COutputEvent m_OnTrigger9;
- COutputEvent m_OnTrigger10;
- COutputEvent m_OnTrigger11;
- COutputEvent m_OnTrigger12;
- COutputEvent m_OnTrigger13;
- COutputEvent m_OnTrigger14;
- COutputEvent m_OnTrigger15;
- COutputEvent m_OnTrigger16;
-
- int m_nInterruptCount;
- bool m_bInterrupted;
- CHandle< CSceneEntity > m_hInterruptScene;
-
- bool m_bCompletedEarly;
-
- bool m_bInterruptSceneFinished;
- CUtlVector< CHandle< CSceneEntity > > m_hNotifySceneCompletion;
- CUtlVector< CHandle< CSceneListManager > > m_hListManagers;
-
- bool m_bRestoring;
-
- bool m_bGenerated;
- string_t m_iszSoundName;
- CHandle< CBaseFlex > m_hActor;
-
- EHANDLE m_hActivator;
-
- int m_BusyActor;
-
- int m_iPlayerDeathBehavior;
-
- CRecipientFilter *m_pRecipientFilter;
-
-public:
- void SetBackground( bool bIsBackground );
- bool IsBackground( void );
-};
-
-LINK_ENTITY_TO_CLASS( logic_choreographed_scene, CSceneEntity );
-LINK_ENTITY_TO_CLASS( scripted_scene, CSceneEntity );
-
-IMPLEMENT_SERVERCLASS_ST_NOBASE( CSceneEntity, DT_SceneEntity )
- SendPropInt(SENDINFO(m_nSceneStringIndex),MAX_CHOREO_SCENES_STRING_BITS,SPROP_UNSIGNED),
- SendPropBool(SENDINFO(m_bIsPlayingBack)),
- SendPropBool(SENDINFO(m_bPaused)),
- SendPropBool(SENDINFO(m_bMultiplayer)),
- SendPropFloat(SENDINFO(m_flForceClientTime)),
- SendPropUtlVector(
- SENDINFO_UTLVECTOR( m_hActorList ),
- MAX_ACTORS_IN_SCENE, // max elements
- SendPropEHandle( NULL, 0 ) ),
-END_SEND_TABLE()
-
-BEGIN_DATADESC( CSceneEntity )
-
- // Keys
- DEFINE_KEYFIELD( m_iszSceneFile, FIELD_STRING, "SceneFile" ),
- DEFINE_KEYFIELD( m_iszResumeSceneFile, FIELD_STRING, "ResumeSceneFile" ),
- DEFINE_FIELD( m_hWaitingForThisResumeScene, FIELD_EHANDLE ),
- DEFINE_FIELD( m_bWaitingForResumeScene, FIELD_BOOLEAN ),
-
- DEFINE_KEYFIELD( m_iszTarget1, FIELD_STRING, "target1" ),
- DEFINE_KEYFIELD( m_iszTarget2, FIELD_STRING, "target2" ),
- DEFINE_KEYFIELD( m_iszTarget3, FIELD_STRING, "target3" ),
- DEFINE_KEYFIELD( m_iszTarget4, FIELD_STRING, "target4" ),
- DEFINE_KEYFIELD( m_iszTarget5, FIELD_STRING, "target5" ),
- DEFINE_KEYFIELD( m_iszTarget6, FIELD_STRING, "target6" ),
- DEFINE_KEYFIELD( m_iszTarget7, FIELD_STRING, "target7" ),
- DEFINE_KEYFIELD( m_iszTarget8, FIELD_STRING, "target8" ),
-
- DEFINE_KEYFIELD( m_BusyActor, FIELD_INTEGER, "busyactor" ),
-
- DEFINE_FIELD( m_hTarget1, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hTarget2, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hTarget3, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hTarget4, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hTarget5, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hTarget6, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hTarget7, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hTarget8, FIELD_EHANDLE ),
-
- DEFINE_FIELD( m_bIsPlayingBack, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bPaused, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_flCurrentTime, FIELD_FLOAT ), // relative, not absolute time
- DEFINE_FIELD( m_flForceClientTime, FIELD_FLOAT ),
- DEFINE_FIELD( m_flFrameTime, FIELD_FLOAT ), // last frametime
- DEFINE_FIELD( m_bCancelAtNextInterrupt, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_fPitch, FIELD_FLOAT ),
- DEFINE_FIELD( m_bAutomated, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_nAutomatedAction, FIELD_INTEGER ),
- DEFINE_FIELD( m_flAutomationDelay, FIELD_FLOAT ),
- DEFINE_FIELD( m_flAutomationTime, FIELD_FLOAT ), // relative, not absolute time
-
- DEFINE_FIELD( m_bPausedViaInput, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bWaitingForActor, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bWaitingForInterrupt, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bInterruptedActorsScenes, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bBreakOnNonIdle, FIELD_BOOLEAN ),
-
- DEFINE_UTLVECTOR( m_hActorList, FIELD_EHANDLE ),
- DEFINE_UTLVECTOR( m_hRemoveActorList, FIELD_EHANDLE ),
-
- // DEFINE_FIELD( m_pScene, FIELD_XXXX ) // Special processing used for this
-
- // These are set up in the constructor
- // DEFINE_FIELD( m_pcvSndMixahead, FIELD_XXXXX ),
- // DEFINE_FIELD( m_bRestoring, FIELD_BOOLEAN ),
-
- DEFINE_FIELD( m_nInterruptCount, FIELD_INTEGER ),
- DEFINE_FIELD( m_bInterrupted, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_hInterruptScene, FIELD_EHANDLE ),
- DEFINE_FIELD( m_bCompletedEarly, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bInterruptSceneFinished, FIELD_BOOLEAN ),
-
- DEFINE_FIELD( m_bGenerated, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_iszSoundName, FIELD_STRING ),
- DEFINE_FIELD( m_hActor, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
-
- // DEFINE_FIELD( m_bSceneMissing, FIELD_BOOLEAN ),
- DEFINE_UTLVECTOR( m_hNotifySceneCompletion, FIELD_EHANDLE ),
- DEFINE_UTLVECTOR( m_hListManagers, FIELD_EHANDLE ),
-
- DEFINE_FIELD( m_bMultiplayer, FIELD_BOOLEAN ),
-// DEFINE_FIELD( m_nSceneStringIndex, FIELD_INTEGER ),
-
- // DEFINE_FIELD( m_pRecipientFilter, IRecipientFilter* ), // Multiplayer only
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_VOID, "Start", InputStartPlayback ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Pause", InputPausePlayback ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Resume", InputResumePlayback ),
- DEFINE_INPUTFUNC( FIELD_VOID, "Cancel", InputCancelPlayback ),
- DEFINE_INPUTFUNC( FIELD_VOID, "CancelAtNextInterrupt", InputCancelAtNextInterrupt ),
- DEFINE_INPUTFUNC( FIELD_FLOAT, "PitchShift", InputPitchShiftPlayback ),
- DEFINE_INPUTFUNC( FIELD_STRING, "InterjectResponse", InputInterjectResponse ),
- DEFINE_INPUTFUNC( FIELD_VOID, "StopWaitingForActor", InputStopWaitingForActor ),
- DEFINE_INPUTFUNC( FIELD_INTEGER, "Trigger", InputTriggerEvent ),
-
- DEFINE_KEYFIELD( m_iPlayerDeathBehavior, FIELD_INTEGER, "onplayerdeath" ),
- DEFINE_INPUTFUNC( FIELD_VOID, "ScriptPlayerDeath", InputScriptPlayerDeath ),
-
- // Outputs
- DEFINE_OUTPUT( m_OnStart, "OnStart"),
- DEFINE_OUTPUT( m_OnCompletion, "OnCompletion"),
- DEFINE_OUTPUT( m_OnCanceled, "OnCanceled"),
- DEFINE_OUTPUT( m_OnTrigger1, "OnTrigger1"),
- DEFINE_OUTPUT( m_OnTrigger2, "OnTrigger2"),
- DEFINE_OUTPUT( m_OnTrigger3, "OnTrigger3"),
- DEFINE_OUTPUT( m_OnTrigger4, "OnTrigger4"),
- DEFINE_OUTPUT( m_OnTrigger5, "OnTrigger5"),
- DEFINE_OUTPUT( m_OnTrigger6, "OnTrigger6"),
- DEFINE_OUTPUT( m_OnTrigger7, "OnTrigger7"),
- DEFINE_OUTPUT( m_OnTrigger8, "OnTrigger8"),
- DEFINE_OUTPUT( m_OnTrigger9, "OnTrigger9"),
- DEFINE_OUTPUT( m_OnTrigger10, "OnTrigger10"),
- DEFINE_OUTPUT( m_OnTrigger11, "OnTrigger11"),
- DEFINE_OUTPUT( m_OnTrigger12, "OnTrigger12"),
- DEFINE_OUTPUT( m_OnTrigger13, "OnTrigger13"),
- DEFINE_OUTPUT( m_OnTrigger14, "OnTrigger14"),
- DEFINE_OUTPUT( m_OnTrigger15, "OnTrigger15"),
- DEFINE_OUTPUT( m_OnTrigger16, "OnTrigger16"),
-END_DATADESC()
-
-const ConVar *CSceneEntity::m_pcvSndMixahead = NULL;
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CSceneEntity::CSceneEntity( void )
-{
- m_bWaitingForActor = false;
- m_bWaitingForInterrupt = false;
- m_bInterruptedActorsScenes = false;
- m_bIsPlayingBack = false;
- m_bPaused = false;
- m_bMultiplayer = false;
- m_fPitch = 1.0f;
- m_iszSceneFile = NULL_STRING;
- m_iszResumeSceneFile = NULL_STRING;
- m_hWaitingForThisResumeScene = NULL;
- m_bWaitingForResumeScene = false;
- SetCurrentTime( 0.0f, false );
- m_bCancelAtNextInterrupt = false;
-
- m_bAutomated = false;
- m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
- m_flAutomationDelay = 0.0f;
- m_flAutomationTime = 0.0f;
-
- m_bPausedViaInput = false;
- ClearInterrupt();
-
- m_pScene = NULL;
-
- m_bCompletedEarly = false;
-
- if ( !m_pcvSndMixahead )
- m_pcvSndMixahead = cvar->FindVar( "snd_mixahead" );
-
- m_BusyActor = SCENE_BUSYACTOR_DEFAULT;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CSceneEntity::~CSceneEntity( void )
-{
- delete m_pRecipientFilter;
- m_pRecipientFilter = NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Resets time such that the client version of the .vcd is also updated, if appropriate
-// Input : t -
-// forceClientSync - forces new timestamp down to client .dll via networking
-//-----------------------------------------------------------------------------
-void CSceneEntity::SetCurrentTime( float t, bool bForceClientSync )
-{
- m_flCurrentTime = t;
- if ( gpGlobals->maxClients == 1 || bForceClientSync )
- {
- m_flForceClientTime = t;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::UpdateOnRemove( void )
-{
- UnloadScene();
- BaseClass::UpdateOnRemove();
-
- if ( GetSceneManager() )
- {
- GetSceneManager()->RemoveSceneEntity( this );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *soundname -
-// Output : CChoreoScene
-//-----------------------------------------------------------------------------
-CChoreoScene *CSceneEntity::GenerateSceneForSound( CBaseFlex *pFlexActor, const char *soundname )
-{
- float duration = CBaseEntity::GetSoundDuration( soundname, pFlexActor ? STRING( pFlexActor->GetModelName() ) : NULL );
- if( duration <= 0.0f )
- {
- Warning( "CSceneEntity::GenerateSceneForSound: Couldn't determine duration of %s\n", soundname );
- return NULL;
- }
-
- CChoreoScene *scene = new CChoreoScene( this );
- if ( !scene )
- {
- Warning( "CSceneEntity::GenerateSceneForSound: Failed to allocated new scene!!!\n" );
- }
- else
- {
- scene->SetPrintFunc( LocalScene_Printf );
-
-
- CChoreoActor *actor = scene->AllocActor();
- CChoreoChannel *channel = scene->AllocChannel();
- CChoreoEvent *event = scene->AllocEvent();
-
- Assert( actor );
- Assert( channel );
- Assert( event );
-
- if ( !actor || !channel || !event )
- {
- Warning( "CSceneEntity::GenerateSceneForSound: Alloc of actor, channel, or event failed!!!\n" );
- delete scene;
- return NULL;
- }
-
- // Set us up the actorz
- actor->SetName( "!self" ); // Could be pFlexActor->GetName()?
- actor->SetActive( true );
-
- // Set us up the channelz
- channel->SetName( STRING( m_iszSceneFile ) );
- channel->SetActor( actor );
-
- // Add to actor
- actor->AddChannel( channel );
-
- // Set us up the eventz
- event->SetType( CChoreoEvent::SPEAK );
- event->SetName( soundname );
- event->SetParameters( soundname );
- event->SetStartTime( 0.0f );
- event->SetUsingRelativeTag( false );
- event->SetEndTime( duration );
- event->SnapTimes();
-
- // Add to channel
- channel->AddEvent( event );
-
- // Point back to our owners
- event->SetChannel( channel );
- event->SetActor( actor );
-
- }
-
- return scene;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::Activate()
-{
- if ( m_bGenerated && !m_pScene )
- {
- m_pScene = GenerateSceneForSound( m_hActor, STRING( m_iszSoundName ) );
- }
-
- BaseClass::Activate();
-
- if ( GetSceneManager() )
- {
- GetSceneManager()->AddSceneEntity( this );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : float
-//-----------------------------------------------------------------------------
-float CSceneEntity::GetSoundSystemLatency( void )
-{
- if ( m_pcvSndMixahead )
- {
- return m_pcvSndMixahead->GetFloat();
- }
-
- // Assume 100 msec sound system latency
- return SOUND_SYSTEM_LATENCY_DEFAULT;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *scene -
-//-----------------------------------------------------------------------------
-void CSceneEntity::PrecacheScene( CChoreoScene *scene )
-{
- Assert( scene );
-
- // Iterate events and precache necessary resources
- for ( int i = 0; i < scene->GetNumEvents(); i++ )
- {
- CChoreoEvent *event = scene->GetEvent( i );
- if ( !event )
- continue;
-
- // load any necessary data
- switch (event->GetType() )
- {
- default:
- break;
- case CChoreoEvent::SPEAK:
- {
- // Defined in SoundEmitterSystem.cpp
- // NOTE: The script entries associated with .vcds are forced to preload to avoid
- // loading hitches during triggering
- PrecacheScriptSound( event->GetParameters() );
-
- if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER &&
- event->GetNumSlaves() > 0 )
- {
- char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
- if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
- {
- PrecacheScriptSound( tok );
- }
- }
- }
- break;
- case CChoreoEvent::SUBSCENE:
- {
- // Only allow a single level of subscenes for now
- if ( !scene->IsSubScene() )
- {
- CChoreoScene *subscene = event->GetSubScene();
- if ( !subscene )
- {
- subscene = LoadScene( event->GetParameters(), this );
- subscene->SetSubScene( true );
- event->SetSubScene( subscene );
-
- // Now precache it's resources, if any
- PrecacheScene( subscene );
- }
- }
- }
- break;
- }
- }
-}
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::Precache( void )
-{
- if ( m_bGenerated )
- return;
-
- if ( m_iszSceneFile == NULL_STRING )
- return;
-
- if ( m_iszResumeSceneFile != NULL_STRING )
- {
- PrecacheInstancedScene( STRING( m_iszResumeSceneFile ) );
- }
-
- PrecacheInstancedScene( STRING( m_iszSceneFile ) );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pActor -
-// *soundname -
-//-----------------------------------------------------------------------------
-void CSceneEntity::GenerateSoundScene( CBaseFlex *pActor, const char *soundname )
-{
- m_bGenerated = true;
- m_iszSoundName = MAKE_STRING( soundname );
- m_hActor = pActor;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CSceneEntity::HasUnplayedSpeech( void )
-{
- if ( m_pScene )
- return m_pScene->HasUnplayedSpeech();
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CSceneEntity::HasFlexAnimation( void )
-{
- if ( m_pScene )
- return m_pScene->HasFlexAnimation();
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output :
-//-----------------------------------------------------------------------------
-
-void CSceneEntity::SetBackground( bool bIsBackground )
-{
- if ( m_pScene )
- {
- m_pScene->SetBackground( bIsBackground );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-
-bool CSceneEntity::IsBackground( void )
-{
- if ( m_pScene )
- return m_pScene->IsBackground( );
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::OnRestore()
-{
- BaseClass::OnRestore();
-
- // Fix saved games that have their pitch set to zero
- if ( m_fPitch < SCENE_MIN_PITCH || m_fPitch > SCENE_MAX_PITCH )
- m_fPitch = 1.0f;
-
- if ( !m_bIsPlayingBack )
- return;
-
- if ( !m_pScene )
- {
- m_pScene = LoadScene( STRING( m_iszSceneFile ), this );
- if ( !m_pScene )
- {
- m_bSceneMissing = true;
- return;
- }
-
- OnLoaded();
-
- if ( ShouldNetwork() )
- {
- m_nSceneStringIndex = g_pStringTableClientSideChoreoScenes->AddString( CBaseEntity::IsServer(), STRING( m_iszSceneFile ) );
- }
-
- UpdateTransmitState();
- }
-
- m_bSceneMissing = false;
-
- int i;
- for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
- {
- CBaseFlex *pTestActor = FindNamedActor( i );
- if ( !pTestActor )
- continue;
-
- if ( !pTestActor->MyCombatCharacterPointer() )
- continue;
-
- // Needed?
- //if ( !pTestActor->MyCombatCharacterPointer()->IsAlive() )
- // return;
-
- pTestActor->StartChoreoScene( m_pScene );
- }
-
- float dt = SCENE_THINK_INTERVAL;
-
- bool paused = m_bPaused;
-
- m_bPaused = false;
-
- // roll back slightly so that pause events still trigger
- m_pScene->ResetSimulation( true, m_flCurrentTime - SCENE_THINK_INTERVAL, m_flCurrentTime );
- m_pScene->SetTime( m_flCurrentTime - SCENE_THINK_INTERVAL );
-
- SetCurrentTime( m_flCurrentTime, true );
-
- // Robin: This causes a miscount of any interrupt events in the scene.
- // All the variables are saved/restored properly, so we can safely leave them alone.
- //ClearInterrupt();
-
- SetRestoring( true );
-
- DoThink( dt );
-
- SetRestoring( false );
-
- if ( paused )
- {
- PausePlayback();
- }
-
- NetworkProp()->NetworkStateForceUpdate();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CSceneEntity::SetRestoring( bool bRestoring )
-{
- m_bRestoring = bRestoring;
- if ( m_pScene )
- {
- m_pScene->SetRestoring( bRestoring );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::Spawn( void )
-{
- Precache();
-}
-
-void CSceneEntity::PauseThink( void )
-{
- if ( !m_pScene )
- return;
-
- // Stay paused if pause occurred from interrupt
- if ( m_bInterrupted )
- return;
-
- // If entity I/O paused the scene, then it'll have to resume/cancel the scene...
- if ( m_bPausedViaInput )
- {
- // If we're waiting for a resume scene to finish, continue when it's done
- if ( m_bWaitingForResumeScene && !m_hWaitingForThisResumeScene )
- {
- // Resume scene has finished, stop waiting for it
- m_bWaitingForResumeScene = false;
- }
- else
- {
- return;
- }
- }
-
- if ( !m_bAutomated )
- {
- // FIXME: Game code should check for AI waiting conditions being met, etc.
- //
- //
- //
- bool bAllFinished = m_pScene->CheckEventCompletion( );
-
- if ( bAllFinished )
- {
- // Perform action
- switch ( m_nAutomatedAction )
- {
- case SCENE_ACTION_RESUME:
- ResumePlayback();
- break;
- case SCENE_ACTION_CANCEL:
- LocalScene_Printf( "%s : PauseThink canceling playback\n", STRING( m_iszSceneFile ) );
- CancelPlayback();
- break;
- default:
- ResumePlayback();
- break;
- }
-
- // Reset
- m_bAutomated = false;
- m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
- m_flAutomationTime = 0.0f;
- m_flAutomationDelay = 0.0f;
- m_bPausedViaInput = false;
- }
- return;
- }
-
- // Otherwise, see if resume/cancel is automatic and act accordingly if enough time
- // has passed
- m_flAutomationTime += (gpGlobals->frametime);
-
- if ( m_flAutomationDelay > 0.0f &&
- m_flAutomationTime < m_flAutomationDelay )
- return;
-
- // Perform action
- switch ( m_nAutomatedAction )
- {
- case SCENE_ACTION_RESUME:
- LocalScene_Printf( "%s : Automatically resuming playback\n", STRING( m_iszSceneFile ) );
- ResumePlayback();
- break;
- case SCENE_ACTION_CANCEL:
- LocalScene_Printf( "%s : Automatically canceling playback\n", STRING( m_iszSceneFile ) );
- CancelPlayback();
- break;
- default:
- LocalScene_Printf( "%s : Unknown action %i, automatically resuming playback\n", STRING( m_iszSceneFile ), m_nAutomatedAction );
- ResumePlayback();
- break;
- }
-
- // Reset
- m_bAutomated = false;
- m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
- m_flAutomationTime = 0.0f;
- m_flAutomationDelay = 0.0f;
- m_bPausedViaInput = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchPauseScene( CChoreoScene *scene, const char *parameters )
-{
- // Don't pause during restore, since we'll be restoring the pause state already
- if ( m_bRestoring )
- return;
-
- // FIXME: Hook this up to AI, etc. somehow, perhaps poll each actor for conditions using
- // scene resume condition iterator
- PausePlayback();
-
- char token[1024];
-
- m_bPausedViaInput = false;
- m_bAutomated = false;
- m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
- m_flAutomationDelay = 0.0f;
- m_flAutomationTime = 0.0f;
-
- // Check for auto resume/cancel
- const char *buffer = parameters;
- buffer = engine->ParseFile( buffer, token, sizeof( token ) );
- if ( !stricmp( token, "automate" ) )
- {
- buffer = engine->ParseFile( buffer, token, sizeof( token ) );
- if ( !stricmp( token, "Cancel" ) )
- {
- m_nAutomatedAction = SCENE_ACTION_CANCEL;
- }
- else if ( !stricmp( token, "Resume" ) )
- {
- m_nAutomatedAction = SCENE_ACTION_RESUME;
- }
-
- if ( m_nAutomatedAction != SCENE_ACTION_UNKNOWN )
- {
- buffer = engine->ParseFile( buffer, token, sizeof( token ) );
- m_flAutomationDelay = (float)atof( token );
-
- if ( m_flAutomationDelay > 0.0f )
- {
- // Success
- m_bAutomated = true;
- m_flAutomationTime = 0.0f;
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *scene -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchProcessLoop( CChoreoScene *scene, CChoreoEvent *event )
-{
- // Don't restore this event since it's implied in the current "state" of the scene timer, etc.
- if ( m_bRestoring )
- return;
-
- Assert( scene );
- 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: Flag the scene as already "completed"
-// Input : *scene -
-// *parameters -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStopPoint( CChoreoScene *scene, const char *parameters )
-{
- if ( m_bCompletedEarly )
- {
- Assert( 0 );
- Warning( "Scene '%s' with two stop point events!\n", STRING( m_iszSceneFile ) );
- return;
- }
- // Fire completion trigger early
- m_bCompletedEarly = true;
- m_OnCompletion.FireOutput( this, this, 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CSceneEntity::IsInterruptable()
-{
- return ( m_nInterruptCount > 0 ) ? true : false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *scene -
-// *actor -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartInterrupt( CChoreoScene *scene, CChoreoEvent *event )
-{
- // Don't re-interrupt during restore
- if ( m_bRestoring )
- return;
-
- // If we're supposed to cancel at our next interrupt point, cancel now
- if ( m_bCancelAtNextInterrupt )
- {
- m_bCancelAtNextInterrupt = false;
- LocalScene_Printf( "%s : cancelled via interrupt\n", STRING( m_iszSceneFile ) );
- CancelPlayback();
- return;
- }
-
- ++m_nInterruptCount;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *scene -
-// *actor -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchEndInterrupt( CChoreoScene *scene, CChoreoEvent *event )
-{
- // Don't re-interrupt during restore
- if ( m_bRestoring )
- return;
-
- --m_nInterruptCount;
-
- if ( m_nInterruptCount < 0 )
- {
- m_nInterruptCount = 0;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartExpression( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->AddSceneEvent( scene, event );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchEndExpression( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->RemoveSceneEvent( scene, event, false );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartFlexAnimation( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->AddSceneEvent( scene, event );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchEndFlexAnimation( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->RemoveSceneEvent( scene, event, false );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *parameters -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartGesture( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- // Ingore null gestures
- if ( !Q_stricmp( event->GetName(), "NULL" ) )
- return;
-
- actor->AddSceneEvent( scene, event);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *parameters -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchEndGesture( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- // Ingore null gestures
- if ( !Q_stricmp( event->GetName(), "NULL" ) )
- return;
-
- actor->RemoveSceneEvent( scene, event, m_bRestoring );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *parameters -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- CBaseEntity *pTarget = FindNamedEntity( event->GetParameters2( ) );
- actor->AddSceneEvent( scene, event, pTarget );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *parameters -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchEndGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->RemoveSceneEvent( scene, event, m_bRestoring );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *actor2 -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartLookAt( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
-{
- actor->AddSceneEvent( scene, event, actor2 );
-}
-
-
-void CSceneEntity::DispatchEndLookAt( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->RemoveSceneEvent( scene, event, m_bRestoring );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Move to spot/actor
-// FIXME: Need to allow this to take arbitrary amount of time and pause playback
-// while waiting for actor to move into position
-// Input : *actor -
-// *parameters -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartMoveTo( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
-{
- actor->AddSceneEvent( scene, event, actor2 );
-}
-
-
-void CSceneEntity::DispatchEndMoveTo( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->RemoveSceneEvent( scene, event, m_bRestoring );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *token -
-// listener -
-// soundorigins -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool AttenuateCaption( const char *token, const Vector& listener, CUtlVector< Vector >& soundorigins )
-{
- if ( scene_maxcaptionradius.GetFloat() <= 0.0f )
- {
- return false;
- }
-
- int c = soundorigins.Count();
-
- if ( c <= 0 )
- {
- return false;
- }
-
- float maxdistSqr = scene_maxcaptionradius.GetFloat() * scene_maxcaptionradius.GetFloat();
-
- for ( int i = 0; i < c; ++i )
- {
- const Vector& org = soundorigins[ i ];
-
- float distSqr = ( org - listener ).LengthSqr();
- if ( distSqr <= maxdistSqr )
- {
- return false;
- }
- }
-
- // All sound sources too far, don't show caption...
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *event - which event
-// player - which recipient
-// buf, buflen: where to put the data
-// Output : Returns true if the sound should be played/prefetched
-//-----------------------------------------------------------------------------
-bool CSceneEntity::GetSoundNameForPlayer( CChoreoEvent *event, CBasePlayer *player, char *buf, size_t buflen, CBaseEntity *pActor )
-{
- Assert( event );
- Assert( player );
- Assert( buf );
- Assert( buflen > 0 );
-
- bool ismasterevent = true;
- char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
- bool validtoken = false;
-
- tok[ 0 ] = 0;
-
- if ( event->GetCloseCaptionType() == CChoreoEvent::CC_SLAVE ||
- event->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED )
- {
- ismasterevent = false;
- }
- else
- {
- validtoken = event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) );
- }
-
- const char* pchToken = "";
-
- if ( pActor && pActor->IsPlayer() )
- {
- pchToken = dynamic_cast< CBasePlayer* >( pActor )->GetSceneSoundToken();
- }
-
- // Copy the sound name
- CopySoundNameWithModifierToken( buf, event->GetParameters(), buflen, pchToken );
-
- bool usingEnglish = true;
- if ( !IsXbox() )
- {
- char const *cvarvalue = engine->GetClientConVarValue( player->entindex(), "english" );
- if ( cvarvalue && *cvarvalue && Q_atoi( cvarvalue ) != 1 )
- {
- usingEnglish = false;
- }
-
- }
-
- // This makes it like they are running in another language
- if ( scene_forcecombined.GetBool() )
- {
- usingEnglish = false;
- }
-
- if ( usingEnglish )
- {
- // English sounds always play
- return true;
- }
-
- if ( ismasterevent )
- {
- // Master event sounds always play too (master will be the combined .wav)
- if ( validtoken )
- {
- Q_strncpy( buf, tok, buflen );
- }
- return true;
- }
-
- // Slave events don't play any sound...
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Playback sound file that contains phonemes
-// Input : *actor -
-// *parameters -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartSpeak( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event, soundlevel_t iSoundlevel )
-{
- // Emit sound
- if ( actor )
- {
- CPASAttenuationFilter filter( actor );
-
- if ( m_pRecipientFilter )
- {
- int filterCount = filter.GetRecipientCount();
- int recipientPlayerCount = m_pRecipientFilter->GetRecipientCount();
- for ( int i = filterCount-1; i >= 0; --i )
- {
- int playerindex = filter.GetRecipientIndex( i );
-
- bool bFound = false;
-
- for ( int j = 0; j < recipientPlayerCount; ++j )
- {
- if ( m_pRecipientFilter->GetRecipientIndex(j) == playerindex )
- {
- bFound = true;
- break;
- }
- }
-
- if ( !bFound )
- {
- filter.RemoveRecipientByPlayerIndex( playerindex );
- }
- }
- }
-
- float time_in_past = m_flCurrentTime - event->GetStartTime() ;
-
- float soundtime = gpGlobals->curtime - time_in_past;
-
- if ( m_bRestoring )
- {
- // Need to queue sounds on restore because the player has not yet connected
- GetSceneManager()->QueueRestoredSound( actor, event->GetParameters(), iSoundlevel, time_in_past );
-
- return;
- }
-
- // Add padding to prevent any other talker talking right after I'm done, because I might
- // be continuing speaking with another scene.
- float flDuration = event->GetDuration() - time_in_past;
-
- CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(actor);
- if ( pBaseActor )
- {
- pBaseActor->NoteSpeaking( flDuration, GetPostSpeakDelay() );
- }
- else if ( actor->IsNPC() )
- {
- GetSpeechSemaphore( actor->MyNPCPointer() )->Acquire( flDuration + GetPostSpeakDelay(), actor );
- }
-
- EmitSound_t es;
- es.m_nChannel = CHAN_VOICE;
- es.m_flVolume = 1;
- es.m_SoundLevel = iSoundlevel;
- // Only specify exact delay in single player
- es.m_flSoundTime = ( gpGlobals->maxClients == 1 ) ? soundtime : 0.0f;
- if ( scene->ShouldIgnorePhonemes() )
- {
- es.m_nFlags |= SND_IGNORE_PHONEMES;
- }
-
- if ( actor->GetSpecialDSP() != 0 )
- {
- es.m_nSpecialDSP = actor->GetSpecialDSP();
- }
-
- // No CC since we do it manually
- // FIXME: This will change
- es.m_bEmitCloseCaption = false;
-
- int c = filter.GetRecipientCount();
- for ( int i = 0; i < c; ++i )
- {
- int playerindex = filter.GetRecipientIndex( i );
- CBasePlayer *player = UTIL_PlayerByIndex( playerindex );
- if ( !player )
- continue;
-
- CSingleUserRecipientFilter filter2( player );
-
- char soundname[ 512 ];
- if ( !GetSoundNameForPlayer( event, player, soundname, sizeof( soundname ), actor ) )
- {
- continue;
- }
-
- es.m_pSoundName = soundname;
-
- // keep track of the last few sounds played for bug reports
- speechListSounds[ speechListIndex ].time = gpGlobals->curtime;
- Q_strncpy( speechListSounds[ speechListIndex ].name, soundname, sizeof( speechListSounds[ 0 ].name ) );
- Q_strncpy( speechListSounds[ speechListIndex ].sceneName, ( scene ) ? scene->GetFilename() : "", sizeof( speechListSounds[ 0 ].sceneName ) );
-
- speechListIndex++;
- if ( speechListIndex >= SPEECH_LIST_MAX_SOUNDS )
- {
- speechListIndex = 0;
- }
-
- // Warning( "Speak %s\n", soundname );
-
- if ( m_fPitch != 1.0f )
- {
- if ( es.m_nPitch )
- es.m_nPitch = static_cast<float>( es.m_nPitch ) * m_fPitch;
- else
- es.m_nPitch = 100.0f * m_fPitch;
-
- es.m_nFlags |= SND_CHANGE_PITCH;
- }
-
- EmitSound( filter2, actor->entindex(), es );
- actor->AddSceneEvent( scene, event );
- }
-
- // 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 )
- {
- char lowercase[ 256 ];
- Q_strncpy( lowercase, tok, sizeof( lowercase ) );
- Q_strlower( lowercase );
-
- // Remove any players who don't want close captions
- CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( filter );
-
- // Certain events are marked "don't attenuate", (breencast), skip those here
- if ( !event->IsSuppressingCaptionAttenuation() &&
- ( filter.GetRecipientCount() > 0 ) )
- {
- int c = filter.GetRecipientCount();
- for ( int i = c - 1 ; i >= 0; --i )
- {
- CBasePlayer *player = UTIL_PlayerByIndex( filter.GetRecipientIndex( i ) );
- if ( !player )
- continue;
-
- Vector playerOrigin = player->GetAbsOrigin();
-
- if ( AttenuateCaption( lowercase, playerOrigin, es.m_UtlVecSoundOrigin ) )
- {
- // If the player has a view entity, measure the distance to that
- if ( !player->GetViewEntity() || AttenuateCaption( lowercase, player->GetViewEntity()->GetAbsOrigin(), es.m_UtlVecSoundOrigin ) )
- {
- filter.RemoveRecipient( player );
- }
- }
- }
- }
-
- // Anyone left?
- if ( filter.GetRecipientCount() > 0 )
- {
- float endtime = event->GetLastSlaveEndTime();
- float durationShort = event->GetDuration();
- float durationLong = endtime - event->GetStartTime();
-
- float duration = MAX( durationShort, durationLong );
-
-
- byte byteflags = CLOSE_CAPTION_WARNIFMISSING; // warnifmissing
- /*
- // Never for .vcds...
- if ( fromplayer )
- {
- byteflags |= CLOSE_CAPTION_FROMPLAYER;
- }
- */
- char const *pszActorModel = STRING( actor->GetModelName() );
- gender_t gender = soundemitterbase->GetActorGender( pszActorModel );
-
- if ( gender == GENDER_MALE )
- {
- byteflags |= CLOSE_CAPTION_GENDER_MALE;
- }
- else if ( gender == GENDER_FEMALE )
- {
- byteflags |= CLOSE_CAPTION_GENDER_FEMALE;
- }
-
- // Send caption and duration hint down to client
- UserMessageBegin( filter, "CloseCaption" );
- WRITE_STRING( lowercase );
- WRITE_SHORT( MIN( 255, (int)( duration * 10.0f ) ) );
- WRITE_BYTE( byteflags ); // warn on missing
- MessageEnd();
- }
- }
- }
- }
-}
-
-void CSceneEntity::DispatchEndSpeak( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->RemoveSceneEvent( scene, event, m_bRestoring );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *actor2 -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartFace( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
-{
- actor->AddSceneEvent( scene, event, actor2 );
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *actor2 -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchEndFace( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->RemoveSceneEvent( scene, event, m_bRestoring );
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->AddSceneEvent( scene, event );
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->RemoveSceneEvent( scene, event, m_bRestoring );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: NPC can play interstitial vcds (such as responding to the player doing something during a scene)
-// Input : *scene -
-// *actor -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartPermitResponses( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->SetPermitResponse( gpGlobals->curtime + event->GetDuration() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *scene -
-// *actor -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchEndPermitResponses( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
-{
- actor->SetPermitResponse( 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-float CSceneEntity::EstimateLength( void )
-{
- if ( !m_pScene )
- {
- return GetSceneDuration( STRING( m_iszSceneFile ) );
- }
- return m_pScene->FindStopTime();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// NOTE: returns false if scene hasn't loaded yet
-//-----------------------------------------------------------------------------
-void CSceneEntity::CancelIfSceneInvolvesActor( CBaseEntity *pActor )
-{
- if ( InvolvesActor( pActor ) )
- {
- LocalScene_Printf( "%s : cancelled for '%s'\n", STRING( m_iszSceneFile ), pActor->GetDebugName() );
- CancelPlayback();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// NOTE: returns false if scene hasn't loaded yet
-//-----------------------------------------------------------------------------
-bool CSceneEntity::InvolvesActor( CBaseEntity *pActor )
-{
- if ( !m_pScene )
- return false;
-
- int i;
- for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
- {
- CBaseFlex *pTestActor = FindNamedActor( i );
- if ( !pTestActor )
- continue;
-
- if ( pTestActor == pActor )
- return true;
- }
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::DoThink( float frametime )
-{
- CheckInterruptCompletion();
-
- if ( m_bWaitingForActor || m_bWaitingForInterrupt )
- {
- // Try to start playback.
- StartPlayback();
- }
-
- if ( !m_pScene )
- return;
-
- if ( !m_bIsPlayingBack )
- return;
-
- // catch bad pitch shifting from old save games
- Assert( m_fPitch >= SCENE_MIN_PITCH && m_fPitch <= SCENE_MAX_PITCH );
- m_fPitch = clamp( m_fPitch, SCENE_MIN_PITCH, SCENE_MAX_PITCH );
-
- if ( m_bPaused )
- {
- PauseThink();
- return;
- }
-
- // Msg("%.2f %s\n", gpGlobals->curtime, STRING( m_iszSceneFile ) );
-
- //Msg( "SV: %d, %f for %s\n", gpGlobals->tickcount, m_flCurrentTime, m_pScene->GetFilename() );
-
- m_flFrameTime = frametime;
-
- m_pScene->SetSoundFileStartupLatency( GetSoundSystemLatency() );
-
- // Tell scene to go
- m_pScene->Think( m_flCurrentTime );
-
- // Did we get to the end
- if ( !m_bPaused )
- {
- // Drive simulation time for scene
- SetCurrentTime( m_flCurrentTime + m_flFrameTime * m_fPitch, false );
-
- if ( m_pScene->SimulationFinished() )
- {
- OnSceneFinished( false, true );
-
- // Stop them from doing anything special
- ClearSchedules( m_pScene );
- }
- }
- else
- {
- // Drive simulation time for scene
- SetCurrentTime( m_pScene->GetTime(), true );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Input handlers
-//-----------------------------------------------------------------------------
-void CSceneEntity::InputStartPlayback( inputdata_t &inputdata )
-{
- // Already playing, ignore
- if ( m_bIsPlayingBack )
- return;
-
- // Already waiting on someone.
- if ( m_bWaitingForActor || m_bWaitingForInterrupt )
- return;
-
- ClearActivatorTargets();
- m_hActivator = inputdata.pActivator;
- StartPlayback();
-}
-
-void CSceneEntity::InputPausePlayback( inputdata_t &inputdata )
-{
- PausePlayback();
- m_bPausedViaInput = true;
-}
-
-void CSceneEntity::InputResumePlayback( inputdata_t &inputdata )
-{
- ResumePlayback();
-}
-
-void CSceneEntity::InputCancelPlayback( inputdata_t &inputdata )
-{
- LocalScene_Printf( "%s : cancelled via input\n", STRING( m_iszSceneFile ) );
- CancelPlayback();
-}
-
-void CSceneEntity::InputScriptPlayerDeath( inputdata_t &inputdata )
-{
- if ( m_iPlayerDeathBehavior == SCRIPT_CANCEL )
- {
- LocalScene_Printf( "%s : cancelled via player death\n", STRING( m_iszSceneFile ) );
- CancelPlayback();
- }
-}
-
-
-void CSceneEntity::InputCancelAtNextInterrupt( inputdata_t &inputdata )
-{
- // If we're currently in an interruptable point, interrupt immediately
- if ( IsInterruptable() )
- {
- LocalScene_Printf( "%s : cancelled via input at interrupt point\n", STRING( m_iszSceneFile ) );
- CancelPlayback();
- return;
- }
-
- // Otherwise, cancel when we next hit an interrupt point
- m_bCancelAtNextInterrupt = true;
-}
-
-void CSceneEntity::InputPitchShiftPlayback( inputdata_t &inputdata )
-{
- PitchShiftPlayback( inputdata.value.Float() );
-}
-
-void CSceneEntity::InputTriggerEvent( inputdata_t &inputdata )
-{
- CBaseEntity *pActivator = this; // at some point, find this from the inputdata
- switch ( inputdata.value.Int() )
- {
- case 1:
- m_OnTrigger1.FireOutput( pActivator, this, 0 );
- break;
- case 2:
- m_OnTrigger2.FireOutput( pActivator, this, 0 );
- break;
- case 3:
- m_OnTrigger3.FireOutput( pActivator, this, 0 );
- break;
- case 4:
- m_OnTrigger4.FireOutput( pActivator, this, 0 );
- break;
- case 5:
- m_OnTrigger5.FireOutput( pActivator, this, 0 );
- break;
- case 6:
- m_OnTrigger6.FireOutput( pActivator, this, 0 );
- break;
- case 7:
- m_OnTrigger7.FireOutput( pActivator, this, 0 );
- break;
- case 8:
- m_OnTrigger8.FireOutput( pActivator, this, 0 );
- break;
- case 9:
- m_OnTrigger9.FireOutput( pActivator, this, 0 );
- break;
- case 10:
- m_OnTrigger10.FireOutput( pActivator, this, 0 );
- break;
- case 11:
- m_OnTrigger11.FireOutput( pActivator, this, 0 );
- break;
- case 12:
- m_OnTrigger12.FireOutput( pActivator, this, 0 );
- break;
- case 13:
- m_OnTrigger13.FireOutput( pActivator, this, 0 );
- break;
- case 14:
- m_OnTrigger14.FireOutput( pActivator, this, 0 );
- break;
- case 15:
- m_OnTrigger15.FireOutput( pActivator, this, 0 );
- break;
- case 16:
- m_OnTrigger16.FireOutput( pActivator, this, 0 );
- break;
- }
-}
-
-struct NPCInterjection
-{
- AI_Response *response;
- CAI_BaseActor *npc;
-};
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &inputdata -
-//-----------------------------------------------------------------------------
-void CSceneEntity::InputInterjectResponse( inputdata_t &inputdata )
-{
- // Not currently playing a scene
- if ( !m_pScene )
- {
- return;
- }
-
- CUtlVector< CAI_BaseActor * > candidates;
- int i;
- for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
- {
- CBaseFlex *pTestActor = FindNamedActor( i );
- if ( !pTestActor )
- continue;
-
- CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(pTestActor);
- if ( !pBaseActor )
- continue;
-
- if ( !pBaseActor->IsAlive() )
- continue;
-
- candidates.AddToTail( pBaseActor );
- }
-
- int c = candidates.Count();
-
- if ( !c )
- {
- return;
- }
-
- int useIndex = 0;
- if ( !m_bIsPlayingBack )
- {
- // Use any actor if not playing a scene
- useIndex = RandomInt( 0, c - 1 );
- }
- else
- {
- CUtlVector< NPCInterjection > validResponses;
-
- char modifiers[ 512 ];
- Q_snprintf( modifiers, sizeof( modifiers ), "scene:%s", STRING( GetEntityName() ) );
-
- for ( int i = 0; i < c; i++ )
- {
- CAI_BaseActor *npc = candidates[ i ];
- Assert( npc );
-
- AI_Response *response = npc->SpeakFindResponse( inputdata.value.String(), modifiers );
- if ( !response )
- continue;
-
- float duration = npc->GetResponseDuration( response );
- // Couldn't look it up
- if ( duration <= 0.0f )
- continue;
-
- if ( !npc->PermitResponse( duration ) )
- {
- delete response;
- continue;
- }
-
- //
- NPCInterjection inter;
- inter.response = response;
- inter.npc = npc;
-
- validResponses.AddToTail( inter );
- }
-
- int rcount = validResponses.Count();
- if ( rcount >= 1 )
- {
- int slot = RandomInt( 0, rcount - 1 );
-
- for ( int i = 0; i < rcount; i++ )
- {
- NPCInterjection *pInterjection = &validResponses[ i ];
- if ( i == slot )
- {
- pInterjection->npc->SpeakDispatchResponse( inputdata.value.String(), pInterjection->response );
- }
- else
- {
- delete pInterjection->response;
- }
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CSceneEntity::InputStopWaitingForActor( inputdata_t &inputdata )
-{
- if( m_bIsPlayingBack )
- {
- // Already started.
- return;
- }
-
- m_bWaitingForActor = false;
-}
-
-bool CSceneEntity::CheckActors()
-{
- Assert( m_pScene );
- if ( !m_pScene )
- return false;
-
- int i;
- for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
- {
- CBaseFlex *pTestActor = FindNamedActor( i );
- if ( !pTestActor )
- continue;
-
- if ( !pTestActor->MyCombatCharacterPointer() )
- continue;
-
- if ( !pTestActor->MyCombatCharacterPointer()->IsAlive() )
- return false;
-
- if ( m_BusyActor == SCENE_BUSYACTOR_WAIT )
- {
- CAI_BaseNPC *pActor = pTestActor->MyNPCPointer();
-
- if ( pActor )
- {
- bool bShouldWait = false;
- if ( hl2_episodic.GetBool() )
- {
- // Episodic waits until the NPC is fully finished with any .vcd with speech in it
- if ( IsRunningScriptedSceneWithSpeech( pActor ) )
- {
- bShouldWait = true;
- }
-
-#ifdef HL2_EPISODIC
- // HACK: Alyx cannot play scenes when she's in the middle of transitioning
- if ( pActor->IsInAVehicle() )
- {
- CNPC_Alyx *pAlyx = dynamic_cast<CNPC_Alyx *>(pActor);
- if ( pAlyx != NULL && ( pAlyx->GetPassengerState() == PASSENGER_STATE_ENTERING || pAlyx->GetPassengerState() == PASSENGER_STATE_EXITING ) )
- {
- bShouldWait = true;
- }
- }
-#endif // HL2_EPISODIC
- }
-
- if ( pActor->GetExpresser() && pActor->GetExpresser()->IsSpeaking() )
- {
- bShouldWait = true;
- }
-
- if ( bShouldWait )
- {
- // One of the actors for this scene is talking already.
- // Try again next think.
- m_bWaitingForActor = true;
- return false;
- }
- }
- }
- else if ( m_BusyActor == SCENE_BUSYACTOR_INTERRUPT || m_BusyActor == SCENE_BUSYACTOR_INTERRUPT_CANCEL )
- {
- CBaseCombatCharacter *pActor = pTestActor->MyCombatCharacterPointer();
- if ( pActor && !IsInInterruptableScenes( pActor ) )
- {
- // One of the actors is in a scene that's not at an interrupt point.
- // Wait until the scene finishes or an interrupt point is reached.
- m_bWaitingForInterrupt = true;
- return false;
- }
-
- if ( m_BusyActor == SCENE_BUSYACTOR_INTERRUPT_CANCEL )
- {
- // Cancel existing scenes
- RemoveActorFromScriptedScenes( pActor, false );
- }
- else
- {
- // Pause existing scenes
- PauseActorsScriptedScenes( pActor, false );
- m_bInterruptedActorsScenes = true;
- }
- }
-
- pTestActor->StartChoreoScene( m_pScene );
- }
-
- return true;
-}
-
-#if !defined( _RETAIL )
-static ConVar scene_async_prefetch_spew( "scene_async_prefetch_spew", "0", 0, "Display async .ani file loading info." );
-#endif
-
-void CSceneEntity::PrefetchAnimBlocks( CChoreoScene *scene )
-{
- Assert( scene );
-
- // Build a fast lookup, too
- CUtlMap< CChoreoActor *, CBaseFlex *> actorMap( 0, 0, DefLessFunc( CChoreoActor * ) );
-
- int spew =
-#if !defined( _RETAIL )
- scene_async_prefetch_spew.GetInt();
-#else
- 0;
-#endif
-
- int resident = 0;
- int checked = 0;
-
- // Iterate events and precache necessary resources
- for ( int i = 0; i < scene->GetNumEvents(); i++ )
- {
- CChoreoEvent *event = scene->GetEvent( i );
- if ( !event )
- continue;
-
- // load any necessary data
- switch ( event->GetType() )
- {
- default:
- break;
- case CChoreoEvent::SEQUENCE:
- case CChoreoEvent::GESTURE:
- {
- CChoreoActor *actor = event->GetActor();
- if ( actor )
- {
- CBaseFlex *pActor = NULL;
- int idx = actorMap.Find( actor );
- if ( idx == actorMap.InvalidIndex() )
- {
- pActor = FindNamedActor( actor );
- idx = actorMap.Insert( actor, pActor );
- }
- else
- {
- pActor = actorMap[ idx ];
- }
-
- if ( pActor )
- {
- int seq = pActor->LookupSequence( event->GetParameters() );
- if ( seq >= 0 )
- {
- CStudioHdr *pStudioHdr = pActor->GetModelPtr();
- if ( pStudioHdr )
- {
- // Now look up the animblock
- mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( seq );
- for ( int i = 0 ; i < seqdesc.groupsize[ 0 ] ; ++i )
- {
- for ( int j = 0; j < seqdesc.groupsize[ 1 ]; ++j )
- {
- int animation = seqdesc.anim( i, j );
- int baseanimation = pStudioHdr->iRelativeAnim( seq, animation );
- mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( baseanimation );
-
- ++checked;
-
- if ( spew != 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 )
- {
- ++resident;
- if ( spew > 1 )
- {
- Msg( "%s:%s[%i:%i] was resident\n", pStudioHdr->pszName(), animdesc.pszName(), i, j );
- }
- }
- else
- {
- if ( spew != 0 )
- {
- Msg( "%s:%s[%i:%i] async load\n", pStudioHdr->pszName(), animdesc.pszName(), i, j );
- }
- }
- }
- }
- }
- }
- }
- }
- }
- break;
- }
- }
-
- if ( !spew || checked <= 0 )
- return;
-
- Msg( "%d of %d animations resident\n", resident, checked );
-}
-
-void CSceneEntity::OnLoaded()
-{
- // Nothing
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Initiate scene playback
-//-----------------------------------------------------------------------------
-void CSceneEntity::StartPlayback( void )
-{
- if ( !m_pScene )
- {
- if ( m_bSceneMissing )
- return;
-
- m_pScene = LoadScene( STRING( m_iszSceneFile ), this );
- if ( !m_pScene )
- {
- DevMsg( "%s missing from scenes.image\n", STRING( m_iszSceneFile ) );
- m_bSceneMissing = true;
- return;
- }
-
- OnLoaded();
-
- if ( ShouldNetwork() )
- {
- m_nSceneStringIndex = g_pStringTableClientSideChoreoScenes->AddString( CBaseEntity::IsServer(), STRING( m_iszSceneFile ) );
- }
-
- UpdateTransmitState();
- }
-
- if ( m_bIsPlayingBack )
- return;
-
- // Make sure actors are alive and able to handle this scene now, otherwise
- // we'll wait for them to show up
- if ( !CheckActors() )
- {
- return;
- }
-
- m_bCompletedEarly = false;
- m_bWaitingForActor = false;
- m_bWaitingForInterrupt = false;
- m_bIsPlayingBack = true;
- NetworkProp()->NetworkStateForceUpdate();
- m_bPaused = false;
- SetCurrentTime( 0.0f, true );
- m_pScene->ResetSimulation();
- ClearInterrupt();
-
- // Put face back in neutral pose
- ClearSceneEvents( m_pScene, false );
-
- m_OnStart.FireOutput( this, this, 0 );
-
- // Aysnchronously load speak sounds
- CUtlSymbolTable prefetchSoundSymbolTable;
- CUtlRBTree< SpeakEventSound_t > soundnames( 0, 0, SpeakEventSoundLessFunc );
-
- BuildSortedSpeakEventSoundsPrefetchList( m_pScene, prefetchSoundSymbolTable, soundnames, 0.0f );
- PrefetchSpeakEventSounds( prefetchSoundSymbolTable, soundnames );
-
- // Tell any managers we're within that we've started
- int c = m_hListManagers.Count();
- for ( int i = 0; i < c; i++ )
- {
- if ( m_hListManagers[i] )
- {
- m_hListManagers[i]->SceneStarted( this );
- }
- }
-
- PrefetchAnimBlocks( m_pScene );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Static method used to sort by event start time
-//-----------------------------------------------------------------------------
-bool CSceneEntity::SpeakEventSoundLessFunc( const SpeakEventSound_t& lhs, const SpeakEventSound_t& rhs )
-{
- return lhs.m_flStartTime < rhs.m_flStartTime;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Prefetches the list of sounds build by BuildSortedSpeakEventSoundsPrefetchList
-//-----------------------------------------------------------------------------
-void CSceneEntity::PrefetchSpeakEventSounds( CUtlSymbolTable& table, CUtlRBTree< SpeakEventSound_t >& soundnames )
-{
- for ( int i = soundnames.FirstInorder(); i != soundnames.InvalidIndex() ; i = soundnames.NextInorder( i ) )
- {
- SpeakEventSound_t& sound = soundnames[ i ];
- // Look it up in the string table
- char const *soundname = table.String( sound.m_Symbol );
-
- // Warning( "Prefetch %s\n", soundname );
-
- PrefetchScriptSound( soundname );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Builds list of sounds sorted by start time for prefetching
-//-----------------------------------------------------------------------------
-void CSceneEntity::BuildSortedSpeakEventSoundsPrefetchList(
- CChoreoScene *scene,
- CUtlSymbolTable& table,
- CUtlRBTree< SpeakEventSound_t >& soundnames,
- float timeOffset )
-{
- Assert( scene );
-
- // Iterate events and precache necessary resources
- for ( int i = 0; i < scene->GetNumEvents(); i++ )
- {
- CChoreoEvent *event = scene->GetEvent( i );
- if ( !event )
- continue;
-
- // load any necessary data
- switch (event->GetType() )
- {
- default:
- break;
- case CChoreoEvent::SPEAK:
- {
-
- // NOTE: The script entries associated with .vcds are forced to preload to avoid
- // loading hitches during triggering
- char soundname[ CChoreoEvent::MAX_CCTOKEN_STRING ];
- Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) );
-
- if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
- {
- event->GetPlaybackCloseCaptionToken( soundname, sizeof( soundname ) );
- }
-
- // In single player, try to use the combined or regular .wav files as needed
- if ( gpGlobals->maxClients == 1 )
- {
- CBasePlayer *player = UTIL_GetLocalPlayer();
- if ( player && !GetSoundNameForPlayer( event, player, soundname, sizeof( soundname ), player ) )
- {
- // Skip to next event
- continue;
- }
- }
- /*
- else
- {
- // UNDONE: Probably need some other solution in multiplayer... (not sure how to "prefetch" on certain players
- // with one sound, but not prefetch the same sound for others...)
- }
- */
-
- SpeakEventSound_t ses;
- ses.m_Symbol = table.AddString( soundname );
- ses.m_flStartTime = timeOffset + event->GetStartTime();
-
- soundnames.Insert( ses );
- }
- break;
- case CChoreoEvent::SUBSCENE:
- {
- // Only allow a single level of subscenes for now
- if ( !scene->IsSubScene() )
- {
- CChoreoScene *subscene = event->GetSubScene();
- if ( !subscene )
- {
- subscene = LoadScene( event->GetParameters(), this );
- subscene->SetSubScene( true );
- event->SetSubScene( subscene );
-
- // Now precache it's resources, if any
- BuildSortedSpeakEventSoundsPrefetchList( subscene, table, soundnames, event->GetStartTime() );
- }
- }
- }
- break;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::PausePlayback( void )
-{
- if ( !m_bIsPlayingBack )
- return;
-
- if ( m_bPaused )
- return;
-
- m_bPaused = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::ResumePlayback( void )
-{
- if ( !m_bIsPlayingBack )
- return;
-
- if ( !m_bPaused )
- return;
-
- Assert( m_pScene );
- if ( !m_pScene )
- {
- // This should never happen!!!!
- return;
- }
-
- // FIXME: Iterate using m_pScene->IterateResumeConditionEvents and
- // only resume if the event conditions have all been satisfied
-
- // FIXME: Just resume for now
- m_pScene->ResumeSimulation();
-
- m_bPaused = false;
- m_bPausedViaInput = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::CancelPlayback( void )
-{
- if ( !m_bIsPlayingBack )
- return;
-
- m_bIsPlayingBack = false;
- m_bPaused = false;
-
- m_OnCanceled.FireOutput( this, this, 0 );
-
- LocalScene_Printf( "%s : %8.2f: canceled\n", STRING( m_iszSceneFile ), m_flCurrentTime );
-
- OnSceneFinished( true, false );
-}
-
-void CSceneEntity::PitchShiftPlayback( float fPitch )
-{
- fPitch = clamp( fPitch, SCENE_MIN_PITCH, SCENE_MAX_PITCH );
-
- m_fPitch = fPitch;
-
- if ( !m_pScene )
- return;
-
- for ( int iActor = 0 ; iActor < m_pScene->GetNumActors(); ++iActor )
- {
- CBaseFlex *pTestActor = FindNamedActor( iActor );
-
- if ( !pTestActor )
- continue;
-
- char szBuff[ 256 ];
-
- if ( m_pScene->GetPlayingSoundName( szBuff, sizeof( szBuff ) ) )
- {
- CPASAttenuationFilter filter( pTestActor );
- EmitSound_t params;
- params.m_pSoundName = szBuff;
- params.m_nPitch = 100.0f * fPitch;
- params.m_nFlags = SND_CHANGE_PITCH;
- pTestActor->EmitSound( filter, pTestActor->entindex(), params );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Start a resume scene, if we have one, and resume playing when it finishes
-//-----------------------------------------------------------------------------
-void CSceneEntity::QueueResumePlayback( void )
-{
- // Do we have a resume scene?
- if ( m_iszResumeSceneFile != NULL_STRING )
- {
- bool bStartedScene = false;
-
- // If it has ".vcd" somewhere in the string, try using it as a scene file first
- if ( Q_stristr( STRING(m_iszResumeSceneFile), ".vcd" ) )
- {
- bStartedScene = InstancedScriptedScene( NULL, STRING(m_iszResumeSceneFile), &m_hWaitingForThisResumeScene, 0, false ) != 0;
- }
-
- // HACKHACK: For now, get the first target, and see if we can find a response for him
- if ( !bStartedScene )
- {
- CBaseFlex *pActor = FindNamedActor( 0 );
- if ( pActor )
- {
- CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(pActor);
- if ( pBaseActor )
- {
- AI_Response *result = pBaseActor->SpeakFindResponse( STRING(m_iszResumeSceneFile), NULL );
- if ( result )
- {
- char response[ 256 ];
- result->GetResponse( response, sizeof( response ) );
- bStartedScene = InstancedScriptedScene( NULL, response, &m_hWaitingForThisResumeScene, 0, false ) != 0;
- }
- }
- }
- }
-
- // If we started a scene/response, wait for it to finish
- if ( bStartedScene )
- {
- m_bWaitingForResumeScene = true;
- }
- else
- {
- // Failed to create the scene. Resume immediately.
- ResumePlayback();
- }
- }
- else
- {
- // No resume scene, so just resume immediately
- ResumePlayback();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Query whether the scene actually loaded. Only meaninful after Spawn()
-//-----------------------------------------------------------------------------
-bool CSceneEntity::ValidScene() const
-{
- return ( m_pScene != NULL );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pActor -
-// *scene -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::DispatchStartSubScene( CChoreoScene *scene, CBaseFlex *pActor, CChoreoEvent *event)
-{
- if ( !scene->IsSubScene() )
- {
- CChoreoScene *subscene = event->GetSubScene();
- if ( !subscene )
- {
- Assert( 0 );
- /*
- subscene = LoadScene( event->GetParameters() );
- subscene->SetSubScene( true );
- event->SetSubScene( subscene );
- */
- }
-
- if ( subscene )
- {
- subscene->ResetSimulation();
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: All events are leading edge triggered
-// Input : currenttime -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
-{
- Assert( event );
-
- if ( !Q_stricmp( event->GetName(), "NULL" ) )
- {
- LocalScene_Printf( "%s : %8.2f: ignored %s\n", STRING( m_iszSceneFile ), currenttime, event->GetDescription() );
- return;
- }
-
-
- CBaseFlex *pActor = NULL;
- CChoreoActor *actor = event->GetActor();
- if ( actor )
- {
- pActor = FindNamedActor( actor );
- if (pActor == NULL)
- {
- Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", STRING(GetEntityName()), actor->GetName() );
- return;
- }
- }
-
- LocalScene_Printf( "%s : %8.2f: start %s\n", STRING( m_iszSceneFile ), currenttime, event->GetDescription() );
-
- switch ( event->GetType() )
- {
- case CChoreoEvent::SUBSCENE:
- {
- if ( pActor && !IsMultiplayer() )
- {
- DispatchStartSubScene( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::EXPRESSION:
- {
- if ( pActor && !IsMultiplayer() )
- {
- DispatchStartExpression( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::FLEXANIMATION:
- {
- if ( pActor && !IsMultiplayer() )
- {
- DispatchStartFlexAnimation( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::LOOKAT:
- {
- if ( pActor && !IsMultiplayer() )
- {
- CBaseEntity *pActor2 = FindNamedEntity( event->GetParameters( ), pActor );
- if ( pActor2 )
- {
- // Huh?
- DispatchStartLookAt( scene, pActor, pActor2, event );
- }
- else
- {
- Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", STRING(GetEntityName()), event->GetParameters() );
- }
- }
- }
- break;
- case CChoreoEvent::SPEAK:
- {
- if ( pActor )
- {
- // Speaking is edge triggered
-
- // 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;
- case CChoreoEvent::MOVETO:
- {
- // FIXME: make sure moveto's aren't edge triggered
- if ( !event->HasEndTime() )
- {
- event->SetEndTime( event->GetStartTime() + 1.0 );
- }
-
- if ( pActor && !IsMultiplayer() )
- {
- CBaseEntity *pActor2 = NULL;
- if ( event->GetParameters3( ) && strlen( event->GetParameters3( ) ) > 0 )
- {
- pActor2 = FindNamedEntityClosest( event->GetParameters( ), pActor, false, true, event->GetParameters3( ) );
- }
- else
- {
- pActor2 = FindNamedEntity( event->GetParameters( ), pActor, false, true );
- }
- if ( pActor2 )
- {
-
- DispatchStartMoveTo( scene, pActor, pActor2, event );
- }
- else
- {
- Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", STRING(GetEntityName()), event->GetParameters() );
- }
- }
- }
- break;
- case CChoreoEvent::FACE:
- {
- if ( pActor && !IsMultiplayer() )
- {
- CBaseEntity *pActor2 = FindNamedEntity( event->GetParameters( ), pActor );
- if ( pActor2 )
- {
- DispatchStartFace( scene, pActor, pActor2, event );
- }
- else
- {
- Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", STRING(GetEntityName()), event->GetParameters() );
- }
- }
- }
- break;
- case CChoreoEvent::GESTURE:
- {
- if ( pActor )
- {
- DispatchStartGesture( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::GENERIC:
- {
- // If the first token in the parameters is "debugtext", print the rest of the text
- if ( event->GetParameters() && !Q_strncmp( event->GetParameters(), "debugtext", 9 ) )
- {
- const char *pszText = event->GetParameters() + 10;
-
- hudtextparms_s tTextParam;
- tTextParam.x = -1;
- tTextParam.y = 0.65;
- tTextParam.effect = 0;
- tTextParam.r1 = 255;
- tTextParam.g1 = 170;
- tTextParam.b1 = 0;
- tTextParam.a1 = 255;
- tTextParam.r2 = 255;
- tTextParam.g2 = 170;
- tTextParam.b2 = 0;
- tTextParam.a2 = 255;
- tTextParam.fadeinTime = 0;
- tTextParam.fadeoutTime = 0;
- tTextParam.holdTime = 3.1;
- tTextParam.fxTime = 0;
- tTextParam.channel = 1;
- UTIL_HudMessageAll( tTextParam, pszText );
- break;
- }
-
- if ( pActor )
- {
- DispatchStartGeneric( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::FIRETRIGGER:
- {
- if ( IsMultiplayer() )
- break;
-
- // Don't re-fire triggers during restore, the entities should already reflect all such state...
- if ( m_bRestoring )
- {
- break;
- }
-
- CBaseEntity *pActivator = pActor;
- if (!pActivator)
- {
- pActivator = this;
- }
-
- // FIXME: how do I decide who fired it??
- switch( atoi( event->GetParameters() ) )
- {
- case 1:
- m_OnTrigger1.FireOutput( pActivator, this, 0 );
- break;
- case 2:
- m_OnTrigger2.FireOutput( pActivator, this, 0 );
- break;
- case 3:
- m_OnTrigger3.FireOutput( pActivator, this, 0 );
- break;
- case 4:
- m_OnTrigger4.FireOutput( pActivator, this, 0 );
- break;
- case 5:
- m_OnTrigger5.FireOutput( pActivator, this, 0 );
- break;
- case 6:
- m_OnTrigger6.FireOutput( pActivator, this, 0 );
- break;
- case 7:
- m_OnTrigger7.FireOutput( pActivator, this, 0 );
- break;
- case 8:
- m_OnTrigger8.FireOutput( pActivator, this, 0 );
- break;
- case 9:
- m_OnTrigger9.FireOutput( pActivator, this, 0 );
- break;
- case 10:
- m_OnTrigger10.FireOutput( pActivator, this, 0 );
- break;
- case 11:
- m_OnTrigger11.FireOutput( pActivator, this, 0 );
- break;
- case 12:
- m_OnTrigger12.FireOutput( pActivator, this, 0 );
- break;
- case 13:
- m_OnTrigger13.FireOutput( pActivator, this, 0 );
- break;
- case 14:
- m_OnTrigger14.FireOutput( pActivator, this, 0 );
- break;
- case 15:
- m_OnTrigger15.FireOutput( pActivator, this, 0 );
- break;
- case 16:
- m_OnTrigger16.FireOutput( pActivator, this, 0 );
- break;
- }
- }
- break;
- case CChoreoEvent::SEQUENCE:
- {
- if ( pActor )
- {
- DispatchStartSequence( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::SECTION:
- {
- if ( IsMultiplayer() )
- break;
-
- // Pauses scene playback
- DispatchPauseScene( scene, event->GetParameters() );
- }
- break;
- case CChoreoEvent::LOOP:
- {
- DispatchProcessLoop( scene, event );
- }
- break;
- case CChoreoEvent::INTERRUPT:
- {
- if ( IsMultiplayer() )
- break;
-
- DispatchStartInterrupt( scene, event );
- }
- break;
-
- case CChoreoEvent::STOPPOINT:
- {
- if ( IsMultiplayer() )
- break;
-
- DispatchStopPoint( scene, event->GetParameters() );
- }
- break;
-
- case CChoreoEvent::PERMIT_RESPONSES:
- {
- if ( IsMultiplayer() )
- break;
-
- DispatchStartPermitResponses( scene, pActor, event );
- }
- break;
- default:
- {
- // FIXME: Unhandeled event
- // Assert(0);
- }
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : currenttime -
-// *event -
-//-----------------------------------------------------------------------------
-void CSceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
-{
- Assert( event );
-
- if ( !Q_stricmp( event->GetName(), "NULL" ) )
- {
- return;
- }
-
- CBaseFlex *pActor = NULL;
- CChoreoActor *actor = event->GetActor();
- if ( actor )
- {
- pActor = FindNamedActor( actor );
- }
-
- LocalScene_Printf( "%s : %8.2f: finish %s\n", STRING( m_iszSceneFile ), currenttime, event->GetDescription() );
-
- switch ( event->GetType() )
- {
- case CChoreoEvent::EXPRESSION:
- {
- if ( pActor && !IsMultiplayer() )
- {
- DispatchEndExpression( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::SPEAK:
- {
- if ( pActor )
- {
- DispatchEndSpeak( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::FLEXANIMATION:
- {
- if ( pActor && !IsMultiplayer() )
- {
- DispatchEndFlexAnimation( scene, pActor, event );
- }
- }
- break;
-
- case CChoreoEvent::LOOKAT:
- {
- if ( pActor && !IsMultiplayer() )
- {
- DispatchEndLookAt( scene, pActor, event );
- }
- }
- break;
-
-
- case CChoreoEvent::GESTURE:
- {
- if ( pActor )
- {
- DispatchEndGesture( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::GENERIC:
- {
- // If the first token in the parameters is "debugtext", we printed it and we're done
- if ( event->GetParameters() && !Q_strncmp( event->GetParameters(), "debugtext", 9 ) )
- break;
-
- if ( pActor )
- {
- DispatchEndGeneric( scene, pActor, event );
- }
- }
- break;
- case CChoreoEvent::SEQUENCE:
- {
- if ( pActor )
- {
- DispatchEndSequence( scene, pActor, event );
- }
- }
- break;
-
- case CChoreoEvent::FACE:
- {
- if ( pActor && !IsMultiplayer() )
- {
- DispatchEndFace( scene, pActor, event );
- }
- }
- break;
-
- case CChoreoEvent::MOVETO:
- {
- if ( pActor && !IsMultiplayer() )
- {
- DispatchEndMoveTo( scene, pActor, event );
- }
- }
- break;
-
- case CChoreoEvent::SUBSCENE:
- {
- if ( IsMultiplayer() )
- break;
-
- CChoreoScene *subscene = event->GetSubScene();
- if ( subscene )
- {
- subscene->ResetSimulation();
- }
- }
- break;
- case CChoreoEvent::INTERRUPT:
- {
- if ( IsMultiplayer() )
- break;
-
- DispatchEndInterrupt( scene, event );
- }
- break;
-
- case CChoreoEvent::PERMIT_RESPONSES:
- {
- if ( IsMultiplayer() )
- break;
-
- DispatchEndPermitResponses( scene, pActor, event );
- }
- break;
- default:
- break;
- }
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Only spew one time per missing scene!!!
-// Input : *scenename -
-//-----------------------------------------------------------------------------
-void MissingSceneWarning( char const *scenename )
-{
- static CUtlSymbolTable missing;
-
- // Make sure we only show the message once
- if ( UTL_INVAL_SYMBOL == missing.Find( scenename ) )
- {
- missing.AddString( scenename );
-
- Warning( "Scene '%s' missing!\n", scenename );
- }
-}
-
-bool CSceneEntity::ShouldNetwork() const
-{
- if ( m_bMultiplayer )
- {
- if ( m_pScene &&
- ( m_pScene->HasEventsOfType( CChoreoEvent::FLEXANIMATION ) ||
- m_pScene->HasEventsOfType( CChoreoEvent::EXPRESSION )||
- m_pScene->HasEventsOfType( CChoreoEvent::GESTURE ) ||
- m_pScene->HasEventsOfType( CChoreoEvent::SEQUENCE ) ) )
- {
- return true;
- }
- }
- else
- {
- if ( m_pScene &&
- ( m_pScene->HasEventsOfType( CChoreoEvent::FLEXANIMATION ) ||
- m_pScene->HasEventsOfType( CChoreoEvent::EXPRESSION ) ) )
- {
- return true;
- }
- }
-
- return false;
-}
-
-CChoreoScene *CSceneEntity::LoadScene( const char *filename, IChoreoEventCallback *pCallback )
-{
- DevMsg( 2, "Blocking load of scene from '%s'\n", filename );
-
- char loadfile[MAX_PATH];
- Q_strncpy( loadfile, filename, sizeof( loadfile ) );
- Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) );
- Q_FixSlashes( loadfile );
-
- // binary compiled vcd
- void *pBuffer;
- int fileSize;
- if ( !CopySceneFileIntoMemory( loadfile, &pBuffer, &fileSize ) )
- {
- MissingSceneWarning( loadfile );
- return NULL;
- }
-
- CChoreoScene *pScene = new CChoreoScene( NULL );
- CUtlBuffer buf( pBuffer, fileSize, CUtlBuffer::READ_ONLY );
- if ( !pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) )
- {
- Warning( "CSceneEntity::LoadScene: Unable to load binary scene '%s'\n", loadfile );
- delete pScene;
- pScene = NULL;
- }
- else
- {
- pScene->SetPrintFunc( LocalScene_Printf );
- pScene->SetEventCallbackInterface( pCallback );
- }
-
- FreeSceneFileMemory( pBuffer );
- return pScene;
-}
-
-CChoreoScene *BlockingLoadScene( const char *filename )
-{
- return CSceneEntity::LoadScene( filename, NULL );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::UnloadScene( void )
-{
- if ( m_pScene )
- {
- ClearSceneEvents( m_pScene, false );
-
- for ( int i = 0 ; i < m_pScene->GetNumActors(); i++ )
- {
- CBaseFlex *pTestActor = FindNamedActor( i );
-
- if ( !pTestActor )
- continue;
-
- pTestActor->RemoveChoreoScene( m_pScene );
- }
- }
- delete m_pScene;
- m_pScene = NULL;
-}
-
-//-----------------------------------------------------------------------------
-// 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 CSceneEntity::ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
-{
- switch ( event->GetType() )
- {
- case CChoreoEvent::SUBSCENE:
- {
- Assert( event->GetType() == CChoreoEvent::SUBSCENE );
-
- CChoreoScene *subscene = event->GetSubScene();
- if ( !subscene )
- return;
-
- if ( subscene->SimulationFinished() )
- return;
-
- // Have subscenes think for appropriate time
- subscene->Think( m_flFrameTime );
- }
- break;
-
- default:
- break;
- }
-
- 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 CSceneEntity::CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
-{
- switch ( event->GetType() )
- {
- case CChoreoEvent::SUBSCENE:
- {
- }
- break;
- default:
- {
- CBaseFlex *pActor = NULL;
- CChoreoActor *actor = event->GetActor();
- if ( actor )
- {
- pActor = FindNamedActor( actor );
- if (pActor == NULL)
- {
- Warning( "CSceneEntity %s unable to find actor \"%s\"\n", STRING(GetEntityName()), actor->GetName() );
- return true;
- }
- }
- if (pActor)
- {
- return pActor->CheckSceneEvent( currenttime, scene, event );
- }
- }
- break;
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Get a sticky version of a named actor
-// Input : CChoreoActor
-// Output : CBaseFlex
-//-----------------------------------------------------------------------------
-
-CBaseFlex *CSceneEntity::FindNamedActor( int index )
-{
- if (m_hActorList.Count() == 0)
- {
- m_hActorList.SetCount( m_pScene->GetNumActors() );
- NetworkProp()->NetworkStateForceUpdate();
- }
-
- if ( !m_hActorList.IsValidIndex( index ) )
- {
- DevWarning( "Scene %s has %d actors, but scene entity only has %d actors\n", m_pScene->GetFilename(), m_pScene->GetNumActors(), m_hActorList.Size() );
- return NULL;
- }
-
- CBaseFlex *pActor = m_hActorList[ index ];
-
- if (pActor == NULL || !pActor->IsAlive() )
- {
- CChoreoActor *pChoreoActor = m_pScene->GetActor( index );
- if ( !pChoreoActor )
- return NULL;
-
- pActor = FindNamedActor( pChoreoActor->GetName() );
-
- if (pActor)
- {
- // save who we found so we'll use them again
- m_hActorList[ index ] = pActor;
- NetworkProp()->NetworkStateForceUpdate();
- }
- }
-
- return pActor;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get a sticky version of a named actor
-// Input : CChoreoActor
-// Output : CBaseFlex
-//-----------------------------------------------------------------------------
-
-CBaseFlex *CSceneEntity::FindNamedActor( CChoreoActor *pChoreoActor )
-{
- int index = m_pScene->FindActorIndex( pChoreoActor );
-
- if (index >= 0)
- {
- return FindNamedActor( index );
- }
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Search for an actor by name, make sure it can do face poses
-// Input : *name -
-// Output : CBaseFlex
-//-----------------------------------------------------------------------------
-CBaseFlex *CSceneEntity::FindNamedActor( const char *name )
-{
- CBaseEntity *entity = FindNamedEntity( name, NULL, true );
-
- if ( !entity )
- {
- // Couldn't find actor!
- return NULL;
- }
-
- // Make sure it can actually do facial animation, etc.
- CBaseFlex *flexEntity = dynamic_cast< CBaseFlex * >( entity );
- if ( !flexEntity )
- {
- // That actor was not a CBaseFlex!
- return NULL;
- }
-
- return flexEntity;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Find an entity specified by a target name
-// Input : *name -
-// Output : CBaseEntity
-//-----------------------------------------------------------------------------
-CBaseEntity *CSceneEntity::FindNamedTarget( string_t iszTarget, bool bBaseFlexOnly )
-{
- if ( !stricmp( STRING(iszTarget), "!activator" ) )
- return m_hActivator;
-
- // If we don't have a wildcard in the target, just return the first entity found
- if ( !strchr( STRING(iszTarget), '*' ) )
- return gEntList.FindEntityByName( NULL, iszTarget );
-
- CBaseEntity *pTarget = NULL;
- while ( (pTarget = gEntList.FindEntityByName( pTarget, iszTarget )) != NULL )
- {
- if ( bBaseFlexOnly )
- {
- // Make sure it can actually do facial animation, etc.
- if ( dynamic_cast< CBaseFlex * >( pTarget ) )
- return pTarget;
- }
- else
- {
- return pTarget;
- }
- }
-
- // Failed to find one
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Filters entities only if they're clear
-//-----------------------------------------------------------------------------
-class CSceneFindMarkFilter : public IEntityFindFilter
-{
-public:
- void SetActor( CBaseEntity *pActor )
- {
- m_hActor = pActor;
- }
-
- bool ShouldFindEntity( CBaseEntity *pEntity )
- {
- if ( !m_hActor )
- return true;
-
- // If we find no truly valid marks, we'll just use the first.
- if ( !m_hEntityFound.Get() )
- {
- m_hEntityFound = pEntity;
- }
-
- // We only want marks that are clear
- trace_t tr;
- Vector vecOrigin = pEntity->GetAbsOrigin();
- AI_TraceHull( vecOrigin, vecOrigin, m_hActor->WorldAlignMins(), m_hActor->WorldAlignMaxs(), MASK_SOLID, m_hActor, COLLISION_GROUP_NONE, &tr );
- if ( tr.startsolid )
- {
- return false;
- }
- m_hEntityFound = pEntity;
- return true;
- }
-
- CBaseEntity *GetFilterResult( void )
- {
- return m_hEntityFound;
- }
-
-private:
- EHANDLE m_hActor;
-
- // To maintain backwards compatability, store off the first mark
- // we find. If we find no truly valid marks, we'll just use the first.
- EHANDLE m_hEntityFound;
-};
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds the entity nearest to both entities, and is clear
-//-----------------------------------------------------------------------------
-class CSceneFindNearestMarkFilter : public IEntityFindFilter
-{
-public:
-
- CSceneFindNearestMarkFilter( const CBaseEntity *pActor, const Vector &vecPos2, float flMaxRadius = MAX_TRACE_LENGTH )
- {
- m_vecPos2 = vecPos2;
-
- m_flMaxSegmentDistance = flMaxRadius;
-
- m_flNearestToTarget = flMaxRadius;
- m_pNearestToTarget = NULL;
- m_flNearestToActor = flMaxRadius;
- m_pNearestToActor = NULL;
-
- m_hActor = pActor;
- if (pActor)
- {
- m_vecPos1 = pActor->GetAbsOrigin();
- m_flMaxSegmentDistance = MIN( flMaxRadius, (m_vecPos1 - m_vecPos2).Length() + 1.0 );
- if (m_flMaxSegmentDistance <= 1.0)
- {
- // must be closest to self
- m_flMaxSegmentDistance = MIN( flMaxRadius, MAX_TRACE_LENGTH );
- }
- }
- }
-
- bool ShouldFindEntity( CBaseEntity *pEntity )
- {
- if ( !m_hActor )
- return true;
-
- // If we find no truly valid marks, we'll just use the first.
- if ( m_pNearestToActor == NULL )
- {
- m_pNearestToActor = pEntity;
- }
-
- // We only want marks that are clear
- trace_t tr;
- Vector vecOrigin = pEntity->GetAbsOrigin();
- AI_TraceHull( vecOrigin, vecOrigin, m_hActor->WorldAlignMins(), m_hActor->WorldAlignMaxs(), MASK_SOLID, m_hActor, COLLISION_GROUP_NONE, &tr );
- if ( !tr.startsolid || tr.m_pEnt == m_hActor)
- {
- float dist1 = (m_vecPos1 - pEntity->GetAbsOrigin()).Length();
- float dist2 = (m_vecPos2 - pEntity->GetAbsOrigin()).Length();
- /*
- char text[256];
- Q_snprintf( text, sizeof( text ), "%.0f : %.0f", dist1, dist2 );
- NDebugOverlay::Text( pEntity->GetAbsOrigin() + Vector( 0, 0, 8 ), text, false, 5.0f );
- */
- // find the point closest to the actor
- if (dist1 <= m_flNearestToActor)
- {
- m_pNearestToActor = pEntity;
- m_flNearestToActor = dist2;
- }
- // find that node that's closest to both, but the distance to it from the actor isn't farther than
- // the distance to the second node. This should keep the actor from walking past their point of interest
- if (dist1 <= m_flMaxSegmentDistance && dist2 <= m_flMaxSegmentDistance && dist2 < m_flNearestToTarget)
- {
- m_pNearestToTarget = pEntity;
- m_flNearestToTarget = dist2;
- }
- }
-
- return false;
- }
-
- CBaseEntity *GetFilterResult( void )
- {
- if (m_pNearestToTarget)
- return m_pNearestToTarget;
- return m_pNearestToActor;
- }
-
-private:
- EHANDLE m_hActor;
- Vector m_vecPos1;
- Vector m_vecPos2;
- float m_flMaxSegmentDistance;
- float m_flNearestToTarget;
- CBaseEntity *m_pNearestToTarget;
- float m_flNearestToActor;
- CBaseEntity *m_pNearestToActor;
-};
-
-//-----------------------------------------------------------------------------
-// Purpose: Search for an actor by name, make sure it can do face poses
-// Input : *name -
-// Output : CBaseFlex
-//-----------------------------------------------------------------------------
-CBaseEntity *CSceneEntity::FindNamedEntity( const char *name, CBaseEntity *pActor, bool bBaseFlexOnly, bool bUseClear )
-{
- CBaseEntity *entity = NULL;
-
- if ( !stricmp( name, "Player" ) || !stricmp( name, "!player" ))
- {
- entity = ( gpGlobals->maxClients == 1 ) ? ( CBaseEntity * )UTIL_GetLocalPlayer() : NULL;
- }
- else if ( !stricmp( name, "!target1" ) )
- {
- if (m_hTarget1 == NULL)
- {
- m_hTarget1 = FindNamedTarget( m_iszTarget1, bBaseFlexOnly );
- }
- return m_hTarget1;
- }
- else if ( !stricmp( name, "!target2" ) )
- {
- if (m_hTarget2 == NULL)
- {
- m_hTarget2 = FindNamedTarget( m_iszTarget2, bBaseFlexOnly );
- }
- return m_hTarget2;
- }
- else if ( !stricmp( name, "!target3" ) )
- {
- if (m_hTarget3 == NULL)
- {
- m_hTarget3 = FindNamedTarget( m_iszTarget3, bBaseFlexOnly );
- }
- return m_hTarget3;
- }
- else if ( !stricmp( name, "!target4" ) )
- {
- if (m_hTarget4 == NULL)
- {
- m_hTarget4 = FindNamedTarget( m_iszTarget4, bBaseFlexOnly );
- }
- return m_hTarget4;
- }
- else if ( !stricmp( name, "!target5" ) )
- {
- if (m_hTarget5 == NULL)
- {
- m_hTarget5 = FindNamedTarget( m_iszTarget5, bBaseFlexOnly );
- }
- return m_hTarget5;
- }
- else if ( !stricmp( name, "!target6" ) )
- {
- if (m_hTarget6 == NULL)
- {
- m_hTarget6 = FindNamedTarget( m_iszTarget6, bBaseFlexOnly );
- }
- return m_hTarget6;
- }
- else if ( !stricmp( name, "!target7" ) )
- {
- if (m_hTarget7 == NULL)
- {
- m_hTarget7 = FindNamedTarget( m_iszTarget7, bBaseFlexOnly );
- }
- return m_hTarget7;
- }
- else if ( !stricmp( name, "!target8" ) )
- {
- if (m_hTarget8 == NULL)
- {
- m_hTarget8 = FindNamedTarget( m_iszTarget8, bBaseFlexOnly );
- }
- return m_hTarget8;
- }
- else if (pActor && pActor->MyNPCPointer())
- {
- CSceneFindMarkFilter *pFilter = NULL;
- if ( bUseClear )
- {
- pFilter = new CSceneFindMarkFilter();
- pFilter->SetActor( pActor );
- }
-
- entity = pActor->MyNPCPointer()->FindNamedEntity( name, pFilter );
- if ( !entity && pFilter )
- {
- entity = pFilter->GetFilterResult();
- }
- }
- else
- {
- // search for up to 32 entities with the same name and choose one randomly
- CBaseEntity *entityList[ FINDNAMEDENTITY_MAX_ENTITIES ];
- int iCount;
-
- entity = NULL;
- for( iCount = 0; iCount < FINDNAMEDENTITY_MAX_ENTITIES; iCount++ )
- {
- entity = gEntList.FindEntityByName( entity, name, NULL, pActor );
- if ( !entity )
- {
- break;
- }
- entityList[ iCount ] = entity;
- }
-
- if ( iCount > 0 )
- {
- entity = entityList[ RandomInt( 0, iCount - 1 ) ];
- }
- else
- {
- entity = NULL;
- }
- }
-
- return entity;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Search for an actor by name, make sure it can do face poses
-// Input : *name -
-// Output : CBaseFlex
-//-----------------------------------------------------------------------------
-CBaseEntity *CSceneEntity::FindNamedEntityClosest( const char *name, CBaseEntity *pActor, bool bBaseFlexOnly, bool bUseClear, const char *pszSecondary )
-{
- CBaseEntity *entity = NULL;
-
- if ( !stricmp( name, "!activator" ) )
- {
- return m_hActivator;
- }
- else if ( !stricmp( name, "Player" ) || !stricmp( name, "!player" ))
- {
- entity = ( gpGlobals->maxClients == 1 ) ? ( CBaseEntity * )UTIL_GetLocalPlayer() : NULL;
- return entity;
- }
- else if ( !stricmp( name, "!target1" ) )
- {
- name = STRING( m_iszTarget1 );
- }
- else if ( !stricmp( name, "!target2" ) )
- {
- name = STRING( m_iszTarget2 );
- }
- else if ( !stricmp( name, "!target3" ) )
- {
- name = STRING( m_iszTarget3 );
- }
- else if ( !stricmp( name, "!target4" ) )
- {
- name = STRING( m_iszTarget4 );
- }
- else if ( !stricmp( name, "!target5" ) )
- {
- name = STRING( m_iszTarget5 );
- }
- else if ( !stricmp( name, "!target6" ) )
- {
- name = STRING( m_iszTarget6 );
- }
- else if ( !stricmp( name, "!target7" ) )
- {
- name = STRING( m_iszTarget7 );
- }
-
- if (pActor && pActor->MyNPCPointer())
- {
- if (pszSecondary && strlen( pszSecondary ) > 0)
- {
- CBaseEntity *pActor2 = FindNamedEntityClosest( pszSecondary, pActor, false, false, NULL );
-
- if (pActor2)
- {
- CSceneFindNearestMarkFilter *pFilter = new CSceneFindNearestMarkFilter( pActor, pActor2->GetAbsOrigin() );
-
- entity = pActor->MyNPCPointer()->FindNamedEntity( name, pFilter );
- if (!entity && pFilter)
- {
- entity = pFilter->GetFilterResult();
- }
- }
- }
- if (!entity)
- {
- CSceneFindMarkFilter *pFilter = NULL;
- if ( bUseClear )
- {
- pFilter = new CSceneFindMarkFilter();
- pFilter->SetActor( pActor );
- }
-
- entity = pActor->MyNPCPointer()->FindNamedEntity( name, pFilter );
- if (!entity && pFilter)
- {
- entity = pFilter->GetFilterResult();
- }
- }
- }
- else
- {
- // search for up to 32 entities with the same name and choose one randomly
- int iCount;
- entity = NULL;
- CBaseEntity *current = NULL;
- for( iCount = 0; iCount < FINDNAMEDENTITY_MAX_ENTITIES; iCount++ )
- {
- current = gEntList.FindEntityByName( current, name, NULL, pActor );
- if ( current )
- {
- if (RandomInt( 0, iCount ) == 0)
- entity = current;
- }
- }
-
- entity = NULL;
- }
-
- return entity;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Remove all "scene" expressions from all actors in this scene
-//-----------------------------------------------------------------------------
-void CSceneEntity::ClearSceneEvents( CChoreoScene *scene, bool canceled )
-{
- if ( !m_pScene )
- return;
-
- LocalScene_Printf( "%s : %8.2f: clearing events\n", STRING( m_iszSceneFile ), m_flCurrentTime );
-
- int i;
- for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
- {
- CBaseFlex *pActor = FindNamedActor( i );
- if ( !pActor )
- continue;
-
- // Clear any existing expressions
- pActor->ClearSceneEvents( scene, canceled );
- }
-
- // Iterate events and precache necessary resources
- for ( i = 0; i < scene->GetNumEvents(); i++ )
- {
- CChoreoEvent *event = scene->GetEvent( i );
- if ( !event )
- continue;
-
- // load any necessary data
- switch (event->GetType() )
- {
- default:
- break;
- case CChoreoEvent::SUBSCENE:
- {
- // Only allow a single level of subscenes for now
- if ( !scene->IsSubScene() )
- {
- CChoreoScene *subscene = event->GetSubScene();
- if ( subscene )
- {
- ClearSceneEvents( subscene, canceled );
- }
- }
- }
- break;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Remove all imposed schedules from all actors in this scene
-//-----------------------------------------------------------------------------
-void CSceneEntity::ClearSchedules( CChoreoScene *scene )
-{
- if ( !m_pScene )
- return;
-
- int i;
- for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
- {
- CBaseFlex *pActor = FindNamedActor( i );
- if ( !pActor )
- continue;
-
- CAI_BaseNPC *pNPC = pActor->MyNPCPointer();
-
- if ( pNPC )
- {
- /*
- if ( pNPC->IsCurSchedule( SCHED_SCENE_GENERIC ) )
- pNPC->ClearSchedule( "Scene entity clearing all actor schedules" );
- */
- }
- else
- {
- pActor->ResetSequence( pActor->SelectWeightedSequence( ACT_IDLE ) );
- pActor->SetCycle( 0 );
- }
- // Clear any existing expressions
- }
-
- // Iterate events and precache necessary resources
- for ( i = 0; i < scene->GetNumEvents(); i++ )
- {
- CChoreoEvent *event = scene->GetEvent( i );
- if ( !event )
- continue;
-
- // load any necessary data
- switch (event->GetType() )
- {
- default:
- break;
- case CChoreoEvent::SUBSCENE:
- {
- // Only allow a single level of subscenes for now
- if ( !scene->IsSubScene() )
- {
- CChoreoScene *subscene = event->GetSubScene();
- if ( subscene )
- {
- ClearSchedules( subscene );
- }
- }
- }
- break;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: If we are currently interruptable, pause this scene and wait for the other
-// scene to finish
-// Input : *otherScene -
-//-----------------------------------------------------------------------------
-bool CSceneEntity::InterruptThisScene( CSceneEntity *otherScene )
-{
- Assert( otherScene );
-
- if ( !IsInterruptable() )
- {
- return false;
- }
-
- // Already interrupted
- if ( m_bInterrupted )
- {
- return false;
- }
-
- m_bInterrupted = true;
- m_hInterruptScene = otherScene;
-
- // Ask other scene to tell us when it's finished or canceled
- otherScene->RequestCompletionNotification( this );
-
- PausePlayback();
- return true;
-}
-
-/*
-void scene_interrupt( const CCommand &args )
-{
- if ( args.ArgC() != 3 )
- return;
-
- const char *scene1 = args[1];
- const char *scene2 = args[2];
-
- CSceneEntity *s1 = dynamic_cast< CSceneEntity * >( gEntList.FindEntityByName( NULL, scene1 ) );
- CSceneEntity *s2 = dynamic_cast< CSceneEntity * >( gEntList.FindEntityByName( NULL, scene2 ) );
-
- if ( !s1 || !s2 )
- return;
-
- if ( s1->InterruptThisScene( s2 ) )
- {
- s2->StartPlayback();
- }
-}
-
-static ConCommand interruptscene( "int", scene_interrupt, "interrupt scene 1 with scene 2.", FCVAR_CHEAT );
-*/
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::CheckInterruptCompletion()
-{
- if ( !m_bInterrupted )
- return;
-
- // If the interruptor goes away it's the same as having that scene finish up...
- if ( m_hInterruptScene != NULL &&
- !m_bInterruptSceneFinished )
- {
- return;
- }
-
- m_bInterrupted = false;
- m_hInterruptScene = NULL;
-
- ResumePlayback();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::ClearInterrupt()
-{
- m_nInterruptCount = 0;
- m_bInterrupted = false;
- m_hInterruptScene = NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Another scene is asking us to notify upon completion
-// Input : *notify -
-//-----------------------------------------------------------------------------
-void CSceneEntity::RequestCompletionNotification( CSceneEntity *notify )
-{
- CHandle< CSceneEntity > h;
- h = notify;
- // Only add it once
- if ( m_hNotifySceneCompletion.Find( h ) == m_hNotifySceneCompletion.InvalidIndex() )
- {
- m_hNotifySceneCompletion.AddToTail( h );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: An interrupt scene has finished or been canceled, we can resume once we pick up this state in CheckInterruptCompletion
-// Input : *interruptor -
-//-----------------------------------------------------------------------------
-void CSceneEntity::NotifyOfCompletion( CSceneEntity *interruptor )
-{
- Assert( m_bInterrupted );
- Assert( m_hInterruptScene == interruptor );
- m_bInterruptSceneFinished = true;
-
- CheckInterruptCompletion();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneEntity::AddListManager( CSceneListManager *pManager )
-{
- CHandle< CSceneListManager > h;
- h = pManager;
- // Only add it once
- if ( m_hListManagers.Find( h ) == m_hListManagers.InvalidIndex() )
- {
- m_hListManagers.AddToTail( h );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Clear any targets that a referencing !activator
-//-----------------------------------------------------------------------------
-void CSceneEntity::ClearActivatorTargets( void )
-{
- if ( !stricmp( STRING(m_iszTarget1), "!activator" ) )
- {
- // We need to clear out actors so they're re-evaluated
- m_hActorList.Purge();
- NetworkProp()->NetworkStateForceUpdate();
- m_hTarget1 = NULL;
- }
- if ( !stricmp( STRING(m_iszTarget2), "!activator" ) )
- {
- // We need to clear out actors so they're re-evaluated
- m_hActorList.Purge();
- NetworkProp()->NetworkStateForceUpdate();
- m_hTarget2 = NULL;
- }
- if ( !stricmp( STRING(m_iszTarget3), "!activator" ) )
- {
- // We need to clear out actors so they're re-evaluated
- m_hActorList.Purge();
- NetworkProp()->NetworkStateForceUpdate();
- m_hTarget3 = NULL;
- }
- if ( !stricmp( STRING(m_iszTarget4), "!activator" ) )
- {
- // We need to clear out actors so they're re-evaluated
- m_hActorList.Purge();
- NetworkProp()->NetworkStateForceUpdate();
- m_hTarget4 = NULL;
- }
- if ( !stricmp( STRING(m_iszTarget5), "!activator" ) )
- {
- // We need to clear out actors so they're re-evaluated
- m_hActorList.Purge();
- NetworkProp()->NetworkStateForceUpdate();
- m_hTarget5 = NULL;
- }
- if ( !stricmp( STRING(m_iszTarget6), "!activator" ) )
- {
- // We need to clear out actors so they're re-evaluated
- m_hActorList.Purge();
- NetworkProp()->NetworkStateForceUpdate();
- m_hTarget6 = NULL;
- }
- if ( !stricmp( STRING(m_iszTarget7), "!activator" ) )
- {
- // We need to clear out actors so they're re-evaluated
- m_hActorList.Purge();
- NetworkProp()->NetworkStateForceUpdate();
- m_hTarget7 = NULL;
- }
- if ( !stricmp( STRING(m_iszTarget8), "!activator" ) )
- {
- // We need to clear out actors so they're re-evaluated
- m_hActorList.Purge();
- NetworkProp()->NetworkStateForceUpdate();
- m_hTarget8 = NULL;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called when a scene is completed or canceled
-//-----------------------------------------------------------------------------
-void CSceneEntity::OnSceneFinished( bool canceled, bool fireoutput )
-{
- if ( !m_pScene )
- return;
-
- LocalScene_Printf( "%s : %8.2f: finished\n", STRING( m_iszSceneFile ), m_flCurrentTime );
-
- // Notify any listeners
- int c = m_hNotifySceneCompletion.Count();
- int i;
- for ( i = 0; i < c; i++ )
- {
- CSceneEntity *ent = m_hNotifySceneCompletion[ i ].Get();
- if ( !ent )
- continue;
-
- ent->NotifyOfCompletion( this );
- }
- m_hNotifySceneCompletion.RemoveAll();
-
- // Clear simulation
- m_pScene->ResetSimulation();
- m_bIsPlayingBack = false;
- m_bPaused = false;
- SetCurrentTime( 0.0f, false );
-
- // Clear interrupt state if we were interrupted for some reason
- ClearInterrupt();
-
- if ( fireoutput && !m_bCompletedEarly)
- {
- m_OnCompletion.FireOutput( this, this, 0 );
- }
-
- // Put face back in neutral pose
- ClearSceneEvents( m_pScene, canceled );
-
- for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
- {
- CBaseFlex *pTestActor = FindNamedActor( i );
-
- if ( !pTestActor )
- continue;
-
- pTestActor->RemoveChoreoScene( m_pScene, canceled );
-
- // If we interrupted the actor's previous scenes, resume them
- if ( m_bInterruptedActorsScenes )
- {
- QueueActorsScriptedScenesToResume( pTestActor, false );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Should we transmit it to the client?
-//-----------------------------------------------------------------------------
-int CSceneEntity::UpdateTransmitState()
-{
- if ( !ShouldNetwork() )
- {
- return SetTransmitState( FL_EDICT_DONTSEND );
- }
-
- if ( m_pRecipientFilter )
- {
- return SetTransmitState( FL_EDICT_FULLCHECK );
- }
-
- return SetTransmitState( FL_EDICT_ALWAYS );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Which clients should we be transmitting to?
-//-----------------------------------------------------------------------------
-int CSceneEntity::ShouldTransmit( const CCheckTransmitInfo *pInfo )
-{
- int result = BaseClass::ShouldTransmit( pInfo );
-
- // if we have excluded them via our recipient filter, don't send
- if ( m_pRecipientFilter && result != FL_EDICT_DONTSEND )
- {
- bool bFound = false;
-
- // If we can't find them in the recipient list, exclude
- int i;
- for ( i=0; i<m_pRecipientFilter->GetRecipientCount();i++ )
- {
- int iRecipient = m_pRecipientFilter->GetRecipientIndex(i);
-
- CBasePlayer *player = static_cast< CBasePlayer * >( CBaseEntity::Instance( iRecipient ) );
-
- if ( player && player->edict() == pInfo->m_pClientEnt )
- {
- bFound = true;
- break;
- }
- }
-
- if ( !bFound )
- {
- result = FL_EDICT_DONTSEND;
- }
- }
-
- return result;
-}
-
-void CSceneEntity::SetRecipientFilter( IRecipientFilter *filter )
-{
- // create a copy of this filter
- if ( filter )
- {
- m_pRecipientFilter = new CRecipientFilter();
- m_pRecipientFilter->CopyFrom( (CRecipientFilter &)( *filter ) );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-class CInstancedSceneEntity : public CSceneEntity
-{
- DECLARE_DATADESC();
- DECLARE_CLASS( CInstancedSceneEntity, CSceneEntity );
-public:
- EHANDLE m_hOwner;
- bool m_bHadOwner;
- float m_flPostSpeakDelay;
- float m_flPreDelay;
- char m_szInstanceFilename[ CChoreoScene::MAX_SCENE_FILENAME ];
- bool m_bIsBackground;
-
- virtual void StartPlayback( void );
- virtual void DoThink( float frametime );
- virtual CBaseFlex *FindNamedActor( const char *name );
- virtual CBaseEntity *FindNamedEntity( const char *name );
- virtual float GetPostSpeakDelay() { return m_flPostSpeakDelay; }
- virtual void SetPostSpeakDelay( float flDelay ) { m_flPostSpeakDelay = flDelay; }
- virtual float GetPreDelay() { return m_flPreDelay; }
- virtual void SetPreDelay( float flDelay ) { m_flPreDelay = flDelay; }
-
- virtual void OnLoaded();
-
- virtual void DispatchStartMoveTo( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
- {
- if (PassThrough( actor )) BaseClass::DispatchStartMoveTo( scene, actor, actor2, event );
- };
-
- virtual void DispatchEndMoveTo( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
- {
- if (PassThrough( actor )) BaseClass::DispatchEndMoveTo( scene, actor, event );
- };
-
- virtual void DispatchStartFace( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
- {
- if (PassThrough( actor )) BaseClass::DispatchStartFace( scene, actor, actor2, event );
- };
-
- virtual void DispatchEndFace( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
- {
- if (PassThrough( actor )) BaseClass::DispatchEndFace( scene, actor, event );
- };
-
- virtual void DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
- {
- if ( IsMultiplayer() )
- {
- BaseClass::DispatchStartSequence( scene, actor, event );
- }
- };
- virtual void DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
- {
- if ( IsMultiplayer() )
- {
- BaseClass::DispatchEndSequence( scene, actor, event );
- }
- };
- virtual void DispatchPauseScene( CChoreoScene *scene, const char *parameters ) { /* suppress */ };
-
- void OnRestore();
-
- virtual float EstimateLength( void );
-
-private:
- bool PassThrough( CBaseFlex *actor );
-};
-
-LINK_ENTITY_TO_CLASS( instanced_scripted_scene, CInstancedSceneEntity );
-
-//---------------------------------------------------------
-// Save/Restore
-//---------------------------------------------------------
-BEGIN_DATADESC( CInstancedSceneEntity )
-
- DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
- DEFINE_FIELD( m_bHadOwner, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_flPostSpeakDelay, FIELD_FLOAT ),
- DEFINE_FIELD( m_flPreDelay, FIELD_FLOAT ),
- DEFINE_AUTO_ARRAY( m_szInstanceFilename, FIELD_CHARACTER ),
- DEFINE_FIELD( m_bIsBackground, FIELD_BOOLEAN ),
-
-END_DATADESC()
-
-//-----------------------------------------------------------------------------
-// Purpose: create a one-shot scene, no movement, sequences, etc.
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-float InstancedScriptedScene( CBaseFlex *pActor, const char *pszScene, EHANDLE *phSceneEnt,
- float flPostDelay, bool bIsBackground, AI_Response *response,
- bool bMultiplayer, IRecipientFilter *filter /* = NULL */ )
-{
- VPROF( "InstancedScriptedScene" );
-
- CInstancedSceneEntity *pScene = (CInstancedSceneEntity *)CBaseEntity::CreateNoSpawn( "instanced_scripted_scene", vec3_origin, vec3_angle );
-
- // This code expands any $gender tags into male or female tags based on the gender of the actor (based on his/her .mdl)
- if ( pActor )
- {
- pActor->GenderExpandString( pszScene, pScene->m_szInstanceFilename, sizeof( pScene->m_szInstanceFilename ) );
- }
- else
- {
- Q_strncpy( pScene->m_szInstanceFilename, pszScene, sizeof( pScene->m_szInstanceFilename ) );
- }
- pScene->m_iszSceneFile = MAKE_STRING( pScene->m_szInstanceFilename );
-
- // FIXME: I should set my output to fire something that kills me....
-
- // FIXME: add a proper initialization function
- pScene->m_hOwner = pActor;
- pScene->m_bHadOwner = pActor != NULL;
- pScene->m_bMultiplayer = bMultiplayer;
- pScene->SetPostSpeakDelay( flPostDelay );
- DispatchSpawn( pScene );
- pScene->Activate();
- pScene->m_bIsBackground = bIsBackground;
-
- pScene->SetBackground( bIsBackground );
- pScene->SetRecipientFilter( filter );
-
- if ( response )
- {
- float flPreDelay = response->GetPreDelay();
- if ( flPreDelay )
- {
- pScene->SetPreDelay( flPreDelay );
- }
- }
-
- pScene->StartPlayback();
-
- if ( response )
- {
- // If the response wants us to abort on NPC state switch, remember that
- pScene->SetBreakOnNonIdle( response->ShouldBreakOnNonIdle() );
- }
-
- if ( phSceneEnt )
- {
- *phSceneEnt = pScene;
- }
-
- return pScene->EstimateLength();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pActor -
-// *soundnmame -
-// *phSceneEnt -
-// Output : float
-//-----------------------------------------------------------------------------
-float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt /*= NULL*/ )
-{
- if ( !pActor )
- {
- Warning( "InstancedAutoGeneratedSoundScene: Expecting non-NULL pActor for sound %s\n", soundname );
- return 0;
- }
-
- CInstancedSceneEntity *pScene = (CInstancedSceneEntity *)CBaseEntity::CreateNoSpawn( "instanced_scripted_scene", vec3_origin, vec3_angle );
-
- Q_strncpy( pScene->m_szInstanceFilename, UTIL_VarArgs( "AutoGenerated(%s)", soundname ), sizeof( pScene->m_szInstanceFilename ) );
- pScene->m_iszSceneFile = MAKE_STRING( pScene->m_szInstanceFilename );
-
- pScene->m_hOwner = pActor;
- pScene->m_bHadOwner = pActor != NULL;
-
- pScene->GenerateSoundScene( pActor, soundname );
-
- pScene->Spawn();
- pScene->Activate();
- pScene->StartPlayback();
-
- if ( phSceneEnt )
- {
- *phSceneEnt = pScene;
- }
-
- return pScene->EstimateLength();
-}
-
-//-----------------------------------------------------------------------------
-
-void StopScriptedScene( CBaseFlex *pActor, EHANDLE hSceneEnt )
-{
- CBaseEntity *pEntity = hSceneEnt;
- CSceneEntity *pScene = dynamic_cast<CSceneEntity *>(pEntity);
-
- if ( pScene )
- {
- LocalScene_Printf( "%s : stop scripted scene\n", STRING( pScene->m_iszSceneFile ) );
- pScene->CancelPlayback();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pszScene -
-// Output : float
-//-----------------------------------------------------------------------------
-float GetSceneDuration( char const *pszScene )
-{
- unsigned int msecs = 0;
-
- SceneCachedData_t cachedData;
- if ( scenefilecache->GetSceneCachedData( pszScene, &cachedData ) )
- {
- msecs = cachedData.msecs;
- }
-
- return (float)msecs * 0.001f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pszScene -
-// Output : int
-//-----------------------------------------------------------------------------
-int GetSceneSpeechCount( char const *pszScene )
-{
- SceneCachedData_t cachedData;
- if ( scenefilecache->GetSceneCachedData( pszScene, &cachedData ) )
- {
- return cachedData.numSounds;
- }
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Used for precaching instanced scenes
-// Input : *pszScene -
-//-----------------------------------------------------------------------------
-void PrecacheInstancedScene( char const *pszScene )
-{
- static int nMakingReslists = -1;
-
- if ( nMakingReslists == -1 )
- {
- nMakingReslists = CommandLine()->FindParm( "-makereslists" ) > 0 ? 1 : 0;
- }
-
- if ( nMakingReslists == 1 )
- {
- // Just stat the file to add to reslist
- g_pFullFileSystem->Size( pszScene );
- }
-
- // verify existence, cache is pre-populated, should be there
- SceneCachedData_t sceneData;
- if ( !scenefilecache->GetSceneCachedData( pszScene, &sceneData ) )
- {
- // Scenes are sloppy and don't always exist.
- // A scene that is not in the pre-built cache image, but on disk, is a true error.
- if ( developer.GetInt() && ( IsX360() && ( g_pFullFileSystem->GetDVDMode() != DVDMODE_STRICT ) && g_pFullFileSystem->FileExists( pszScene, "GAME" ) ) )
- {
- Warning( "PrecacheInstancedScene: Missing scene '%s' from scene image cache.\nRebuild scene image cache!\n", pszScene );
- }
- }
- else
- {
- for ( int i = 0; i < sceneData.numSounds; ++i )
- {
- short stringId = scenefilecache->GetSceneCachedSound( sceneData.sceneId, i );
- CBaseEntity::PrecacheScriptSound( scenefilecache->GetSceneString( stringId ) );
- }
- }
-
- g_pStringTableClientSideChoreoScenes->AddString( CBaseEntity::IsServer(), pszScene );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CInstancedSceneEntity::StartPlayback( void )
-{
- // Wait until our pre delay is over
- if ( GetPreDelay() )
- return;
-
- BaseClass::StartPlayback();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CInstancedSceneEntity::DoThink( float frametime )
-{
- CheckInterruptCompletion();
-
- if ( m_flPreDelay > 0 )
- {
- m_flPreDelay = MAX( 0, m_flPreDelay - frametime );
- StartPlayback();
- if ( !m_bIsPlayingBack )
- return;
- }
-
- if ( !m_pScene || !m_bIsPlayingBack || ( m_bHadOwner && m_hOwner == NULL ) )
- {
- UTIL_Remove( this );
- return;
- }
-
- // catch bad pitch shifting from old save games
- Assert( m_fPitch >= SCENE_MIN_PITCH && m_fPitch <= SCENE_MAX_PITCH );
- m_fPitch = clamp( m_fPitch, SCENE_MIN_PITCH, SCENE_MAX_PITCH );
-
- if ( m_bPaused )
- {
- PauseThink();
- return;
- }
-
- float dt = frametime;
-
- m_pScene->SetSoundFileStartupLatency( GetSoundSystemLatency() );
-
- // Tell scene to go
- m_pScene->Think( m_flCurrentTime );
- // Drive simulation time for scene
- SetCurrentTime( m_flCurrentTime + dt * m_fPitch, false );
-
- // Did we get to the end
- if ( m_pScene->SimulationFinished() )
- {
- OnSceneFinished( false, false );
-
- UTIL_Remove( this );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Search for an actor by name, make sure it can do face poses
-// Input : *name -
-// Output : CBaseFlex
-//-----------------------------------------------------------------------------
-CBaseFlex *CInstancedSceneEntity::FindNamedActor( const char *name )
-{
- if ( m_pScene->GetNumActors() == 1 || stricmp( name, "!self" ) == 0 )
- {
- if ( m_hOwner != NULL )
- {
- CBaseCombatCharacter *pCharacter = m_hOwner->MyCombatCharacterPointer();
- if ( pCharacter )
- {
- return pCharacter;
- }
- }
- }
- return BaseClass::FindNamedActor( name );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Search for an actor by name, make sure it can do face poses
-// Input : *name -
-// Output : CBaseFlex
-//-----------------------------------------------------------------------------
-CBaseEntity *CInstancedSceneEntity::FindNamedEntity( const char *name )
-{
- CBaseEntity *pOther = NULL;
-
- if (m_hOwner != NULL)
- {
- CAI_BaseNPC *npc = m_hOwner->MyNPCPointer();
-
- if (npc)
- {
- pOther = npc->FindNamedEntity( name );
- }
- else if ( m_hOwner->MyCombatCharacterPointer() )
- {
- pOther = m_hOwner;
- }
- }
-
- if (!pOther)
- {
- pOther = BaseClass::FindNamedEntity( name );
- }
- return pOther;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Suppress certain events when it's instanced since they're can cause odd problems
-// Input : actor
-// Output : true - the event should happen, false - it shouldn't
-//-----------------------------------------------------------------------------
-
-bool CInstancedSceneEntity::PassThrough( CBaseFlex *actor )
-{
- if (!actor)
- return false;
-
- CAI_BaseNPC *myNpc = actor->MyNPCPointer( );
-
- if (!myNpc)
- return false;
-
- if (myNpc->IsCurSchedule( SCHED_SCENE_GENERIC ))
- {
- return true;
- }
-
- if (myNpc->GetCurSchedule())
- {
- CAI_ScheduleBits testBits;
- myNpc->GetCurSchedule()->GetInterruptMask( &testBits );
-
- if (testBits.IsBitSet( COND_IDLE_INTERRUPT ))
- {
- return true;
- }
- }
-
- LocalScene_Printf( "%s : event suppressed\n", STRING( m_iszSceneFile ) );
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-void CInstancedSceneEntity::OnRestore()
-{
- if ( m_bHadOwner && !m_hOwner )
- {
- // probably just came back from a level transition
- UTIL_Remove( this );
- return;
- }
- // reset background state
- if ( m_pScene )
- {
- m_pScene->SetBackground( m_bIsBackground );
- }
- BaseClass::OnRestore();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-float CInstancedSceneEntity::EstimateLength( void )
-{
- return (BaseClass::EstimateLength() + GetPreDelay());
-}
-
-
-void CInstancedSceneEntity::OnLoaded()
-{
- BaseClass::OnLoaded();
- SetBackground( m_bIsBackground );
-}
-
-bool g_bClientFlex = true;
-
-LINK_ENTITY_TO_CLASS( scene_manager, CSceneManager );
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneManager::Think()
-{
- // Latch this only once per frame...
- g_bClientFlex = scene_clientflex.GetBool();
-
- // The manager is always thinking at 20 hz
- SetNextThink( gpGlobals->curtime + SCENE_THINK_INTERVAL );
- float frameTime = ( gpGlobals->curtime - GetLastThink() );
- frameTime = MIN( 0.1, frameTime );
-
- // stop if AI is diabled
- if (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
- return;
-
- bool needCleanupPass = false;
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *scene = m_ActiveScenes[ i ].Get();
- if ( !scene )
- {
- needCleanupPass = true;
- continue;
- }
-
- scene->DoThink( frameTime );
-
- if ( m_ActiveScenes.Count() < c )
- {
- // Scene removed self while thinking. Adjust iteration.
- c = m_ActiveScenes.Count();
- i--;
- }
- }
-
- // Now delete any invalid ones
- if ( needCleanupPass )
- {
- for ( int i = c - 1; i >= 0; i-- )
- {
- CSceneEntity *scene = m_ActiveScenes[ i ].Get();
- if ( scene )
- continue;
-
- m_ActiveScenes.Remove( i );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneManager::ClearAllScenes()
-{
- m_ActiveScenes.RemoveAll();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *scene -
-//-----------------------------------------------------------------------------
-void CSceneManager::AddSceneEntity( CSceneEntity *scene )
-{
- CHandle< CSceneEntity > h;
-
- h = scene;
-
- // Already added/activated
- if ( m_ActiveScenes.Find( h ) != m_ActiveScenes.InvalidIndex() )
- {
- return;
- }
-
- m_ActiveScenes.AddToTail( h );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *scene -
-//-----------------------------------------------------------------------------
-void CSceneManager::RemoveSceneEntity( CSceneEntity *scene )
-{
- CHandle< CSceneEntity > h;
-
- h = scene;
-
- m_ActiveScenes.FindAndRemove( h );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *player -
-//-----------------------------------------------------------------------------
-void CSceneManager::OnClientActive( CBasePlayer *player )
-{
- int c = m_QueuedSceneSounds.Count();
- for ( int i = 0; i < c; i++ )
- {
- CRestoreSceneSound *sound = &m_QueuedSceneSounds[ i ];
-
- if ( sound->actor == NULL )
- continue;
-
- // Blow off sounds too far in past to encode over networking layer
- if ( fabs( 1000.0f * sound->time_in_past ) > MAX_SOUND_DELAY_MSEC )
- continue;
-
- CPASAttenuationFilter filter( sound->actor );
-
- EmitSound_t es;
- es.m_nChannel = CHAN_VOICE;
- es.m_flVolume = 1;
- es.m_pSoundName = sound->soundname;
- es.m_SoundLevel = sound->soundlevel;
- es.m_flSoundTime = gpGlobals->curtime - sound->time_in_past;
-
- EmitSound( filter, sound->actor->entindex(), es );
- }
-
- m_QueuedSceneSounds.RemoveAll();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Deletes scenes involving the specified actor
-//-----------------------------------------------------------------------------
-void CSceneManager::RemoveScenesInvolvingActor( CBaseFlex *pActor )
-{
- if ( !pActor )
- return;
-
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene )
- {
- continue;
- }
-
- if ( pScene->InvolvesActor( pActor ) ) // NOTE: returns false if scene hasn't loaded yet
- {
- LocalScene_Printf( "%s : removed for '%s'\n", STRING( pScene->m_iszSceneFile ), pActor ? pActor->GetDebugName() : "NULL" );
- pScene->CancelPlayback();
- }
- else
- {
- CInstancedSceneEntity *pInstancedScene = dynamic_cast< CInstancedSceneEntity * >( pScene );
- if ( pInstancedScene && pInstancedScene->m_hOwner )
- {
- if ( pInstancedScene->m_hOwner == pActor )
- {
- if ( pInstancedScene->m_bIsPlayingBack )
- {
- pInstancedScene->OnSceneFinished( true, false );
- }
-
- LocalScene_Printf( "%s : removed for '%s'\n", STRING( pInstancedScene->m_iszSceneFile ), pActor ? pActor->GetDebugName() : "NULL" );
- UTIL_Remove( pInstancedScene );
- }
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Stops scenes involving the specified actor
-//-----------------------------------------------------------------------------
-void CSceneManager::RemoveActorFromScenes( CBaseFlex *pActor, bool bInstancedOnly, bool bNonIdleOnly, const char *pszThisSceneOnly )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene )
- {
- continue;
- }
-
- // If only stopping instanced scenes, then skip it if it can't cast to an instanced scene
- if ( bInstancedOnly &&
- ( dynamic_cast< CInstancedSceneEntity * >( pScene ) == NULL ) )
- {
- continue;
- }
-
- if ( bNonIdleOnly && !pScene->ShouldBreakOnNonIdle() )
- continue;
-
- if ( pScene->InvolvesActor( pActor ) )
- {
- if ( pszThisSceneOnly && pszThisSceneOnly[0] )
- {
- if ( Q_strcmp( pszThisSceneOnly, STRING(pScene->m_iszSceneFile) ) )
- continue;
- }
-
- LocalScene_Printf( "%s : removed for '%s'\n", STRING( pScene->m_iszSceneFile ), pActor ? pActor->GetDebugName() : "NULL" );
- pScene->CancelPlayback();
- }
-
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Pause scenes involving the specified actor
-//-----------------------------------------------------------------------------
-void CSceneManager::PauseActorsScenes( CBaseFlex *pActor, bool bInstancedOnly )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene )
- {
- continue;
- }
-
- // If only stopping instanced scenes, then skip it if it can't cast to an instanced scene
- if ( bInstancedOnly &&
- ( dynamic_cast< CInstancedSceneEntity * >( pScene ) == NULL ) )
- {
- continue;
- }
-
- if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() )
- {
- LocalScene_Printf( "Pausing actor %s scripted scene: %s\n", pActor->GetDebugName(), STRING(pScene->m_iszSceneFile) );
-
- variant_t emptyVariant;
- pScene->AcceptInput( "Pause", pScene, pScene, emptyVariant, 0 );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return true if this Actor is only in scenes that are interruptable right now
-//-----------------------------------------------------------------------------
-bool CSceneManager::IsInInterruptableScenes( CBaseFlex *pActor )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene )
- continue;
-
- //Ignore background scenes since they're harmless.
- if ( pScene->IsBackground() == true )
- continue;
-
- if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() )
- {
- if ( pScene->IsInterruptable() == false )
- return false;
- }
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Resume any paused scenes involving the specified actor
-//-----------------------------------------------------------------------------
-void CSceneManager::ResumeActorsScenes( CBaseFlex *pActor, bool bInstancedOnly )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene )
- {
- continue;
- }
-
- // If only stopping instanced scenes, then skip it if it can't cast to an instanced scene
- if ( bInstancedOnly &&
- ( dynamic_cast< CInstancedSceneEntity * >( pScene ) == NULL ) )
- {
- continue;
- }
-
- if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() )
- {
- LocalScene_Printf( "Resuming actor %s scripted scene: %s\n", pActor->GetDebugName(), STRING(pScene->m_iszSceneFile) );
-
- variant_t emptyVariant;
- pScene->AcceptInput( "Resume", pScene, pScene, emptyVariant, 0 );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Set all paused, in-playback scenes to resume when the actor is ready
-//-----------------------------------------------------------------------------
-void CSceneManager::QueueActorsScenesToResume( CBaseFlex *pActor, bool bInstancedOnly )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene )
- {
- continue;
- }
-
- // If only stopping instanced scenes, then skip it if it can't cast to an instanced scene
- if ( bInstancedOnly &&
- ( dynamic_cast< CInstancedSceneEntity * >( pScene ) == NULL ) )
- {
- continue;
- }
-
- if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() && pScene->IsPaused() )
- {
- pScene->QueueResumePlayback();
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: returns if there are scenes involving the specified actor
-//-----------------------------------------------------------------------------
-bool CSceneManager::IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene ||
- !pScene->IsPlayingBack() ||
- ( bIgnoreInstancedScenes && dynamic_cast<CInstancedSceneEntity *>(pScene) != NULL )
- )
- {
- continue;
- }
-
- if ( pScene->InvolvesActor( pActor ) )
- {
- return true;
- }
- }
- return false;
-}
-
-bool CSceneManager::IsRunningScriptedSceneAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene ||
- !pScene->IsPlayingBack() ||
- pScene->IsPaused() ||
- ( bIgnoreInstancedScenes && dynamic_cast<CInstancedSceneEntity *>(pScene) != NULL )
- )
- {
- continue;
- }
-
- if ( pScene->InvolvesActor( pActor ) )
- {
- return true;
- }
- }
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pActor -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CSceneManager::IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene ||
- !pScene->IsPlayingBack() ||
- ( bIgnoreInstancedScenes && dynamic_cast<CInstancedSceneEntity *>(pScene) != NULL )
- )
- {
- continue;
- }
-
- if ( pScene->InvolvesActor( pActor ) )
- {
- if ( pScene->HasUnplayedSpeech() )
- return true;
- }
- }
- return false;
-}
-
-
-bool CSceneManager::IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
-{
- int c = m_ActiveScenes.Count();
- for ( int i = 0; i < c; i++ )
- {
- CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
- if ( !pScene ||
- !pScene->IsPlayingBack() ||
- pScene->IsPaused() ||
- ( bIgnoreInstancedScenes && dynamic_cast<CInstancedSceneEntity *>(pScene) != NULL )
- )
- {
- continue;
- }
-
- if ( pScene->InvolvesActor( pActor ) )
- {
- if ( pScene->HasUnplayedSpeech() )
- return true;
- }
- }
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *actor -
-// *soundname -
-// soundlevel -
-// soundtime -
-//-----------------------------------------------------------------------------
-void CSceneManager::QueueRestoredSound( CBaseFlex *actor, char const *soundname, soundlevel_t soundlevel, float time_in_past )
-{
- CRestoreSceneSound e;
- e.actor = actor;
- Q_strncpy( e.soundname, soundname, sizeof( e.soundname ) );
- e.soundlevel = soundlevel;
- e.time_in_past = time_in_past;
-
- m_QueuedSceneSounds.AddToTail( e );
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void RemoveActorFromScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly, bool nonidlescenesonly, const char *pszThisSceneOnly )
-{
- GetSceneManager()->RemoveActorFromScenes( pActor, instancedscenesonly, nonidlescenesonly, pszThisSceneOnly );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void RemoveAllScenesInvolvingActor( CBaseFlex *pActor )
-{
- GetSceneManager()->RemoveScenesInvolvingActor( pActor );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void PauseActorsScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly )
-{
- GetSceneManager()->PauseActorsScenes( pActor, instancedscenesonly );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool IsInInterruptableScenes( CBaseFlex *pActor )
-{
- return GetSceneManager()->IsInInterruptableScenes( pActor );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void ResumeActorsScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly )
-{
- GetSceneManager()->ResumeActorsScenes( pActor, instancedscenesonly );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void QueueActorsScriptedScenesToResume( CBaseFlex *pActor, bool instancedscenesonly )
-{
- GetSceneManager()->QueueActorsScenesToResume( pActor, instancedscenesonly );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
-{
- return GetSceneManager()->IsRunningScriptedScene( pActor, bIgnoreInstancedScenes );
-}
-
-bool IsRunningScriptedSceneAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
-{
- return GetSceneManager()->IsRunningScriptedSceneAndNotPaused( pActor, bIgnoreInstancedScenes );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
-{
- return GetSceneManager()->IsRunningScriptedSceneWithSpeech( pActor, bIgnoreInstancedScenes );
-}
-
-bool IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
-{
- return GetSceneManager()->IsRunningScriptedSceneWithSpeechAndNotPaused( pActor, bIgnoreInstancedScenes );
-}
-
-
-//===========================================================================================================
-// SCENE LIST MANAGER
-//===========================================================================================================
-LINK_ENTITY_TO_CLASS( logic_scene_list_manager, CSceneListManager );
-
-BEGIN_DATADESC( CSceneListManager )
- DEFINE_UTLVECTOR( m_hListManagers, FIELD_EHANDLE ),
-
- // Keys
- DEFINE_KEYFIELD( m_iszScenes[0], FIELD_STRING, "scene0" ),
- DEFINE_KEYFIELD( m_iszScenes[1], FIELD_STRING, "scene1" ),
- DEFINE_KEYFIELD( m_iszScenes[2], FIELD_STRING, "scene2" ),
- DEFINE_KEYFIELD( m_iszScenes[3], FIELD_STRING, "scene3" ),
- DEFINE_KEYFIELD( m_iszScenes[4], FIELD_STRING, "scene4" ),
- DEFINE_KEYFIELD( m_iszScenes[5], FIELD_STRING, "scene5" ),
- DEFINE_KEYFIELD( m_iszScenes[6], FIELD_STRING, "scene6" ),
- DEFINE_KEYFIELD( m_iszScenes[7], FIELD_STRING, "scene7" ),
- DEFINE_KEYFIELD( m_iszScenes[8], FIELD_STRING, "scene8" ),
- DEFINE_KEYFIELD( m_iszScenes[9], FIELD_STRING, "scene9" ),
- DEFINE_KEYFIELD( m_iszScenes[10], FIELD_STRING, "scene10" ),
- DEFINE_KEYFIELD( m_iszScenes[11], FIELD_STRING, "scene11" ),
- DEFINE_KEYFIELD( m_iszScenes[12], FIELD_STRING, "scene12" ),
- DEFINE_KEYFIELD( m_iszScenes[13], FIELD_STRING, "scene13" ),
- DEFINE_KEYFIELD( m_iszScenes[14], FIELD_STRING, "scene14" ),
- DEFINE_KEYFIELD( m_iszScenes[15], FIELD_STRING, "scene15" ),
-
- DEFINE_FIELD( m_hScenes[0], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[1], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[2], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[3], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[4], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[5], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[6], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[7], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[8], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[9], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[10], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[11], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[12], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[13], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[14], FIELD_EHANDLE ),
- DEFINE_FIELD( m_hScenes[15], FIELD_EHANDLE ),
-
- // Inputs
- DEFINE_INPUTFUNC( FIELD_VOID, "Shutdown", InputShutdown ),
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneListManager::Activate( void )
-{
- BaseClass::Activate();
-
- // Hook up scenes, but not after loading a game because they're saved.
- if ( gpGlobals->eLoadType != MapLoad_LoadGame )
- {
- for ( int i = 0; i < SCENE_LIST_MANAGER_MAX_SCENES; i++ )
- {
- if ( m_iszScenes[i] != NULL_STRING )
- {
- m_hScenes[i] = gEntList.FindEntityByName( NULL, STRING(m_iszScenes[i]) );
- if ( m_hScenes[i] )
- {
- CSceneEntity *pScene = dynamic_cast<CSceneEntity*>(m_hScenes[i].Get());
- if ( pScene )
- {
- pScene->AddListManager( this );
- }
- else
- {
- CSceneListManager *pList = dynamic_cast<CSceneListManager*>(m_hScenes[i].Get());
- if ( pList )
- {
- pList->AddListManager( this );
- }
- else
- {
- Warning( "%s(%s) found an entity that wasn't a logic_choreographed_scene or logic_scene_list_manager in slot %d, named %s\n", GetDebugName(), GetClassname(), i, STRING(m_iszScenes[i]) );
- m_hScenes[i] = NULL;
- }
- }
- }
- else
- {
- Warning( "%s(%s) could not find scene %d, named %s\n", GetDebugName(), GetClassname(), i, STRING(m_iszScenes[i]) );
- }
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: A scene or manager in our list has started playing.
-// Remove all scenes earlier in the list.
-//-----------------------------------------------------------------------------
-void CSceneListManager::SceneStarted( CBaseEntity *pSceneOrManager )
-{
- // Move backwards and call remove on all scenes / managers earlier in the list to the fired one
- bool bFoundStart = false;
- for ( int i = SCENE_LIST_MANAGER_MAX_SCENES-1; i >= 0; i-- )
- {
- if ( !m_hScenes[i] )
- continue;
-
- if ( bFoundStart )
- {
- RemoveScene( i );
- }
- else if ( m_hScenes[i] == pSceneOrManager )
- {
- bFoundStart = true;
- }
- }
-
- // Tell any managers we're within that we've started a scene
- if ( bFoundStart )
- {
- int c = m_hListManagers.Count();
- for ( int i = 0; i < c; i++ )
- {
- if ( m_hListManagers[i] )
- {
- m_hListManagers[i]->SceneStarted( this );
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneListManager::AddListManager( CSceneListManager *pManager )
-{
- CHandle< CSceneListManager > h;
- h = pManager;
- // Only add it once
- if ( m_hListManagers.Find( h ) == m_hListManagers.InvalidIndex() )
- {
- m_hListManagers.AddToTail( h );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Shut down all scenes, and then remove this entity
-//-----------------------------------------------------------------------------
-void CSceneListManager::InputShutdown( inputdata_t &inputdata )
-{
- ShutdownList();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneListManager::ShutdownList( void )
-{
- for ( int i = 0; i < SCENE_LIST_MANAGER_MAX_SCENES; i++ )
- {
- if ( m_hScenes[i] )
- {
- RemoveScene(i);
- }
- }
-
- UTIL_Remove( this );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CSceneListManager::RemoveScene( int iIndex )
-{
- CSceneEntity *pScene = dynamic_cast<CSceneEntity*>(m_hScenes[iIndex].Get());
- if ( pScene )
- {
- // Remove the scene
- UTIL_Remove( pScene );
- return;
- }
-
- // Tell the list manager to shut down all scenes
- CSceneListManager *pList = dynamic_cast<CSceneListManager*>(m_hScenes[iIndex].Get());
- if ( pList )
- {
- pList->ShutdownList();
- }
-}
-
-void ReloadSceneFromDisk( CBaseEntity *ent )
-{
- CSceneEntity *scene = dynamic_cast< CSceneEntity * >( ent );
- if ( !scene )
- return;
-
- Assert( 0 );
-}
-
-// Purpose:
-// Input : *ent -
-// Output : char const
-//-----------------------------------------------------------------------------
-char const *GetSceneFilename( CBaseEntity *ent )
-{
- CSceneEntity *scene = dynamic_cast< CSceneEntity * >( ent );
- if ( !scene )
- return "";
-
- return STRING( scene->m_iszSceneFile );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Return a list of the last 5 lines of speech from NPCs for bug reports
-// Input :
-// Output : speech - last 5 sound files played as speech
-// returns the number of sounds in the returned list
-//-----------------------------------------------------------------------------
-
-int GetRecentNPCSpeech( recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ] )
-{
- int i;
- int num;
- int index;
-
- // clear out the output list
- for( i = 0; i < SPEECH_LIST_MAX_SOUNDS; i++ )
- {
- speech[ i ].time = 0.0f;
- speech[ i ].name[ 0 ] = 0;
- speech[ i ].sceneName[ 0 ] = 0;
- }
-
- // copy the sound names into the list in order they were played
- num = 0;
- index = speechListIndex;
- for( i = 0; i < SPEECH_LIST_MAX_SOUNDS; i++ )
- {
- if ( speechListSounds[ index ].name[ 0 ] )
- {
- // only copy names that are not zero length
- speech[ num ] = speechListSounds[ index ];
- num++;
- }
-
- index++;
- if ( index >= SPEECH_LIST_MAX_SOUNDS )
- {
- index = 0;
- }
- }
-
- return num;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Displays a list of the last 5 lines of speech from NPCs
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-
-static void ListRecentNPCSpeech( void )
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ];
- int num;
- int i;
-
- // get any sounds that were spoken by NPCs recently
- num = GetRecentNPCSpeech( speech );
- Msg( "Recent NPC speech:\n" );
- for( i = 0; i < num; i++ )
- {
- Msg( " time: %6.3f sound name: %s scene: %s\n", speech[ i ].time, speech[ i ].name, speech[ i ].sceneName );
- }
- Msg( "Current time: %6.3f\n", gpGlobals->curtime );
-}
-
-static ConCommand ListRecentNPCSpeechCmd( "listRecentNPCSpeech", ListRecentNPCSpeech, "Displays a list of the last 5 lines of speech from NPCs.", FCVAR_DONTRECORD|FCVAR_GAMEDLL );
-
-CON_COMMAND( scene_flush, "Flush all .vcds from the cache and reload from disk." )
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- Msg( "Reloading\n" );
- scenefilecache->Reload();
- Msg( " done\n" );
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include <stdarg.h>
+#include "baseflex.h"
+#include "entitylist.h"
+#include "choreoevent.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "choreoscene.h"
+#include "studio.h"
+#include "networkstringtable_gamedll.h"
+#include "ai_basenpc.h"
+#include "engine/IEngineSound.h"
+#include "ai_navigator.h"
+#include "saverestore_utlvector.h"
+#include "ai_baseactor.h"
+#include "AI_Criteria.h"
+#include "tier1/strtools.h"
+#include "checksum_crc.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "utlbuffer.h"
+#include "tier0/icommandline.h"
+#include "sceneentity.h"
+#include "datacache/idatacache.h"
+#include "dt_utlvector_send.h"
+#include "ichoreoeventcallback.h"
+#include "scenefilecache/ISceneFileCache.h"
+#include "SceneCache.h"
+#include "scripted.h"
+#include "env_debughistory.h"
+
+#ifdef HL2_EPISODIC
+#include "npc_alyx_episodic.h"
+#endif // HL2_EPISODIC
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern ISoundEmitterSystemBase *soundemitterbase;
+extern ISceneFileCache *scenefilecache;
+
+class CSceneEntity;
+class CBaseFlex;
+
+// VCDS are loaded from their compiled/binary format (much faster)
+// Requies vcds be saved as compiled assets
+//#define COMPILED_VCDS 1
+
+static ConVar scene_forcecombined( "scene_forcecombined", "0", 0, "When playing back, force use of combined .wav files even in english." );
+static ConVar scene_maxcaptionradius( "scene_maxcaptionradius", "1200", 0, "Only show closed captions if recipient is within this many units of speaking actor (0==disabled)." );
+
+// Assume sound system is 100 msec lagged (only used if we can't find snd_mixahead cvar!)
+#define SOUND_SYSTEM_LATENCY_DEFAULT ( 0.1f )
+
+// Think every 50 msec (FIXME: Try 10hz?)
+#define SCENE_THINK_INTERVAL 0.001 // FIXME: make scene's think in concert with their npc's
+
+#define FINDNAMEDENTITY_MAX_ENTITIES 32 // max number of entities to be considered for random entity selection in FindNamedEntity
+
+// List of the last 5 lines of speech from NPCs for bug reports
+static recentNPCSpeech_t speechListSounds[ SPEECH_LIST_MAX_SOUNDS ] = { { 0, "", "" }, { 0, "", "" }, { 0, "", "" }, { 0, "", "" }, { 0, "", "" } };
+static int speechListIndex = 0;
+
+// Only allow scenes to change their pitch within a range of values
+#define SCENE_MIN_PITCH 0.25f
+#define SCENE_MAX_PITCH 2.5f
+
+//===========================================================================================================
+// SCENE LIST MANAGER
+//===========================================================================================================
+#define SCENE_LIST_MANAGER_MAX_SCENES 16
+
+//-----------------------------------------------------------------------------
+// Purpose: Entity that manages a list of scenes
+//-----------------------------------------------------------------------------
+class CSceneListManager : public CLogicalEntity
+{
+ DECLARE_CLASS( CSceneListManager, CLogicalEntity );
+public:
+ DECLARE_DATADESC();
+
+ virtual void Activate( void );
+
+ void ShutdownList( void );
+ void SceneStarted( CBaseEntity *pSceneOrManager );
+ void AddListManager( CSceneListManager *pManager );
+ void RemoveScene( int iIndex );
+
+ // Inputs
+ void InputShutdown( inputdata_t &inputdata );
+
+private:
+ CUtlVector< CHandle< CSceneListManager > > m_hListManagers;
+ string_t m_iszScenes[SCENE_LIST_MANAGER_MAX_SCENES];
+ EHANDLE m_hScenes[SCENE_LIST_MANAGER_MAX_SCENES];
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: This class exists solely to call think on all scene entities in a deterministic order
+//-----------------------------------------------------------------------------
+class CSceneManager : public CBaseEntity
+{
+ DECLARE_CLASS( CSceneManager, CBaseEntity );
+ DECLARE_DATADESC();
+
+public:
+ virtual void Spawn()
+ {
+ BaseClass::Spawn();
+ SetNextThink( gpGlobals->curtime );
+ }
+
+ virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_DONT_SAVE; }
+
+ virtual void Think();
+
+ void ClearAllScenes();
+
+ void AddSceneEntity( CSceneEntity *scene );
+ void RemoveSceneEntity( CSceneEntity *scene );
+
+ void QueueRestoredSound( CBaseFlex *actor, char const *soundname, soundlevel_t soundlevel, float time_in_past );
+
+ void OnClientActive( CBasePlayer *player );
+
+ void RemoveActorFromScenes( CBaseFlex *pActor, bool bInstancedOnly, bool bNonIdleOnly, const char *pszThisSceneOnly );
+ void RemoveScenesInvolvingActor( CBaseFlex *pActor );
+ void PauseActorsScenes( CBaseFlex *pActor, bool bInstancedOnly );
+ bool IsInInterruptableScenes( CBaseFlex *pActor );
+ void ResumeActorsScenes( CBaseFlex *pActor, bool bInstancedOnly );
+ void QueueActorsScenesToResume( CBaseFlex *pActor, bool bInstancedOnly );
+ bool IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes );
+ bool IsRunningScriptedSceneAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes );
+ bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes );
+ bool IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes );
+
+
+private:
+
+ struct CRestoreSceneSound
+ {
+ CRestoreSceneSound()
+ {
+ actor = NULL;
+ soundname[ 0 ] = NULL;
+ soundlevel = SNDLVL_NORM;
+ time_in_past = 0.0f;
+ }
+
+ CHandle< CBaseFlex > actor;
+ char soundname[ 128 ];
+ soundlevel_t soundlevel;
+ float time_in_past;
+ };
+
+ CUtlVector< CHandle< CSceneEntity > > m_ActiveScenes;
+
+ CUtlVector< CRestoreSceneSound > m_QueuedSceneSounds;
+};
+
+//---------------------------------------------------------
+// Save/Restore
+//---------------------------------------------------------
+BEGIN_DATADESC( CSceneManager )
+
+ DEFINE_UTLVECTOR( m_ActiveScenes, FIELD_EHANDLE ),
+ // DEFINE_FIELD( m_QueuedSceneSounds, CUtlVector < CRestoreSceneSound > ), // Don't save/restore this, it's created and used by OnRestore only
+
+END_DATADESC()
+
+#ifdef DISABLE_DEBUG_HISTORY
+#define LocalScene_Printf Scene_Printf
+#else
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFormat -
+// ... -
+// Output : static void
+//-----------------------------------------------------------------------------
+void LocalScene_Printf( const char *pFormat, ... )
+{
+ va_list marker;
+ char msg[8192];
+
+ va_start(marker, pFormat);
+ Q_vsnprintf(msg, sizeof(msg), pFormat, marker);
+ va_end(marker);
+
+ Scene_Printf( "%s", msg );
+ ADD_DEBUG_HISTORY( HISTORY_SCENE_PRINT, UTIL_VarArgs( "(%0.2f) %s", gpGlobals->curtime, msg ) );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+// **buffer -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CopySceneFileIntoMemory( char const *pFilename, void **pBuffer, int *pSize )
+{
+ size_t bufSize = scenefilecache->GetSceneBufferSize( pFilename );
+ if ( bufSize > 0 )
+ {
+ *pBuffer = new byte[bufSize];
+ *pSize = bufSize;
+ return scenefilecache->GetSceneData( pFilename, (byte *)(*pBuffer), bufSize );
+ }
+
+ *pBuffer = 0;
+ *pSize = 0;
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void FreeSceneFileMemory( void *buffer )
+{
+ delete[] (byte*) buffer;
+}
+
+//-----------------------------------------------------------------------------
+// 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;
+
+//-----------------------------------------------------------------------------
+// Purpose: Singleton scene manager. Created by first placed scene or recreated it it's deleted for some unknown reason
+// Output : CSceneManager
+//-----------------------------------------------------------------------------
+CSceneManager *GetSceneManager()
+{
+ // Create it if it doesn't exist
+ static CHandle< CSceneManager > s_SceneManager;
+ if ( s_SceneManager == NULL )
+ {
+ s_SceneManager = ( CSceneManager * )CreateEntityByName( "scene_manager" );
+ Assert( s_SceneManager );
+ if ( s_SceneManager )
+ {
+ s_SceneManager->Spawn();
+ }
+ }
+
+ Assert( s_SceneManager );
+ return s_SceneManager;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *player -
+//-----------------------------------------------------------------------------
+void SceneManager_ClientActive( CBasePlayer *player )
+{
+ Assert( GetSceneManager() );
+
+ if ( GetSceneManager() )
+ {
+ GetSceneManager()->OnClientActive( player );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: FIXME, need to deal with save/restore
+//-----------------------------------------------------------------------------
+class CSceneEntity : public CPointEntity, public IChoreoEventCallback
+{
+ friend class CInstancedSceneEntity;
+public:
+
+ enum
+ {
+ SCENE_ACTION_UNKNOWN = 0,
+ SCENE_ACTION_CANCEL,
+ SCENE_ACTION_RESUME,
+ };
+
+ enum
+ {
+ SCENE_BUSYACTOR_DEFAULT = 0,
+ SCENE_BUSYACTOR_WAIT,
+ SCENE_BUSYACTOR_INTERRUPT,
+ SCENE_BUSYACTOR_INTERRUPT_CANCEL,
+ };
+
+
+
+
+ DECLARE_CLASS( CSceneEntity, CPointEntity );
+ DECLARE_SERVERCLASS();
+
+ CSceneEntity( void );
+ ~CSceneEntity( void );
+
+ // From IChoreoEventCallback
+ virtual void StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
+ virtual void EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
+ virtual void ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
+ virtual bool CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event );
+
+
+ virtual int UpdateTransmitState();
+ virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
+
+ void SetRecipientFilter( IRecipientFilter *filter );
+
+ virtual void Activate();
+
+ virtual void Precache( void );
+ virtual void Spawn( void );
+ virtual void UpdateOnRemove( void );
+
+ virtual void OnRestore();
+ virtual void OnLoaded();
+
+ DECLARE_DATADESC();
+
+ virtual void OnSceneFinished( bool canceled, bool fireoutput );
+
+ virtual void DoThink( float frametime );
+ virtual void PauseThink( void );
+
+ bool IsPlayingBack() const { return m_bIsPlayingBack; }
+ bool IsPaused() const { return m_bPaused; }
+ bool IsMultiplayer() const { return m_bMultiplayer; }
+
+ bool IsInterruptable();
+ virtual void ClearInterrupt();
+ virtual void CheckInterruptCompletion();
+
+ virtual bool InterruptThisScene( CSceneEntity *otherScene );
+ void RequestCompletionNotification( CSceneEntity *otherScene );
+
+ virtual void NotifyOfCompletion( CSceneEntity *interruptor );
+
+ void AddListManager( CSceneListManager *pManager );
+
+ void ClearActivatorTargets( void );
+
+ void SetBreakOnNonIdle( bool bBreakOnNonIdle ) { m_bBreakOnNonIdle = bBreakOnNonIdle; }
+ bool ShouldBreakOnNonIdle( void ) { return m_bBreakOnNonIdle; }
+
+ // Inputs
+ void InputStartPlayback( inputdata_t &inputdata );
+ void InputPausePlayback( inputdata_t &inputdata );
+ void InputResumePlayback( inputdata_t &inputdata );
+ void InputCancelPlayback( inputdata_t &inputdata );
+ void InputCancelAtNextInterrupt( inputdata_t &inputdata );
+ void InputPitchShiftPlayback( inputdata_t &inputdata );
+ void InputTriggerEvent( inputdata_t &inputdata );
+
+ // If the scene is playing, finds an actor in the scene who can respond to the specified concept token
+ void InputInterjectResponse( inputdata_t &inputdata );
+
+ // If this scene is waiting on an actor, give up and quit trying.
+ void InputStopWaitingForActor( inputdata_t &inputdata );
+
+ virtual void StartPlayback( void );
+ virtual void PausePlayback( void );
+ virtual void ResumePlayback( void );
+ virtual void CancelPlayback( void );
+ virtual void PitchShiftPlayback( float fPitch );
+ virtual void QueueResumePlayback( void );
+
+ bool ValidScene() const;
+
+ // Scene load/unload
+ static CChoreoScene *LoadScene( const char *filename, IChoreoEventCallback *pCallback );
+
+ void UnloadScene( void );
+
+ struct SpeakEventSound_t
+ {
+ CUtlSymbol m_Symbol;
+ float m_flStartTime;
+ };
+
+ static bool SpeakEventSoundLessFunc( const SpeakEventSound_t& lhs, const SpeakEventSound_t& rhs );
+
+ bool GetSoundNameForPlayer( CChoreoEvent *event, CBasePlayer *player, char *buf, size_t buflen, CBaseEntity *pActor );
+
+ void BuildSortedSpeakEventSoundsPrefetchList(
+ CChoreoScene *scene,
+ CUtlSymbolTable& table,
+ CUtlRBTree< SpeakEventSound_t >& soundnames,
+ float timeOffset );
+ void PrefetchSpeakEventSounds( CUtlSymbolTable& table, CUtlRBTree< SpeakEventSound_t >& soundnames );
+
+ // Event handlers
+ virtual void DispatchStartExpression( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchEndExpression( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartFlexAnimation( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchEndFlexAnimation( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartGesture( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchEndGesture( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartLookAt( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event );
+ virtual void DispatchEndLookAt( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartMoveTo( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event );
+ virtual void DispatchEndMoveTo( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartSpeak( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event, soundlevel_t iSoundlevel );
+ virtual void DispatchEndSpeak( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartFace( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event );
+ virtual void DispatchEndFace( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartSubScene( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchStartInterrupt( CChoreoScene *scene, CChoreoEvent *event );
+ virtual void DispatchEndInterrupt( CChoreoScene *scene, CChoreoEvent *event );
+ virtual void DispatchStartGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchEndGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+
+ // NPC can play interstitial vcds (such as responding to the player doing something during a scene)
+ virtual void DispatchStartPermitResponses( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+ virtual void DispatchEndPermitResponses( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event );
+
+
+ // Global events
+ virtual void DispatchProcessLoop( CChoreoScene *scene, CChoreoEvent *event );
+ virtual void DispatchPauseScene( CChoreoScene *scene, const char *parameters );
+ virtual void DispatchStopPoint( CChoreoScene *scene, const char *parameters );
+
+ virtual float EstimateLength( void );
+
+ void CancelIfSceneInvolvesActor( CBaseEntity *pActor );
+ bool InvolvesActor( CBaseEntity *pActor ); // NOTE: returns false if scene hasn't loaded yet
+
+ void GenerateSoundScene( CBaseFlex *pActor, const char *soundname );
+
+ virtual float GetPostSpeakDelay() { return 1.0; }
+
+ bool HasUnplayedSpeech( void );
+ bool HasFlexAnimation( void );
+
+ void SetCurrentTime( float t, bool forceClientSync );
+
+ void InputScriptPlayerDeath( inputdata_t &inputdata );
+
+// Data
+public:
+ string_t m_iszSceneFile;
+
+ string_t m_iszResumeSceneFile;
+ EHANDLE m_hWaitingForThisResumeScene;
+ bool m_bWaitingForResumeScene;
+
+ string_t m_iszTarget1;
+ string_t m_iszTarget2;
+ string_t m_iszTarget3;
+ string_t m_iszTarget4;
+ string_t m_iszTarget5;
+ string_t m_iszTarget6;
+ string_t m_iszTarget7;
+ string_t m_iszTarget8;
+
+ EHANDLE m_hTarget1;
+ EHANDLE m_hTarget2;
+ EHANDLE m_hTarget3;
+ EHANDLE m_hTarget4;
+ EHANDLE m_hTarget5;
+ EHANDLE m_hTarget6;
+ EHANDLE m_hTarget7;
+ EHANDLE m_hTarget8;
+
+ CNetworkVar( bool, m_bIsPlayingBack );
+ CNetworkVar( bool, m_bPaused );
+ CNetworkVar( bool, m_bMultiplayer );
+ CNetworkVar( float, m_flForceClientTime );
+
+ float m_flCurrentTime;
+ float m_flFrameTime;
+ bool m_bCancelAtNextInterrupt;
+
+ float m_fPitch;
+
+ bool m_bAutomated;
+ int m_nAutomatedAction;
+ float m_flAutomationDelay;
+ float m_flAutomationTime;
+
+ // A pause from an input requires another input to unpause (it's a hard pause)
+ bool m_bPausedViaInput;
+
+ // Waiting for the actor to be able to speak.
+ bool m_bWaitingForActor;
+
+ // Waiting for a point at which we can interrupt our actors
+ bool m_bWaitingForInterrupt;
+ bool m_bInterruptedActorsScenes;
+
+ bool m_bBreakOnNonIdle;
+
+public:
+ virtual CBaseFlex *FindNamedActor( int index );
+ virtual CBaseFlex *FindNamedActor( CChoreoActor *pChoreoActor );
+ virtual CBaseFlex *FindNamedActor( const char *name );
+ virtual CBaseEntity *FindNamedEntity( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false );
+ CBaseEntity *FindNamedTarget( string_t iszTarget, bool bBaseFlexOnly = false );
+ virtual CBaseEntity *FindNamedEntityClosest( const char *name, CBaseEntity *pActor = NULL, bool bBaseFlexOnly = false, bool bUseClear = false, const char *pszSecondary = NULL );
+
+private:
+
+ CUtlVector< CHandle< CBaseFlex > > m_hActorList;
+ CUtlVector< CHandle< CBaseEntity > > m_hRemoveActorList;
+
+private:
+
+ inline void SetRestoring( bool bRestoring );
+
+ // Prevent derived classed from using this!
+ virtual void Think( void ) {};
+
+
+ void ClearSceneEvents( CChoreoScene *scene, bool canceled );
+ void ClearSchedules( CChoreoScene *scene );
+
+ float GetSoundSystemLatency( void );
+ void PrecacheScene( CChoreoScene *scene );
+
+ CChoreoScene *GenerateSceneForSound( CBaseFlex *pFlexActor, const char *soundname );
+
+ bool CheckActors();
+
+ void PrefetchAnimBlocks( CChoreoScene *scene );
+
+ bool ShouldNetwork() const;
+ // Set if we tried to async the scene but the FS returned that the data was not loadable
+ bool m_bSceneMissing;
+
+ CChoreoScene *m_pScene;
+ CNetworkVar( int, m_nSceneStringIndex );
+
+ static const ConVar *m_pcvSndMixahead;
+
+ COutputEvent m_OnStart;
+ COutputEvent m_OnCompletion;
+ COutputEvent m_OnCanceled;
+ COutputEvent m_OnTrigger1;
+ COutputEvent m_OnTrigger2;
+ COutputEvent m_OnTrigger3;
+ COutputEvent m_OnTrigger4;
+ COutputEvent m_OnTrigger5;
+ COutputEvent m_OnTrigger6;
+ COutputEvent m_OnTrigger7;
+ COutputEvent m_OnTrigger8;
+ COutputEvent m_OnTrigger9;
+ COutputEvent m_OnTrigger10;
+ COutputEvent m_OnTrigger11;
+ COutputEvent m_OnTrigger12;
+ COutputEvent m_OnTrigger13;
+ COutputEvent m_OnTrigger14;
+ COutputEvent m_OnTrigger15;
+ COutputEvent m_OnTrigger16;
+
+ int m_nInterruptCount;
+ bool m_bInterrupted;
+ CHandle< CSceneEntity > m_hInterruptScene;
+
+ bool m_bCompletedEarly;
+
+ bool m_bInterruptSceneFinished;
+ CUtlVector< CHandle< CSceneEntity > > m_hNotifySceneCompletion;
+ CUtlVector< CHandle< CSceneListManager > > m_hListManagers;
+
+ bool m_bRestoring;
+
+ bool m_bGenerated;
+ string_t m_iszSoundName;
+ CHandle< CBaseFlex > m_hActor;
+
+ EHANDLE m_hActivator;
+
+ int m_BusyActor;
+
+ int m_iPlayerDeathBehavior;
+
+ CRecipientFilter *m_pRecipientFilter;
+
+public:
+ void SetBackground( bool bIsBackground );
+ bool IsBackground( void );
+};
+
+LINK_ENTITY_TO_CLASS( logic_choreographed_scene, CSceneEntity );
+LINK_ENTITY_TO_CLASS( scripted_scene, CSceneEntity );
+
+IMPLEMENT_SERVERCLASS_ST_NOBASE( CSceneEntity, DT_SceneEntity )
+ SendPropInt(SENDINFO(m_nSceneStringIndex),MAX_CHOREO_SCENES_STRING_BITS,SPROP_UNSIGNED),
+ SendPropBool(SENDINFO(m_bIsPlayingBack)),
+ SendPropBool(SENDINFO(m_bPaused)),
+ SendPropBool(SENDINFO(m_bMultiplayer)),
+ SendPropFloat(SENDINFO(m_flForceClientTime)),
+ SendPropUtlVector(
+ SENDINFO_UTLVECTOR( m_hActorList ),
+ MAX_ACTORS_IN_SCENE, // max elements
+ SendPropEHandle( NULL, 0 ) ),
+END_SEND_TABLE()
+
+BEGIN_DATADESC( CSceneEntity )
+
+ // Keys
+ DEFINE_KEYFIELD( m_iszSceneFile, FIELD_STRING, "SceneFile" ),
+ DEFINE_KEYFIELD( m_iszResumeSceneFile, FIELD_STRING, "ResumeSceneFile" ),
+ DEFINE_FIELD( m_hWaitingForThisResumeScene, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bWaitingForResumeScene, FIELD_BOOLEAN ),
+
+ DEFINE_KEYFIELD( m_iszTarget1, FIELD_STRING, "target1" ),
+ DEFINE_KEYFIELD( m_iszTarget2, FIELD_STRING, "target2" ),
+ DEFINE_KEYFIELD( m_iszTarget3, FIELD_STRING, "target3" ),
+ DEFINE_KEYFIELD( m_iszTarget4, FIELD_STRING, "target4" ),
+ DEFINE_KEYFIELD( m_iszTarget5, FIELD_STRING, "target5" ),
+ DEFINE_KEYFIELD( m_iszTarget6, FIELD_STRING, "target6" ),
+ DEFINE_KEYFIELD( m_iszTarget7, FIELD_STRING, "target7" ),
+ DEFINE_KEYFIELD( m_iszTarget8, FIELD_STRING, "target8" ),
+
+ DEFINE_KEYFIELD( m_BusyActor, FIELD_INTEGER, "busyactor" ),
+
+ DEFINE_FIELD( m_hTarget1, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hTarget2, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hTarget3, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hTarget4, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hTarget5, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hTarget6, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hTarget7, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hTarget8, FIELD_EHANDLE ),
+
+ DEFINE_FIELD( m_bIsPlayingBack, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bPaused, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flCurrentTime, FIELD_FLOAT ), // relative, not absolute time
+ DEFINE_FIELD( m_flForceClientTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flFrameTime, FIELD_FLOAT ), // last frametime
+ DEFINE_FIELD( m_bCancelAtNextInterrupt, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_fPitch, FIELD_FLOAT ),
+ DEFINE_FIELD( m_bAutomated, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_nAutomatedAction, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flAutomationDelay, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flAutomationTime, FIELD_FLOAT ), // relative, not absolute time
+
+ DEFINE_FIELD( m_bPausedViaInput, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bWaitingForActor, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bWaitingForInterrupt, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bInterruptedActorsScenes, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bBreakOnNonIdle, FIELD_BOOLEAN ),
+
+ DEFINE_UTLVECTOR( m_hActorList, FIELD_EHANDLE ),
+ DEFINE_UTLVECTOR( m_hRemoveActorList, FIELD_EHANDLE ),
+
+ // DEFINE_FIELD( m_pScene, FIELD_XXXX ) // Special processing used for this
+
+ // These are set up in the constructor
+ // DEFINE_FIELD( m_pcvSndMixahead, FIELD_XXXXX ),
+ // DEFINE_FIELD( m_bRestoring, FIELD_BOOLEAN ),
+
+ DEFINE_FIELD( m_nInterruptCount, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bInterrupted, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hInterruptScene, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bCompletedEarly, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bInterruptSceneFinished, FIELD_BOOLEAN ),
+
+ DEFINE_FIELD( m_bGenerated, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_iszSoundName, FIELD_STRING ),
+ DEFINE_FIELD( m_hActor, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
+
+ // DEFINE_FIELD( m_bSceneMissing, FIELD_BOOLEAN ),
+ DEFINE_UTLVECTOR( m_hNotifySceneCompletion, FIELD_EHANDLE ),
+ DEFINE_UTLVECTOR( m_hListManagers, FIELD_EHANDLE ),
+
+ DEFINE_FIELD( m_bMultiplayer, FIELD_BOOLEAN ),
+// DEFINE_FIELD( m_nSceneStringIndex, FIELD_INTEGER ),
+
+ // DEFINE_FIELD( m_pRecipientFilter, IRecipientFilter* ), // Multiplayer only
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_VOID, "Start", InputStartPlayback ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Pause", InputPausePlayback ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Resume", InputResumePlayback ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Cancel", InputCancelPlayback ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "CancelAtNextInterrupt", InputCancelAtNextInterrupt ),
+ DEFINE_INPUTFUNC( FIELD_FLOAT, "PitchShift", InputPitchShiftPlayback ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "InterjectResponse", InputInterjectResponse ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "StopWaitingForActor", InputStopWaitingForActor ),
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "Trigger", InputTriggerEvent ),
+
+ DEFINE_KEYFIELD( m_iPlayerDeathBehavior, FIELD_INTEGER, "onplayerdeath" ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "ScriptPlayerDeath", InputScriptPlayerDeath ),
+
+ // Outputs
+ DEFINE_OUTPUT( m_OnStart, "OnStart"),
+ DEFINE_OUTPUT( m_OnCompletion, "OnCompletion"),
+ DEFINE_OUTPUT( m_OnCanceled, "OnCanceled"),
+ DEFINE_OUTPUT( m_OnTrigger1, "OnTrigger1"),
+ DEFINE_OUTPUT( m_OnTrigger2, "OnTrigger2"),
+ DEFINE_OUTPUT( m_OnTrigger3, "OnTrigger3"),
+ DEFINE_OUTPUT( m_OnTrigger4, "OnTrigger4"),
+ DEFINE_OUTPUT( m_OnTrigger5, "OnTrigger5"),
+ DEFINE_OUTPUT( m_OnTrigger6, "OnTrigger6"),
+ DEFINE_OUTPUT( m_OnTrigger7, "OnTrigger7"),
+ DEFINE_OUTPUT( m_OnTrigger8, "OnTrigger8"),
+ DEFINE_OUTPUT( m_OnTrigger9, "OnTrigger9"),
+ DEFINE_OUTPUT( m_OnTrigger10, "OnTrigger10"),
+ DEFINE_OUTPUT( m_OnTrigger11, "OnTrigger11"),
+ DEFINE_OUTPUT( m_OnTrigger12, "OnTrigger12"),
+ DEFINE_OUTPUT( m_OnTrigger13, "OnTrigger13"),
+ DEFINE_OUTPUT( m_OnTrigger14, "OnTrigger14"),
+ DEFINE_OUTPUT( m_OnTrigger15, "OnTrigger15"),
+ DEFINE_OUTPUT( m_OnTrigger16, "OnTrigger16"),
+END_DATADESC()
+
+const ConVar *CSceneEntity::m_pcvSndMixahead = NULL;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CSceneEntity::CSceneEntity( void )
+{
+ m_bWaitingForActor = false;
+ m_bWaitingForInterrupt = false;
+ m_bInterruptedActorsScenes = false;
+ m_bIsPlayingBack = false;
+ m_bPaused = false;
+ m_bMultiplayer = false;
+ m_fPitch = 1.0f;
+ m_iszSceneFile = NULL_STRING;
+ m_iszResumeSceneFile = NULL_STRING;
+ m_hWaitingForThisResumeScene = NULL;
+ m_bWaitingForResumeScene = false;
+ SetCurrentTime( 0.0f, false );
+ m_bCancelAtNextInterrupt = false;
+
+ m_bAutomated = false;
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationDelay = 0.0f;
+ m_flAutomationTime = 0.0f;
+
+ m_bPausedViaInput = false;
+ ClearInterrupt();
+
+ m_pScene = NULL;
+
+ m_bCompletedEarly = false;
+
+ if ( !m_pcvSndMixahead )
+ m_pcvSndMixahead = cvar->FindVar( "snd_mixahead" );
+
+ m_BusyActor = SCENE_BUSYACTOR_DEFAULT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CSceneEntity::~CSceneEntity( void )
+{
+ delete m_pRecipientFilter;
+ m_pRecipientFilter = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets time such that the client version of the .vcd is also updated, if appropriate
+// Input : t -
+// forceClientSync - forces new timestamp down to client .dll via networking
+//-----------------------------------------------------------------------------
+void CSceneEntity::SetCurrentTime( float t, bool bForceClientSync )
+{
+ m_flCurrentTime = t;
+ if ( gpGlobals->maxClients == 1 || bForceClientSync )
+ {
+ m_flForceClientTime = t;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::UpdateOnRemove( void )
+{
+ UnloadScene();
+ BaseClass::UpdateOnRemove();
+
+ if ( GetSceneManager() )
+ {
+ GetSceneManager()->RemoveSceneEntity( this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *soundname -
+// Output : CChoreoScene
+//-----------------------------------------------------------------------------
+CChoreoScene *CSceneEntity::GenerateSceneForSound( CBaseFlex *pFlexActor, const char *soundname )
+{
+ float duration = CBaseEntity::GetSoundDuration( soundname, pFlexActor ? STRING( pFlexActor->GetModelName() ) : NULL );
+ if( duration <= 0.0f )
+ {
+ Warning( "CSceneEntity::GenerateSceneForSound: Couldn't determine duration of %s\n", soundname );
+ return NULL;
+ }
+
+ CChoreoScene *scene = new CChoreoScene( this );
+ if ( !scene )
+ {
+ Warning( "CSceneEntity::GenerateSceneForSound: Failed to allocated new scene!!!\n" );
+ }
+ else
+ {
+ scene->SetPrintFunc( LocalScene_Printf );
+
+
+ CChoreoActor *actor = scene->AllocActor();
+ CChoreoChannel *channel = scene->AllocChannel();
+ CChoreoEvent *event = scene->AllocEvent();
+
+ Assert( actor );
+ Assert( channel );
+ Assert( event );
+
+ if ( !actor || !channel || !event )
+ {
+ Warning( "CSceneEntity::GenerateSceneForSound: Alloc of actor, channel, or event failed!!!\n" );
+ delete scene;
+ return NULL;
+ }
+
+ // Set us up the actorz
+ actor->SetName( "!self" ); // Could be pFlexActor->GetName()?
+ actor->SetActive( true );
+
+ // Set us up the channelz
+ channel->SetName( STRING( m_iszSceneFile ) );
+ channel->SetActor( actor );
+
+ // Add to actor
+ actor->AddChannel( channel );
+
+ // Set us up the eventz
+ event->SetType( CChoreoEvent::SPEAK );
+ event->SetName( soundname );
+ event->SetParameters( soundname );
+ event->SetStartTime( 0.0f );
+ event->SetUsingRelativeTag( false );
+ event->SetEndTime( duration );
+ event->SnapTimes();
+
+ // Add to channel
+ channel->AddEvent( event );
+
+ // Point back to our owners
+ event->SetChannel( channel );
+ event->SetActor( actor );
+
+ }
+
+ return scene;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::Activate()
+{
+ if ( m_bGenerated && !m_pScene )
+ {
+ m_pScene = GenerateSceneForSound( m_hActor, STRING( m_iszSoundName ) );
+ }
+
+ BaseClass::Activate();
+
+ if ( GetSceneManager() )
+ {
+ GetSceneManager()->AddSceneEntity( this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CSceneEntity::GetSoundSystemLatency( void )
+{
+ if ( m_pcvSndMixahead )
+ {
+ return m_pcvSndMixahead->GetFloat();
+ }
+
+ // Assume 100 msec sound system latency
+ return SOUND_SYSTEM_LATENCY_DEFAULT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+//-----------------------------------------------------------------------------
+void CSceneEntity::PrecacheScene( CChoreoScene *scene )
+{
+ Assert( scene );
+
+ // Iterate events and precache necessary resources
+ for ( int i = 0; i < scene->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ // load any necessary data
+ switch (event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ // Defined in SoundEmitterSystem.cpp
+ // NOTE: The script entries associated with .vcds are forced to preload to avoid
+ // loading hitches during triggering
+ PrecacheScriptSound( event->GetParameters() );
+
+ if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER &&
+ event->GetNumSlaves() > 0 )
+ {
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ if ( event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
+ {
+ PrecacheScriptSound( tok );
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ // Only allow a single level of subscenes for now
+ if ( !scene->IsSubScene() )
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( !subscene )
+ {
+ subscene = LoadScene( event->GetParameters(), this );
+ subscene->SetSubScene( true );
+ event->SetSubScene( subscene );
+
+ // Now precache it's resources, if any
+ PrecacheScene( subscene );
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::Precache( void )
+{
+ if ( m_bGenerated )
+ return;
+
+ if ( m_iszSceneFile == NULL_STRING )
+ return;
+
+ if ( m_iszResumeSceneFile != NULL_STRING )
+ {
+ PrecacheInstancedScene( STRING( m_iszResumeSceneFile ) );
+ }
+
+ PrecacheInstancedScene( STRING( m_iszSceneFile ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pActor -
+// *soundname -
+//-----------------------------------------------------------------------------
+void CSceneEntity::GenerateSoundScene( CBaseFlex *pActor, const char *soundname )
+{
+ m_bGenerated = true;
+ m_iszSoundName = MAKE_STRING( soundname );
+ m_hActor = pActor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSceneEntity::HasUnplayedSpeech( void )
+{
+ if ( m_pScene )
+ return m_pScene->HasUnplayedSpeech();
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSceneEntity::HasFlexAnimation( void )
+{
+ if ( m_pScene )
+ return m_pScene->HasFlexAnimation();
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+
+void CSceneEntity::SetBackground( bool bIsBackground )
+{
+ if ( m_pScene )
+ {
+ m_pScene->SetBackground( bIsBackground );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+
+bool CSceneEntity::IsBackground( void )
+{
+ if ( m_pScene )
+ return m_pScene->IsBackground( );
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::OnRestore()
+{
+ BaseClass::OnRestore();
+
+ // Fix saved games that have their pitch set to zero
+ if ( m_fPitch < SCENE_MIN_PITCH || m_fPitch > SCENE_MAX_PITCH )
+ m_fPitch = 1.0f;
+
+ if ( !m_bIsPlayingBack )
+ return;
+
+ if ( !m_pScene )
+ {
+ m_pScene = LoadScene( STRING( m_iszSceneFile ), this );
+ if ( !m_pScene )
+ {
+ m_bSceneMissing = true;
+ return;
+ }
+
+ OnLoaded();
+
+ if ( ShouldNetwork() )
+ {
+ m_nSceneStringIndex = g_pStringTableClientSideChoreoScenes->AddString( CBaseEntity::IsServer(), STRING( m_iszSceneFile ) );
+ }
+
+ UpdateTransmitState();
+ }
+
+ m_bSceneMissing = false;
+
+ int i;
+ for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ CBaseFlex *pTestActor = FindNamedActor( i );
+ if ( !pTestActor )
+ continue;
+
+ if ( !pTestActor->MyCombatCharacterPointer() )
+ continue;
+
+ // Needed?
+ //if ( !pTestActor->MyCombatCharacterPointer()->IsAlive() )
+ // return;
+
+ pTestActor->StartChoreoScene( m_pScene );
+ }
+
+ float dt = SCENE_THINK_INTERVAL;
+
+ bool paused = m_bPaused;
+
+ m_bPaused = false;
+
+ // roll back slightly so that pause events still trigger
+ m_pScene->ResetSimulation( true, m_flCurrentTime - SCENE_THINK_INTERVAL, m_flCurrentTime );
+ m_pScene->SetTime( m_flCurrentTime - SCENE_THINK_INTERVAL );
+
+ SetCurrentTime( m_flCurrentTime, true );
+
+ // Robin: This causes a miscount of any interrupt events in the scene.
+ // All the variables are saved/restored properly, so we can safely leave them alone.
+ //ClearInterrupt();
+
+ SetRestoring( true );
+
+ DoThink( dt );
+
+ SetRestoring( false );
+
+ if ( paused )
+ {
+ PausePlayback();
+ }
+
+ NetworkProp()->NetworkStateForceUpdate();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CSceneEntity::SetRestoring( bool bRestoring )
+{
+ m_bRestoring = bRestoring;
+ if ( m_pScene )
+ {
+ m_pScene->SetRestoring( bRestoring );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::Spawn( void )
+{
+ Precache();
+}
+
+void CSceneEntity::PauseThink( void )
+{
+ if ( !m_pScene )
+ return;
+
+ // Stay paused if pause occurred from interrupt
+ if ( m_bInterrupted )
+ return;
+
+ // If entity I/O paused the scene, then it'll have to resume/cancel the scene...
+ if ( m_bPausedViaInput )
+ {
+ // If we're waiting for a resume scene to finish, continue when it's done
+ if ( m_bWaitingForResumeScene && !m_hWaitingForThisResumeScene )
+ {
+ // Resume scene has finished, stop waiting for it
+ m_bWaitingForResumeScene = false;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ if ( !m_bAutomated )
+ {
+ // FIXME: Game code should check for AI waiting conditions being met, etc.
+ //
+ //
+ //
+ bool bAllFinished = m_pScene->CheckEventCompletion( );
+
+ if ( bAllFinished )
+ {
+ // Perform action
+ switch ( m_nAutomatedAction )
+ {
+ case SCENE_ACTION_RESUME:
+ ResumePlayback();
+ break;
+ case SCENE_ACTION_CANCEL:
+ LocalScene_Printf( "%s : PauseThink canceling playback\n", STRING( m_iszSceneFile ) );
+ CancelPlayback();
+ break;
+ default:
+ ResumePlayback();
+ break;
+ }
+
+ // Reset
+ m_bAutomated = false;
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationTime = 0.0f;
+ m_flAutomationDelay = 0.0f;
+ m_bPausedViaInput = false;
+ }
+ return;
+ }
+
+ // Otherwise, see if resume/cancel is automatic and act accordingly if enough time
+ // has passed
+ m_flAutomationTime += (gpGlobals->frametime);
+
+ if ( m_flAutomationDelay > 0.0f &&
+ m_flAutomationTime < m_flAutomationDelay )
+ return;
+
+ // Perform action
+ switch ( m_nAutomatedAction )
+ {
+ case SCENE_ACTION_RESUME:
+ LocalScene_Printf( "%s : Automatically resuming playback\n", STRING( m_iszSceneFile ) );
+ ResumePlayback();
+ break;
+ case SCENE_ACTION_CANCEL:
+ LocalScene_Printf( "%s : Automatically canceling playback\n", STRING( m_iszSceneFile ) );
+ CancelPlayback();
+ break;
+ default:
+ LocalScene_Printf( "%s : Unknown action %i, automatically resuming playback\n", STRING( m_iszSceneFile ), m_nAutomatedAction );
+ ResumePlayback();
+ break;
+ }
+
+ // Reset
+ m_bAutomated = false;
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationTime = 0.0f;
+ m_flAutomationDelay = 0.0f;
+ m_bPausedViaInput = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchPauseScene( CChoreoScene *scene, const char *parameters )
+{
+ // Don't pause during restore, since we'll be restoring the pause state already
+ if ( m_bRestoring )
+ return;
+
+ // FIXME: Hook this up to AI, etc. somehow, perhaps poll each actor for conditions using
+ // scene resume condition iterator
+ PausePlayback();
+
+ char token[1024];
+
+ m_bPausedViaInput = false;
+ m_bAutomated = false;
+ m_nAutomatedAction = SCENE_ACTION_UNKNOWN;
+ m_flAutomationDelay = 0.0f;
+ m_flAutomationTime = 0.0f;
+
+ // Check for auto resume/cancel
+ const char *buffer = parameters;
+ buffer = engine->ParseFile( buffer, token, sizeof( token ) );
+ if ( !stricmp( token, "automate" ) )
+ {
+ buffer = engine->ParseFile( buffer, token, sizeof( token ) );
+ if ( !stricmp( token, "Cancel" ) )
+ {
+ m_nAutomatedAction = SCENE_ACTION_CANCEL;
+ }
+ else if ( !stricmp( token, "Resume" ) )
+ {
+ m_nAutomatedAction = SCENE_ACTION_RESUME;
+ }
+
+ if ( m_nAutomatedAction != SCENE_ACTION_UNKNOWN )
+ {
+ buffer = engine->ParseFile( buffer, token, sizeof( token ) );
+ m_flAutomationDelay = (float)atof( token );
+
+ if ( m_flAutomationDelay > 0.0f )
+ {
+ // Success
+ m_bAutomated = true;
+ m_flAutomationTime = 0.0f;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchProcessLoop( CChoreoScene *scene, CChoreoEvent *event )
+{
+ // Don't restore this event since it's implied in the current "state" of the scene timer, etc.
+ if ( m_bRestoring )
+ return;
+
+ Assert( scene );
+ 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: Flag the scene as already "completed"
+// Input : *scene -
+// *parameters -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStopPoint( CChoreoScene *scene, const char *parameters )
+{
+ if ( m_bCompletedEarly )
+ {
+ Assert( 0 );
+ Warning( "Scene '%s' with two stop point events!\n", STRING( m_iszSceneFile ) );
+ return;
+ }
+ // Fire completion trigger early
+ m_bCompletedEarly = true;
+ m_OnCompletion.FireOutput( this, this, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSceneEntity::IsInterruptable()
+{
+ return ( m_nInterruptCount > 0 ) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartInterrupt( CChoreoScene *scene, CChoreoEvent *event )
+{
+ // Don't re-interrupt during restore
+ if ( m_bRestoring )
+ return;
+
+ // If we're supposed to cancel at our next interrupt point, cancel now
+ if ( m_bCancelAtNextInterrupt )
+ {
+ m_bCancelAtNextInterrupt = false;
+ LocalScene_Printf( "%s : cancelled via interrupt\n", STRING( m_iszSceneFile ) );
+ CancelPlayback();
+ return;
+ }
+
+ ++m_nInterruptCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchEndInterrupt( CChoreoScene *scene, CChoreoEvent *event )
+{
+ // Don't re-interrupt during restore
+ if ( m_bRestoring )
+ return;
+
+ --m_nInterruptCount;
+
+ if ( m_nInterruptCount < 0 )
+ {
+ m_nInterruptCount = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartExpression( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchEndExpression( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartFlexAnimation( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchEndFlexAnimation( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartGesture( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ // Ingore null gestures
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ return;
+
+ actor->AddSceneEvent( scene, event);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchEndGesture( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ // Ingore null gestures
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ return;
+
+ actor->RemoveSceneEvent( scene, event, m_bRestoring );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ CBaseEntity *pTarget = FindNamedEntity( event->GetParameters2( ) );
+ actor->AddSceneEvent( scene, event, pTarget );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchEndGeneric( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, m_bRestoring );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *actor2 -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartLookAt( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event, actor2 );
+}
+
+
+void CSceneEntity::DispatchEndLookAt( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, m_bRestoring );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Move to spot/actor
+// FIXME: Need to allow this to take arbitrary amount of time and pause playback
+// while waiting for actor to move into position
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartMoveTo( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event, actor2 );
+}
+
+
+void CSceneEntity::DispatchEndMoveTo( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, m_bRestoring );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *token -
+// listener -
+// soundorigins -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool AttenuateCaption( const char *token, const Vector& listener, CUtlVector< Vector >& soundorigins )
+{
+ if ( scene_maxcaptionradius.GetFloat() <= 0.0f )
+ {
+ return false;
+ }
+
+ int c = soundorigins.Count();
+
+ if ( c <= 0 )
+ {
+ return false;
+ }
+
+ float maxdistSqr = scene_maxcaptionradius.GetFloat() * scene_maxcaptionradius.GetFloat();
+
+ for ( int i = 0; i < c; ++i )
+ {
+ const Vector& org = soundorigins[ i ];
+
+ float distSqr = ( org - listener ).LengthSqr();
+ if ( distSqr <= maxdistSqr )
+ {
+ return false;
+ }
+ }
+
+ // All sound sources too far, don't show caption...
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event - which event
+// player - which recipient
+// buf, buflen: where to put the data
+// Output : Returns true if the sound should be played/prefetched
+//-----------------------------------------------------------------------------
+bool CSceneEntity::GetSoundNameForPlayer( CChoreoEvent *event, CBasePlayer *player, char *buf, size_t buflen, CBaseEntity *pActor )
+{
+ Assert( event );
+ Assert( player );
+ Assert( buf );
+ Assert( buflen > 0 );
+
+ bool ismasterevent = true;
+ char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ bool validtoken = false;
+
+ tok[ 0 ] = 0;
+
+ if ( event->GetCloseCaptionType() == CChoreoEvent::CC_SLAVE ||
+ event->GetCloseCaptionType() == CChoreoEvent::CC_DISABLED )
+ {
+ ismasterevent = false;
+ }
+ else
+ {
+ validtoken = event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) );
+ }
+
+ const char* pchToken = "";
+
+ if ( pActor && pActor->IsPlayer() )
+ {
+ pchToken = dynamic_cast< CBasePlayer* >( pActor )->GetSceneSoundToken();
+ }
+
+ // Copy the sound name
+ CopySoundNameWithModifierToken( buf, event->GetParameters(), buflen, pchToken );
+
+ bool usingEnglish = true;
+ if ( !IsXbox() )
+ {
+ char const *cvarvalue = engine->GetClientConVarValue( player->entindex(), "english" );
+ if ( cvarvalue && *cvarvalue && Q_atoi( cvarvalue ) != 1 )
+ {
+ usingEnglish = false;
+ }
+
+ }
+
+ // This makes it like they are running in another language
+ if ( scene_forcecombined.GetBool() )
+ {
+ usingEnglish = false;
+ }
+
+ if ( usingEnglish )
+ {
+ // English sounds always play
+ return true;
+ }
+
+ if ( ismasterevent )
+ {
+ // Master event sounds always play too (master will be the combined .wav)
+ if ( validtoken )
+ {
+ Q_strncpy( buf, tok, buflen );
+ }
+ return true;
+ }
+
+ // Slave events don't play any sound...
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Playback sound file that contains phonemes
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartSpeak( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event, soundlevel_t iSoundlevel )
+{
+ // Emit sound
+ if ( actor )
+ {
+ CPASAttenuationFilter filter( actor );
+
+ if ( m_pRecipientFilter )
+ {
+ int filterCount = filter.GetRecipientCount();
+ int recipientPlayerCount = m_pRecipientFilter->GetRecipientCount();
+ for ( int i = filterCount-1; i >= 0; --i )
+ {
+ int playerindex = filter.GetRecipientIndex( i );
+
+ bool bFound = false;
+
+ for ( int j = 0; j < recipientPlayerCount; ++j )
+ {
+ if ( m_pRecipientFilter->GetRecipientIndex(j) == playerindex )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if ( !bFound )
+ {
+ filter.RemoveRecipientByPlayerIndex( playerindex );
+ }
+ }
+ }
+
+ float time_in_past = m_flCurrentTime - event->GetStartTime() ;
+
+ float soundtime = gpGlobals->curtime - time_in_past;
+
+ if ( m_bRestoring )
+ {
+ // Need to queue sounds on restore because the player has not yet connected
+ GetSceneManager()->QueueRestoredSound( actor, event->GetParameters(), iSoundlevel, time_in_past );
+
+ return;
+ }
+
+ // Add padding to prevent any other talker talking right after I'm done, because I might
+ // be continuing speaking with another scene.
+ float flDuration = event->GetDuration() - time_in_past;
+
+ CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(actor);
+ if ( pBaseActor )
+ {
+ pBaseActor->NoteSpeaking( flDuration, GetPostSpeakDelay() );
+ }
+ else if ( actor->IsNPC() )
+ {
+ GetSpeechSemaphore( actor->MyNPCPointer() )->Acquire( flDuration + GetPostSpeakDelay(), actor );
+ }
+
+ EmitSound_t es;
+ es.m_nChannel = CHAN_VOICE;
+ es.m_flVolume = 1;
+ es.m_SoundLevel = iSoundlevel;
+ // Only specify exact delay in single player
+ es.m_flSoundTime = ( gpGlobals->maxClients == 1 ) ? soundtime : 0.0f;
+ if ( scene->ShouldIgnorePhonemes() )
+ {
+ es.m_nFlags |= SND_IGNORE_PHONEMES;
+ }
+
+ if ( actor->GetSpecialDSP() != 0 )
+ {
+ es.m_nSpecialDSP = actor->GetSpecialDSP();
+ }
+
+ // No CC since we do it manually
+ // FIXME: This will change
+ es.m_bEmitCloseCaption = false;
+
+ int c = filter.GetRecipientCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ int playerindex = filter.GetRecipientIndex( i );
+ CBasePlayer *player = UTIL_PlayerByIndex( playerindex );
+ if ( !player )
+ continue;
+
+ CSingleUserRecipientFilter filter2( player );
+
+ char soundname[ 512 ];
+ if ( !GetSoundNameForPlayer( event, player, soundname, sizeof( soundname ), actor ) )
+ {
+ continue;
+ }
+
+ es.m_pSoundName = soundname;
+
+ // keep track of the last few sounds played for bug reports
+ speechListSounds[ speechListIndex ].time = gpGlobals->curtime;
+ Q_strncpy( speechListSounds[ speechListIndex ].name, soundname, sizeof( speechListSounds[ 0 ].name ) );
+ Q_strncpy( speechListSounds[ speechListIndex ].sceneName, ( scene ) ? scene->GetFilename() : "", sizeof( speechListSounds[ 0 ].sceneName ) );
+
+ speechListIndex++;
+ if ( speechListIndex >= SPEECH_LIST_MAX_SOUNDS )
+ {
+ speechListIndex = 0;
+ }
+
+ // Warning( "Speak %s\n", soundname );
+
+ if ( m_fPitch != 1.0f )
+ {
+ if ( es.m_nPitch )
+ es.m_nPitch = static_cast<float>( es.m_nPitch ) * m_fPitch;
+ else
+ es.m_nPitch = 100.0f * m_fPitch;
+
+ es.m_nFlags |= SND_CHANGE_PITCH;
+ }
+
+ EmitSound( filter2, actor->entindex(), es );
+ actor->AddSceneEvent( scene, event );
+ }
+
+ // 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 )
+ {
+ char lowercase[ 256 ];
+ Q_strncpy( lowercase, tok, sizeof( lowercase ) );
+ Q_strlower( lowercase );
+
+ // Remove any players who don't want close captions
+ CBaseEntity::RemoveRecipientsIfNotCloseCaptioning( filter );
+
+ // Certain events are marked "don't attenuate", (breencast), skip those here
+ if ( !event->IsSuppressingCaptionAttenuation() &&
+ ( filter.GetRecipientCount() > 0 ) )
+ {
+ int c = filter.GetRecipientCount();
+ for ( int i = c - 1 ; i >= 0; --i )
+ {
+ CBasePlayer *player = UTIL_PlayerByIndex( filter.GetRecipientIndex( i ) );
+ if ( !player )
+ continue;
+
+ Vector playerOrigin = player->GetAbsOrigin();
+
+ if ( AttenuateCaption( lowercase, playerOrigin, es.m_UtlVecSoundOrigin ) )
+ {
+ // If the player has a view entity, measure the distance to that
+ if ( !player->GetViewEntity() || AttenuateCaption( lowercase, player->GetViewEntity()->GetAbsOrigin(), es.m_UtlVecSoundOrigin ) )
+ {
+ filter.RemoveRecipient( player );
+ }
+ }
+ }
+ }
+
+ // Anyone left?
+ if ( filter.GetRecipientCount() > 0 )
+ {
+ float endtime = event->GetLastSlaveEndTime();
+ float durationShort = event->GetDuration();
+ float durationLong = endtime - event->GetStartTime();
+
+ float duration = MAX( durationShort, durationLong );
+
+
+ byte byteflags = CLOSE_CAPTION_WARNIFMISSING; // warnifmissing
+ /*
+ // Never for .vcds...
+ if ( fromplayer )
+ {
+ byteflags |= CLOSE_CAPTION_FROMPLAYER;
+ }
+ */
+ char const *pszActorModel = STRING( actor->GetModelName() );
+ gender_t gender = soundemitterbase->GetActorGender( pszActorModel );
+
+ if ( gender == GENDER_MALE )
+ {
+ byteflags |= CLOSE_CAPTION_GENDER_MALE;
+ }
+ else if ( gender == GENDER_FEMALE )
+ {
+ byteflags |= CLOSE_CAPTION_GENDER_FEMALE;
+ }
+
+ // Send caption and duration hint down to client
+ UserMessageBegin( filter, "CloseCaption" );
+ WRITE_STRING( lowercase );
+ WRITE_SHORT( MIN( 255, (int)( duration * 10.0f ) ) );
+ WRITE_BYTE( byteflags ); // warn on missing
+ MessageEnd();
+ }
+ }
+ }
+ }
+}
+
+void CSceneEntity::DispatchEndSpeak( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, m_bRestoring );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *actor2 -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartFace( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event, actor2 );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *actor2 -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchEndFace( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, m_bRestoring );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->AddSceneEvent( scene, event );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->RemoveSceneEvent( scene, event, m_bRestoring );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: NPC can play interstitial vcds (such as responding to the player doing something during a scene)
+// Input : *scene -
+// *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartPermitResponses( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->SetPermitResponse( gpGlobals->curtime + event->GetDuration() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+// *actor -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchEndPermitResponses( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+{
+ actor->SetPermitResponse( 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CSceneEntity::EstimateLength( void )
+{
+ if ( !m_pScene )
+ {
+ return GetSceneDuration( STRING( m_iszSceneFile ) );
+ }
+ return m_pScene->FindStopTime();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// NOTE: returns false if scene hasn't loaded yet
+//-----------------------------------------------------------------------------
+void CSceneEntity::CancelIfSceneInvolvesActor( CBaseEntity *pActor )
+{
+ if ( InvolvesActor( pActor ) )
+ {
+ LocalScene_Printf( "%s : cancelled for '%s'\n", STRING( m_iszSceneFile ), pActor->GetDebugName() );
+ CancelPlayback();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// NOTE: returns false if scene hasn't loaded yet
+//-----------------------------------------------------------------------------
+bool CSceneEntity::InvolvesActor( CBaseEntity *pActor )
+{
+ if ( !m_pScene )
+ return false;
+
+ int i;
+ for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ CBaseFlex *pTestActor = FindNamedActor( i );
+ if ( !pTestActor )
+ continue;
+
+ if ( pTestActor == pActor )
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::DoThink( float frametime )
+{
+ CheckInterruptCompletion();
+
+ if ( m_bWaitingForActor || m_bWaitingForInterrupt )
+ {
+ // Try to start playback.
+ StartPlayback();
+ }
+
+ if ( !m_pScene )
+ return;
+
+ if ( !m_bIsPlayingBack )
+ return;
+
+ // catch bad pitch shifting from old save games
+ Assert( m_fPitch >= SCENE_MIN_PITCH && m_fPitch <= SCENE_MAX_PITCH );
+ m_fPitch = clamp( m_fPitch, SCENE_MIN_PITCH, SCENE_MAX_PITCH );
+
+ if ( m_bPaused )
+ {
+ PauseThink();
+ return;
+ }
+
+ // Msg("%.2f %s\n", gpGlobals->curtime, STRING( m_iszSceneFile ) );
+
+ //Msg( "SV: %d, %f for %s\n", gpGlobals->tickcount, m_flCurrentTime, m_pScene->GetFilename() );
+
+ m_flFrameTime = frametime;
+
+ m_pScene->SetSoundFileStartupLatency( GetSoundSystemLatency() );
+
+ // Tell scene to go
+ m_pScene->Think( m_flCurrentTime );
+
+ // Did we get to the end
+ if ( !m_bPaused )
+ {
+ // Drive simulation time for scene
+ SetCurrentTime( m_flCurrentTime + m_flFrameTime * m_fPitch, false );
+
+ if ( m_pScene->SimulationFinished() )
+ {
+ OnSceneFinished( false, true );
+
+ // Stop them from doing anything special
+ ClearSchedules( m_pScene );
+ }
+ }
+ else
+ {
+ // Drive simulation time for scene
+ SetCurrentTime( m_pScene->GetTime(), true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handlers
+//-----------------------------------------------------------------------------
+void CSceneEntity::InputStartPlayback( inputdata_t &inputdata )
+{
+ // Already playing, ignore
+ if ( m_bIsPlayingBack )
+ return;
+
+ // Already waiting on someone.
+ if ( m_bWaitingForActor || m_bWaitingForInterrupt )
+ return;
+
+ ClearActivatorTargets();
+ m_hActivator = inputdata.pActivator;
+ StartPlayback();
+}
+
+void CSceneEntity::InputPausePlayback( inputdata_t &inputdata )
+{
+ PausePlayback();
+ m_bPausedViaInput = true;
+}
+
+void CSceneEntity::InputResumePlayback( inputdata_t &inputdata )
+{
+ ResumePlayback();
+}
+
+void CSceneEntity::InputCancelPlayback( inputdata_t &inputdata )
+{
+ LocalScene_Printf( "%s : cancelled via input\n", STRING( m_iszSceneFile ) );
+ CancelPlayback();
+}
+
+void CSceneEntity::InputScriptPlayerDeath( inputdata_t &inputdata )
+{
+ if ( m_iPlayerDeathBehavior == SCRIPT_CANCEL )
+ {
+ LocalScene_Printf( "%s : cancelled via player death\n", STRING( m_iszSceneFile ) );
+ CancelPlayback();
+ }
+}
+
+
+void CSceneEntity::InputCancelAtNextInterrupt( inputdata_t &inputdata )
+{
+ // If we're currently in an interruptable point, interrupt immediately
+ if ( IsInterruptable() )
+ {
+ LocalScene_Printf( "%s : cancelled via input at interrupt point\n", STRING( m_iszSceneFile ) );
+ CancelPlayback();
+ return;
+ }
+
+ // Otherwise, cancel when we next hit an interrupt point
+ m_bCancelAtNextInterrupt = true;
+}
+
+void CSceneEntity::InputPitchShiftPlayback( inputdata_t &inputdata )
+{
+ PitchShiftPlayback( inputdata.value.Float() );
+}
+
+void CSceneEntity::InputTriggerEvent( inputdata_t &inputdata )
+{
+ CBaseEntity *pActivator = this; // at some point, find this from the inputdata
+ switch ( inputdata.value.Int() )
+ {
+ case 1:
+ m_OnTrigger1.FireOutput( pActivator, this, 0 );
+ break;
+ case 2:
+ m_OnTrigger2.FireOutput( pActivator, this, 0 );
+ break;
+ case 3:
+ m_OnTrigger3.FireOutput( pActivator, this, 0 );
+ break;
+ case 4:
+ m_OnTrigger4.FireOutput( pActivator, this, 0 );
+ break;
+ case 5:
+ m_OnTrigger5.FireOutput( pActivator, this, 0 );
+ break;
+ case 6:
+ m_OnTrigger6.FireOutput( pActivator, this, 0 );
+ break;
+ case 7:
+ m_OnTrigger7.FireOutput( pActivator, this, 0 );
+ break;
+ case 8:
+ m_OnTrigger8.FireOutput( pActivator, this, 0 );
+ break;
+ case 9:
+ m_OnTrigger9.FireOutput( pActivator, this, 0 );
+ break;
+ case 10:
+ m_OnTrigger10.FireOutput( pActivator, this, 0 );
+ break;
+ case 11:
+ m_OnTrigger11.FireOutput( pActivator, this, 0 );
+ break;
+ case 12:
+ m_OnTrigger12.FireOutput( pActivator, this, 0 );
+ break;
+ case 13:
+ m_OnTrigger13.FireOutput( pActivator, this, 0 );
+ break;
+ case 14:
+ m_OnTrigger14.FireOutput( pActivator, this, 0 );
+ break;
+ case 15:
+ m_OnTrigger15.FireOutput( pActivator, this, 0 );
+ break;
+ case 16:
+ m_OnTrigger16.FireOutput( pActivator, this, 0 );
+ break;
+ }
+}
+
+struct NPCInterjection
+{
+ AI_Response *response;
+ CAI_BaseActor *npc;
+};
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &inputdata -
+//-----------------------------------------------------------------------------
+void CSceneEntity::InputInterjectResponse( inputdata_t &inputdata )
+{
+ // Not currently playing a scene
+ if ( !m_pScene )
+ {
+ return;
+ }
+
+ CUtlVector< CAI_BaseActor * > candidates;
+ int i;
+ for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ CBaseFlex *pTestActor = FindNamedActor( i );
+ if ( !pTestActor )
+ continue;
+
+ CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(pTestActor);
+ if ( !pBaseActor )
+ continue;
+
+ if ( !pBaseActor->IsAlive() )
+ continue;
+
+ candidates.AddToTail( pBaseActor );
+ }
+
+ int c = candidates.Count();
+
+ if ( !c )
+ {
+ return;
+ }
+
+ int useIndex = 0;
+ if ( !m_bIsPlayingBack )
+ {
+ // Use any actor if not playing a scene
+ useIndex = RandomInt( 0, c - 1 );
+ }
+ else
+ {
+ CUtlVector< NPCInterjection > validResponses;
+
+ char modifiers[ 512 ];
+ Q_snprintf( modifiers, sizeof( modifiers ), "scene:%s", STRING( GetEntityName() ) );
+
+ for ( int i = 0; i < c; i++ )
+ {
+ CAI_BaseActor *npc = candidates[ i ];
+ Assert( npc );
+
+ AI_Response *response = npc->SpeakFindResponse( inputdata.value.String(), modifiers );
+ if ( !response )
+ continue;
+
+ float duration = npc->GetResponseDuration( response );
+ // Couldn't look it up
+ if ( duration <= 0.0f )
+ continue;
+
+ if ( !npc->PermitResponse( duration ) )
+ {
+ delete response;
+ continue;
+ }
+
+ //
+ NPCInterjection inter;
+ inter.response = response;
+ inter.npc = npc;
+
+ validResponses.AddToTail( inter );
+ }
+
+ int rcount = validResponses.Count();
+ if ( rcount >= 1 )
+ {
+ int slot = RandomInt( 0, rcount - 1 );
+
+ for ( int i = 0; i < rcount; i++ )
+ {
+ NPCInterjection *pInterjection = &validResponses[ i ];
+ if ( i == slot )
+ {
+ pInterjection->npc->SpeakDispatchResponse( inputdata.value.String(), pInterjection->response );
+ }
+ else
+ {
+ delete pInterjection->response;
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CSceneEntity::InputStopWaitingForActor( inputdata_t &inputdata )
+{
+ if( m_bIsPlayingBack )
+ {
+ // Already started.
+ return;
+ }
+
+ m_bWaitingForActor = false;
+}
+
+bool CSceneEntity::CheckActors()
+{
+ Assert( m_pScene );
+ if ( !m_pScene )
+ return false;
+
+ int i;
+ for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ CBaseFlex *pTestActor = FindNamedActor( i );
+ if ( !pTestActor )
+ continue;
+
+ if ( !pTestActor->MyCombatCharacterPointer() )
+ continue;
+
+ if ( !pTestActor->MyCombatCharacterPointer()->IsAlive() )
+ return false;
+
+ if ( m_BusyActor == SCENE_BUSYACTOR_WAIT )
+ {
+ CAI_BaseNPC *pActor = pTestActor->MyNPCPointer();
+
+ if ( pActor )
+ {
+ bool bShouldWait = false;
+ if ( hl2_episodic.GetBool() )
+ {
+ // Episodic waits until the NPC is fully finished with any .vcd with speech in it
+ if ( IsRunningScriptedSceneWithSpeech( pActor ) )
+ {
+ bShouldWait = true;
+ }
+
+#ifdef HL2_EPISODIC
+ // HACK: Alyx cannot play scenes when she's in the middle of transitioning
+ if ( pActor->IsInAVehicle() )
+ {
+ CNPC_Alyx *pAlyx = dynamic_cast<CNPC_Alyx *>(pActor);
+ if ( pAlyx != NULL && ( pAlyx->GetPassengerState() == PASSENGER_STATE_ENTERING || pAlyx->GetPassengerState() == PASSENGER_STATE_EXITING ) )
+ {
+ bShouldWait = true;
+ }
+ }
+#endif // HL2_EPISODIC
+ }
+
+ if ( pActor->GetExpresser() && pActor->GetExpresser()->IsSpeaking() )
+ {
+ bShouldWait = true;
+ }
+
+ if ( bShouldWait )
+ {
+ // One of the actors for this scene is talking already.
+ // Try again next think.
+ m_bWaitingForActor = true;
+ return false;
+ }
+ }
+ }
+ else if ( m_BusyActor == SCENE_BUSYACTOR_INTERRUPT || m_BusyActor == SCENE_BUSYACTOR_INTERRUPT_CANCEL )
+ {
+ CBaseCombatCharacter *pActor = pTestActor->MyCombatCharacterPointer();
+ if ( pActor && !IsInInterruptableScenes( pActor ) )
+ {
+ // One of the actors is in a scene that's not at an interrupt point.
+ // Wait until the scene finishes or an interrupt point is reached.
+ m_bWaitingForInterrupt = true;
+ return false;
+ }
+
+ if ( m_BusyActor == SCENE_BUSYACTOR_INTERRUPT_CANCEL )
+ {
+ // Cancel existing scenes
+ RemoveActorFromScriptedScenes( pActor, false );
+ }
+ else
+ {
+ // Pause existing scenes
+ PauseActorsScriptedScenes( pActor, false );
+ m_bInterruptedActorsScenes = true;
+ }
+ }
+
+ pTestActor->StartChoreoScene( m_pScene );
+ }
+
+ return true;
+}
+
+#if !defined( _RETAIL )
+static ConVar scene_async_prefetch_spew( "scene_async_prefetch_spew", "0", 0, "Display async .ani file loading info." );
+#endif
+
+void CSceneEntity::PrefetchAnimBlocks( CChoreoScene *scene )
+{
+ Assert( scene );
+
+ // Build a fast lookup, too
+ CUtlMap< CChoreoActor *, CBaseFlex *> actorMap( 0, 0, DefLessFunc( CChoreoActor * ) );
+
+ int spew =
+#if !defined( _RETAIL )
+ scene_async_prefetch_spew.GetInt();
+#else
+ 0;
+#endif
+
+ int resident = 0;
+ int checked = 0;
+
+ // Iterate events and precache necessary resources
+ for ( int i = 0; i < scene->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ // load any necessary data
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SEQUENCE:
+ case CChoreoEvent::GESTURE:
+ {
+ CChoreoActor *actor = event->GetActor();
+ if ( actor )
+ {
+ CBaseFlex *pActor = NULL;
+ int idx = actorMap.Find( actor );
+ if ( idx == actorMap.InvalidIndex() )
+ {
+ pActor = FindNamedActor( actor );
+ idx = actorMap.Insert( actor, pActor );
+ }
+ else
+ {
+ pActor = actorMap[ idx ];
+ }
+
+ if ( pActor )
+ {
+ int seq = pActor->LookupSequence( event->GetParameters() );
+ if ( seq >= 0 )
+ {
+ CStudioHdr *pStudioHdr = pActor->GetModelPtr();
+ if ( pStudioHdr )
+ {
+ // Now look up the animblock
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( seq );
+ for ( int i = 0 ; i < seqdesc.groupsize[ 0 ] ; ++i )
+ {
+ for ( int j = 0; j < seqdesc.groupsize[ 1 ]; ++j )
+ {
+ int animation = seqdesc.anim( i, j );
+ int baseanimation = pStudioHdr->iRelativeAnim( seq, animation );
+ mstudioanimdesc_t &animdesc = pStudioHdr->pAnimdesc( baseanimation );
+
+ ++checked;
+
+ if ( spew != 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 )
+ {
+ ++resident;
+ if ( spew > 1 )
+ {
+ Msg( "%s:%s[%i:%i] was resident\n", pStudioHdr->pszName(), animdesc.pszName(), i, j );
+ }
+ }
+ else
+ {
+ if ( spew != 0 )
+ {
+ Msg( "%s:%s[%i:%i] async load\n", pStudioHdr->pszName(), animdesc.pszName(), i, j );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if ( !spew || checked <= 0 )
+ return;
+
+ Msg( "%d of %d animations resident\n", resident, checked );
+}
+
+void CSceneEntity::OnLoaded()
+{
+ // Nothing
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initiate scene playback
+//-----------------------------------------------------------------------------
+void CSceneEntity::StartPlayback( void )
+{
+ if ( !m_pScene )
+ {
+ if ( m_bSceneMissing )
+ return;
+
+ m_pScene = LoadScene( STRING( m_iszSceneFile ), this );
+ if ( !m_pScene )
+ {
+ DevMsg( "%s missing from scenes.image\n", STRING( m_iszSceneFile ) );
+ m_bSceneMissing = true;
+ return;
+ }
+
+ OnLoaded();
+
+ if ( ShouldNetwork() )
+ {
+ m_nSceneStringIndex = g_pStringTableClientSideChoreoScenes->AddString( CBaseEntity::IsServer(), STRING( m_iszSceneFile ) );
+ }
+
+ UpdateTransmitState();
+ }
+
+ if ( m_bIsPlayingBack )
+ return;
+
+ // Make sure actors are alive and able to handle this scene now, otherwise
+ // we'll wait for them to show up
+ if ( !CheckActors() )
+ {
+ return;
+ }
+
+ m_bCompletedEarly = false;
+ m_bWaitingForActor = false;
+ m_bWaitingForInterrupt = false;
+ m_bIsPlayingBack = true;
+ NetworkProp()->NetworkStateForceUpdate();
+ m_bPaused = false;
+ SetCurrentTime( 0.0f, true );
+ m_pScene->ResetSimulation();
+ ClearInterrupt();
+
+ // Put face back in neutral pose
+ ClearSceneEvents( m_pScene, false );
+
+ m_OnStart.FireOutput( this, this, 0 );
+
+ // Aysnchronously load speak sounds
+ CUtlSymbolTable prefetchSoundSymbolTable;
+ CUtlRBTree< SpeakEventSound_t > soundnames( 0, 0, SpeakEventSoundLessFunc );
+
+ BuildSortedSpeakEventSoundsPrefetchList( m_pScene, prefetchSoundSymbolTable, soundnames, 0.0f );
+ PrefetchSpeakEventSounds( prefetchSoundSymbolTable, soundnames );
+
+ // Tell any managers we're within that we've started
+ int c = m_hListManagers.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( m_hListManagers[i] )
+ {
+ m_hListManagers[i]->SceneStarted( this );
+ }
+ }
+
+ PrefetchAnimBlocks( m_pScene );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Static method used to sort by event start time
+//-----------------------------------------------------------------------------
+bool CSceneEntity::SpeakEventSoundLessFunc( const SpeakEventSound_t& lhs, const SpeakEventSound_t& rhs )
+{
+ return lhs.m_flStartTime < rhs.m_flStartTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Prefetches the list of sounds build by BuildSortedSpeakEventSoundsPrefetchList
+//-----------------------------------------------------------------------------
+void CSceneEntity::PrefetchSpeakEventSounds( CUtlSymbolTable& table, CUtlRBTree< SpeakEventSound_t >& soundnames )
+{
+ for ( int i = soundnames.FirstInorder(); i != soundnames.InvalidIndex() ; i = soundnames.NextInorder( i ) )
+ {
+ SpeakEventSound_t& sound = soundnames[ i ];
+ // Look it up in the string table
+ char const *soundname = table.String( sound.m_Symbol );
+
+ // Warning( "Prefetch %s\n", soundname );
+
+ PrefetchScriptSound( soundname );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Builds list of sounds sorted by start time for prefetching
+//-----------------------------------------------------------------------------
+void CSceneEntity::BuildSortedSpeakEventSoundsPrefetchList(
+ CChoreoScene *scene,
+ CUtlSymbolTable& table,
+ CUtlRBTree< SpeakEventSound_t >& soundnames,
+ float timeOffset )
+{
+ Assert( scene );
+
+ // Iterate events and precache necessary resources
+ for ( int i = 0; i < scene->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ // load any necessary data
+ switch (event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+
+ // NOTE: The script entries associated with .vcds are forced to preload to avoid
+ // loading hitches during triggering
+ char soundname[ CChoreoEvent::MAX_CCTOKEN_STRING ];
+ Q_strncpy( soundname, event->GetParameters(), sizeof( soundname ) );
+
+ if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
+ {
+ event->GetPlaybackCloseCaptionToken( soundname, sizeof( soundname ) );
+ }
+
+ // In single player, try to use the combined or regular .wav files as needed
+ if ( gpGlobals->maxClients == 1 )
+ {
+ CBasePlayer *player = UTIL_GetLocalPlayer();
+ if ( player && !GetSoundNameForPlayer( event, player, soundname, sizeof( soundname ), player ) )
+ {
+ // Skip to next event
+ continue;
+ }
+ }
+ /*
+ else
+ {
+ // UNDONE: Probably need some other solution in multiplayer... (not sure how to "prefetch" on certain players
+ // with one sound, but not prefetch the same sound for others...)
+ }
+ */
+
+ SpeakEventSound_t ses;
+ ses.m_Symbol = table.AddString( soundname );
+ ses.m_flStartTime = timeOffset + event->GetStartTime();
+
+ soundnames.Insert( ses );
+ }
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ // Only allow a single level of subscenes for now
+ if ( !scene->IsSubScene() )
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( !subscene )
+ {
+ subscene = LoadScene( event->GetParameters(), this );
+ subscene->SetSubScene( true );
+ event->SetSubScene( subscene );
+
+ // Now precache it's resources, if any
+ BuildSortedSpeakEventSoundsPrefetchList( subscene, table, soundnames, event->GetStartTime() );
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::PausePlayback( void )
+{
+ if ( !m_bIsPlayingBack )
+ return;
+
+ if ( m_bPaused )
+ return;
+
+ m_bPaused = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::ResumePlayback( void )
+{
+ if ( !m_bIsPlayingBack )
+ return;
+
+ if ( !m_bPaused )
+ return;
+
+ Assert( m_pScene );
+ if ( !m_pScene )
+ {
+ // This should never happen!!!!
+ return;
+ }
+
+ // FIXME: Iterate using m_pScene->IterateResumeConditionEvents and
+ // only resume if the event conditions have all been satisfied
+
+ // FIXME: Just resume for now
+ m_pScene->ResumeSimulation();
+
+ m_bPaused = false;
+ m_bPausedViaInput = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::CancelPlayback( void )
+{
+ if ( !m_bIsPlayingBack )
+ return;
+
+ m_bIsPlayingBack = false;
+ m_bPaused = false;
+
+ m_OnCanceled.FireOutput( this, this, 0 );
+
+ LocalScene_Printf( "%s : %8.2f: canceled\n", STRING( m_iszSceneFile ), m_flCurrentTime );
+
+ OnSceneFinished( true, false );
+}
+
+void CSceneEntity::PitchShiftPlayback( float fPitch )
+{
+ fPitch = clamp( fPitch, SCENE_MIN_PITCH, SCENE_MAX_PITCH );
+
+ m_fPitch = fPitch;
+
+ if ( !m_pScene )
+ return;
+
+ for ( int iActor = 0 ; iActor < m_pScene->GetNumActors(); ++iActor )
+ {
+ CBaseFlex *pTestActor = FindNamedActor( iActor );
+
+ if ( !pTestActor )
+ continue;
+
+ char szBuff[ 256 ];
+
+ if ( m_pScene->GetPlayingSoundName( szBuff, sizeof( szBuff ) ) )
+ {
+ CPASAttenuationFilter filter( pTestActor );
+ EmitSound_t params;
+ params.m_pSoundName = szBuff;
+ params.m_nPitch = 100.0f * fPitch;
+ params.m_nFlags = SND_CHANGE_PITCH;
+ pTestActor->EmitSound( filter, pTestActor->entindex(), params );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start a resume scene, if we have one, and resume playing when it finishes
+//-----------------------------------------------------------------------------
+void CSceneEntity::QueueResumePlayback( void )
+{
+ // Do we have a resume scene?
+ if ( m_iszResumeSceneFile != NULL_STRING )
+ {
+ bool bStartedScene = false;
+
+ // If it has ".vcd" somewhere in the string, try using it as a scene file first
+ if ( Q_stristr( STRING(m_iszResumeSceneFile), ".vcd" ) )
+ {
+ bStartedScene = InstancedScriptedScene( NULL, STRING(m_iszResumeSceneFile), &m_hWaitingForThisResumeScene, 0, false ) != 0;
+ }
+
+ // HACKHACK: For now, get the first target, and see if we can find a response for him
+ if ( !bStartedScene )
+ {
+ CBaseFlex *pActor = FindNamedActor( 0 );
+ if ( pActor )
+ {
+ CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(pActor);
+ if ( pBaseActor )
+ {
+ AI_Response *result = pBaseActor->SpeakFindResponse( STRING(m_iszResumeSceneFile), NULL );
+ if ( result )
+ {
+ char response[ 256 ];
+ result->GetResponse( response, sizeof( response ) );
+ bStartedScene = InstancedScriptedScene( NULL, response, &m_hWaitingForThisResumeScene, 0, false ) != 0;
+ }
+ }
+ }
+ }
+
+ // If we started a scene/response, wait for it to finish
+ if ( bStartedScene )
+ {
+ m_bWaitingForResumeScene = true;
+ }
+ else
+ {
+ // Failed to create the scene. Resume immediately.
+ ResumePlayback();
+ }
+ }
+ else
+ {
+ // No resume scene, so just resume immediately
+ ResumePlayback();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Query whether the scene actually loaded. Only meaninful after Spawn()
+//-----------------------------------------------------------------------------
+bool CSceneEntity::ValidScene() const
+{
+ return ( m_pScene != NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pActor -
+// *scene -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::DispatchStartSubScene( CChoreoScene *scene, CBaseFlex *pActor, CChoreoEvent *event)
+{
+ if ( !scene->IsSubScene() )
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( !subscene )
+ {
+ Assert( 0 );
+ /*
+ subscene = LoadScene( event->GetParameters() );
+ subscene->SetSubScene( true );
+ event->SetSubScene( subscene );
+ */
+ }
+
+ if ( subscene )
+ {
+ subscene->ResetSimulation();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: All events are leading edge triggered
+// Input : currenttime -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::StartEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event );
+
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ {
+ LocalScene_Printf( "%s : %8.2f: ignored %s\n", STRING( m_iszSceneFile ), currenttime, event->GetDescription() );
+ return;
+ }
+
+
+ CBaseFlex *pActor = NULL;
+ CChoreoActor *actor = event->GetActor();
+ if ( actor )
+ {
+ pActor = FindNamedActor( actor );
+ if (pActor == NULL)
+ {
+ Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", STRING(GetEntityName()), actor->GetName() );
+ return;
+ }
+ }
+
+ LocalScene_Printf( "%s : %8.2f: start %s\n", STRING( m_iszSceneFile ), currenttime, event->GetDescription() );
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SUBSCENE:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ DispatchStartSubScene( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::EXPRESSION:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ DispatchStartExpression( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ DispatchStartFlexAnimation( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::LOOKAT:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ CBaseEntity *pActor2 = FindNamedEntity( event->GetParameters( ), pActor );
+ if ( pActor2 )
+ {
+ // Huh?
+ DispatchStartLookAt( scene, pActor, pActor2, event );
+ }
+ else
+ {
+ Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", STRING(GetEntityName()), event->GetParameters() );
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ if ( pActor )
+ {
+ // Speaking is edge triggered
+
+ // 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;
+ case CChoreoEvent::MOVETO:
+ {
+ // FIXME: make sure moveto's aren't edge triggered
+ if ( !event->HasEndTime() )
+ {
+ event->SetEndTime( event->GetStartTime() + 1.0 );
+ }
+
+ if ( pActor && !IsMultiplayer() )
+ {
+ CBaseEntity *pActor2 = NULL;
+ if ( event->GetParameters3( ) && strlen( event->GetParameters3( ) ) > 0 )
+ {
+ pActor2 = FindNamedEntityClosest( event->GetParameters( ), pActor, false, true, event->GetParameters3( ) );
+ }
+ else
+ {
+ pActor2 = FindNamedEntity( event->GetParameters( ), pActor, false, true );
+ }
+ if ( pActor2 )
+ {
+
+ DispatchStartMoveTo( scene, pActor, pActor2, event );
+ }
+ else
+ {
+ Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", STRING(GetEntityName()), event->GetParameters() );
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::FACE:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ CBaseEntity *pActor2 = FindNamedEntity( event->GetParameters( ), pActor );
+ if ( pActor2 )
+ {
+ DispatchStartFace( scene, pActor, pActor2, event );
+ }
+ else
+ {
+ Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", STRING(GetEntityName()), event->GetParameters() );
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ if ( pActor )
+ {
+ DispatchStartGesture( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::GENERIC:
+ {
+ // If the first token in the parameters is "debugtext", print the rest of the text
+ if ( event->GetParameters() && !Q_strncmp( event->GetParameters(), "debugtext", 9 ) )
+ {
+ const char *pszText = event->GetParameters() + 10;
+
+ hudtextparms_s tTextParam;
+ tTextParam.x = -1;
+ tTextParam.y = 0.65;
+ tTextParam.effect = 0;
+ tTextParam.r1 = 255;
+ tTextParam.g1 = 170;
+ tTextParam.b1 = 0;
+ tTextParam.a1 = 255;
+ tTextParam.r2 = 255;
+ tTextParam.g2 = 170;
+ tTextParam.b2 = 0;
+ tTextParam.a2 = 255;
+ tTextParam.fadeinTime = 0;
+ tTextParam.fadeoutTime = 0;
+ tTextParam.holdTime = 3.1;
+ tTextParam.fxTime = 0;
+ tTextParam.channel = 1;
+ UTIL_HudMessageAll( tTextParam, pszText );
+ break;
+ }
+
+ if ( pActor )
+ {
+ DispatchStartGeneric( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::FIRETRIGGER:
+ {
+ if ( IsMultiplayer() )
+ break;
+
+ // Don't re-fire triggers during restore, the entities should already reflect all such state...
+ if ( m_bRestoring )
+ {
+ break;
+ }
+
+ CBaseEntity *pActivator = pActor;
+ if (!pActivator)
+ {
+ pActivator = this;
+ }
+
+ // FIXME: how do I decide who fired it??
+ switch( atoi( event->GetParameters() ) )
+ {
+ case 1:
+ m_OnTrigger1.FireOutput( pActivator, this, 0 );
+ break;
+ case 2:
+ m_OnTrigger2.FireOutput( pActivator, this, 0 );
+ break;
+ case 3:
+ m_OnTrigger3.FireOutput( pActivator, this, 0 );
+ break;
+ case 4:
+ m_OnTrigger4.FireOutput( pActivator, this, 0 );
+ break;
+ case 5:
+ m_OnTrigger5.FireOutput( pActivator, this, 0 );
+ break;
+ case 6:
+ m_OnTrigger6.FireOutput( pActivator, this, 0 );
+ break;
+ case 7:
+ m_OnTrigger7.FireOutput( pActivator, this, 0 );
+ break;
+ case 8:
+ m_OnTrigger8.FireOutput( pActivator, this, 0 );
+ break;
+ case 9:
+ m_OnTrigger9.FireOutput( pActivator, this, 0 );
+ break;
+ case 10:
+ m_OnTrigger10.FireOutput( pActivator, this, 0 );
+ break;
+ case 11:
+ m_OnTrigger11.FireOutput( pActivator, this, 0 );
+ break;
+ case 12:
+ m_OnTrigger12.FireOutput( pActivator, this, 0 );
+ break;
+ case 13:
+ m_OnTrigger13.FireOutput( pActivator, this, 0 );
+ break;
+ case 14:
+ m_OnTrigger14.FireOutput( pActivator, this, 0 );
+ break;
+ case 15:
+ m_OnTrigger15.FireOutput( pActivator, this, 0 );
+ break;
+ case 16:
+ m_OnTrigger16.FireOutput( pActivator, this, 0 );
+ break;
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ if ( pActor )
+ {
+ DispatchStartSequence( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::SECTION:
+ {
+ if ( IsMultiplayer() )
+ break;
+
+ // Pauses scene playback
+ DispatchPauseScene( scene, event->GetParameters() );
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ DispatchProcessLoop( scene, event );
+ }
+ break;
+ case CChoreoEvent::INTERRUPT:
+ {
+ if ( IsMultiplayer() )
+ break;
+
+ DispatchStartInterrupt( scene, event );
+ }
+ break;
+
+ case CChoreoEvent::STOPPOINT:
+ {
+ if ( IsMultiplayer() )
+ break;
+
+ DispatchStopPoint( scene, event->GetParameters() );
+ }
+ break;
+
+ case CChoreoEvent::PERMIT_RESPONSES:
+ {
+ if ( IsMultiplayer() )
+ break;
+
+ DispatchStartPermitResponses( scene, pActor, event );
+ }
+ break;
+ default:
+ {
+ // FIXME: Unhandeled event
+ // Assert(0);
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : currenttime -
+// *event -
+//-----------------------------------------------------------------------------
+void CSceneEntity::EndEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ Assert( event );
+
+ if ( !Q_stricmp( event->GetName(), "NULL" ) )
+ {
+ return;
+ }
+
+ CBaseFlex *pActor = NULL;
+ CChoreoActor *actor = event->GetActor();
+ if ( actor )
+ {
+ pActor = FindNamedActor( actor );
+ }
+
+ LocalScene_Printf( "%s : %8.2f: finish %s\n", STRING( m_iszSceneFile ), currenttime, event->GetDescription() );
+
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::EXPRESSION:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ DispatchEndExpression( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ if ( pActor )
+ {
+ DispatchEndSpeak( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ DispatchEndFlexAnimation( scene, pActor, event );
+ }
+ }
+ break;
+
+ case CChoreoEvent::LOOKAT:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ DispatchEndLookAt( scene, pActor, event );
+ }
+ }
+ break;
+
+
+ case CChoreoEvent::GESTURE:
+ {
+ if ( pActor )
+ {
+ DispatchEndGesture( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::GENERIC:
+ {
+ // If the first token in the parameters is "debugtext", we printed it and we're done
+ if ( event->GetParameters() && !Q_strncmp( event->GetParameters(), "debugtext", 9 ) )
+ break;
+
+ if ( pActor )
+ {
+ DispatchEndGeneric( scene, pActor, event );
+ }
+ }
+ break;
+ case CChoreoEvent::SEQUENCE:
+ {
+ if ( pActor )
+ {
+ DispatchEndSequence( scene, pActor, event );
+ }
+ }
+ break;
+
+ case CChoreoEvent::FACE:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ DispatchEndFace( scene, pActor, event );
+ }
+ }
+ break;
+
+ case CChoreoEvent::MOVETO:
+ {
+ if ( pActor && !IsMultiplayer() )
+ {
+ DispatchEndMoveTo( scene, pActor, event );
+ }
+ }
+ break;
+
+ case CChoreoEvent::SUBSCENE:
+ {
+ if ( IsMultiplayer() )
+ break;
+
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( subscene )
+ {
+ subscene->ResetSimulation();
+ }
+ }
+ break;
+ case CChoreoEvent::INTERRUPT:
+ {
+ if ( IsMultiplayer() )
+ break;
+
+ DispatchEndInterrupt( scene, event );
+ }
+ break;
+
+ case CChoreoEvent::PERMIT_RESPONSES:
+ {
+ if ( IsMultiplayer() )
+ break;
+
+ DispatchEndPermitResponses( scene, pActor, event );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Only spew one time per missing scene!!!
+// Input : *scenename -
+//-----------------------------------------------------------------------------
+void MissingSceneWarning( char const *scenename )
+{
+ static CUtlSymbolTable missing;
+
+ // Make sure we only show the message once
+ if ( UTL_INVAL_SYMBOL == missing.Find( scenename ) )
+ {
+ missing.AddString( scenename );
+
+ Warning( "Scene '%s' missing!\n", scenename );
+ }
+}
+
+bool CSceneEntity::ShouldNetwork() const
+{
+ if ( m_bMultiplayer )
+ {
+ if ( m_pScene &&
+ ( m_pScene->HasEventsOfType( CChoreoEvent::FLEXANIMATION ) ||
+ m_pScene->HasEventsOfType( CChoreoEvent::EXPRESSION )||
+ m_pScene->HasEventsOfType( CChoreoEvent::GESTURE ) ||
+ m_pScene->HasEventsOfType( CChoreoEvent::SEQUENCE ) ) )
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if ( m_pScene &&
+ ( m_pScene->HasEventsOfType( CChoreoEvent::FLEXANIMATION ) ||
+ m_pScene->HasEventsOfType( CChoreoEvent::EXPRESSION ) ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+CChoreoScene *CSceneEntity::LoadScene( const char *filename, IChoreoEventCallback *pCallback )
+{
+ DevMsg( 2, "Blocking load of scene from '%s'\n", filename );
+
+ char loadfile[MAX_PATH];
+ Q_strncpy( loadfile, filename, sizeof( loadfile ) );
+ Q_SetExtension( loadfile, ".vcd", sizeof( loadfile ) );
+ Q_FixSlashes( loadfile );
+
+ // binary compiled vcd
+ void *pBuffer;
+ int fileSize;
+ if ( !CopySceneFileIntoMemory( loadfile, &pBuffer, &fileSize ) )
+ {
+ MissingSceneWarning( loadfile );
+ return NULL;
+ }
+
+ CChoreoScene *pScene = new CChoreoScene( NULL );
+ CUtlBuffer buf( pBuffer, fileSize, CUtlBuffer::READ_ONLY );
+ if ( !pScene->RestoreFromBinaryBuffer( buf, loadfile, &g_ChoreoStringPool ) )
+ {
+ Warning( "CSceneEntity::LoadScene: Unable to load binary scene '%s'\n", loadfile );
+ delete pScene;
+ pScene = NULL;
+ }
+ else
+ {
+ pScene->SetPrintFunc( LocalScene_Printf );
+ pScene->SetEventCallbackInterface( pCallback );
+ }
+
+ FreeSceneFileMemory( pBuffer );
+ return pScene;
+}
+
+CChoreoScene *BlockingLoadScene( const char *filename )
+{
+ return CSceneEntity::LoadScene( filename, NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::UnloadScene( void )
+{
+ if ( m_pScene )
+ {
+ ClearSceneEvents( m_pScene, false );
+
+ for ( int i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ CBaseFlex *pTestActor = FindNamedActor( i );
+
+ if ( !pTestActor )
+ continue;
+
+ pTestActor->RemoveChoreoScene( m_pScene );
+ }
+ }
+ delete m_pScene;
+ m_pScene = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// 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 CSceneEntity::ProcessEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SUBSCENE:
+ {
+ Assert( event->GetType() == CChoreoEvent::SUBSCENE );
+
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( !subscene )
+ return;
+
+ if ( subscene->SimulationFinished() )
+ return;
+
+ // Have subscenes think for appropriate time
+ subscene->Think( m_flFrameTime );
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ 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 CSceneEntity::CheckEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SUBSCENE:
+ {
+ }
+ break;
+ default:
+ {
+ CBaseFlex *pActor = NULL;
+ CChoreoActor *actor = event->GetActor();
+ if ( actor )
+ {
+ pActor = FindNamedActor( actor );
+ if (pActor == NULL)
+ {
+ Warning( "CSceneEntity %s unable to find actor \"%s\"\n", STRING(GetEntityName()), actor->GetName() );
+ return true;
+ }
+ }
+ if (pActor)
+ {
+ return pActor->CheckSceneEvent( currenttime, scene, event );
+ }
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a sticky version of a named actor
+// Input : CChoreoActor
+// Output : CBaseFlex
+//-----------------------------------------------------------------------------
+
+CBaseFlex *CSceneEntity::FindNamedActor( int index )
+{
+ if (m_hActorList.Count() == 0)
+ {
+ m_hActorList.SetCount( m_pScene->GetNumActors() );
+ NetworkProp()->NetworkStateForceUpdate();
+ }
+
+ if ( !m_hActorList.IsValidIndex( index ) )
+ {
+ DevWarning( "Scene %s has %d actors, but scene entity only has %d actors\n", m_pScene->GetFilename(), m_pScene->GetNumActors(), m_hActorList.Size() );
+ return NULL;
+ }
+
+ CBaseFlex *pActor = m_hActorList[ index ];
+
+ if (pActor == NULL || !pActor->IsAlive() )
+ {
+ CChoreoActor *pChoreoActor = m_pScene->GetActor( index );
+ if ( !pChoreoActor )
+ return NULL;
+
+ pActor = FindNamedActor( pChoreoActor->GetName() );
+
+ if (pActor)
+ {
+ // save who we found so we'll use them again
+ m_hActorList[ index ] = pActor;
+ NetworkProp()->NetworkStateForceUpdate();
+ }
+ }
+
+ return pActor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a sticky version of a named actor
+// Input : CChoreoActor
+// Output : CBaseFlex
+//-----------------------------------------------------------------------------
+
+CBaseFlex *CSceneEntity::FindNamedActor( CChoreoActor *pChoreoActor )
+{
+ int index = m_pScene->FindActorIndex( pChoreoActor );
+
+ if (index >= 0)
+ {
+ return FindNamedActor( index );
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Search for an actor by name, make sure it can do face poses
+// Input : *name -
+// Output : CBaseFlex
+//-----------------------------------------------------------------------------
+CBaseFlex *CSceneEntity::FindNamedActor( const char *name )
+{
+ CBaseEntity *entity = FindNamedEntity( name, NULL, true );
+
+ if ( !entity )
+ {
+ // Couldn't find actor!
+ return NULL;
+ }
+
+ // Make sure it can actually do facial animation, etc.
+ CBaseFlex *flexEntity = dynamic_cast< CBaseFlex * >( entity );
+ if ( !flexEntity )
+ {
+ // That actor was not a CBaseFlex!
+ return NULL;
+ }
+
+ return flexEntity;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find an entity specified by a target name
+// Input : *name -
+// Output : CBaseEntity
+//-----------------------------------------------------------------------------
+CBaseEntity *CSceneEntity::FindNamedTarget( string_t iszTarget, bool bBaseFlexOnly )
+{
+ if ( !stricmp( STRING(iszTarget), "!activator" ) )
+ return m_hActivator;
+
+ // If we don't have a wildcard in the target, just return the first entity found
+ if ( !strchr( STRING(iszTarget), '*' ) )
+ return gEntList.FindEntityByName( NULL, iszTarget );
+
+ CBaseEntity *pTarget = NULL;
+ while ( (pTarget = gEntList.FindEntityByName( pTarget, iszTarget )) != NULL )
+ {
+ if ( bBaseFlexOnly )
+ {
+ // Make sure it can actually do facial animation, etc.
+ if ( dynamic_cast< CBaseFlex * >( pTarget ) )
+ return pTarget;
+ }
+ else
+ {
+ return pTarget;
+ }
+ }
+
+ // Failed to find one
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Filters entities only if they're clear
+//-----------------------------------------------------------------------------
+class CSceneFindMarkFilter : public IEntityFindFilter
+{
+public:
+ void SetActor( CBaseEntity *pActor )
+ {
+ m_hActor = pActor;
+ }
+
+ bool ShouldFindEntity( CBaseEntity *pEntity )
+ {
+ if ( !m_hActor )
+ return true;
+
+ // If we find no truly valid marks, we'll just use the first.
+ if ( !m_hEntityFound.Get() )
+ {
+ m_hEntityFound = pEntity;
+ }
+
+ // We only want marks that are clear
+ trace_t tr;
+ Vector vecOrigin = pEntity->GetAbsOrigin();
+ AI_TraceHull( vecOrigin, vecOrigin, m_hActor->WorldAlignMins(), m_hActor->WorldAlignMaxs(), MASK_SOLID, m_hActor, COLLISION_GROUP_NONE, &tr );
+ if ( tr.startsolid )
+ {
+ return false;
+ }
+ m_hEntityFound = pEntity;
+ return true;
+ }
+
+ CBaseEntity *GetFilterResult( void )
+ {
+ return m_hEntityFound;
+ }
+
+private:
+ EHANDLE m_hActor;
+
+ // To maintain backwards compatability, store off the first mark
+ // we find. If we find no truly valid marks, we'll just use the first.
+ EHANDLE m_hEntityFound;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds the entity nearest to both entities, and is clear
+//-----------------------------------------------------------------------------
+class CSceneFindNearestMarkFilter : public IEntityFindFilter
+{
+public:
+
+ CSceneFindNearestMarkFilter( const CBaseEntity *pActor, const Vector &vecPos2, float flMaxRadius = MAX_TRACE_LENGTH )
+ {
+ m_vecPos2 = vecPos2;
+
+ m_flMaxSegmentDistance = flMaxRadius;
+
+ m_flNearestToTarget = flMaxRadius;
+ m_pNearestToTarget = NULL;
+ m_flNearestToActor = flMaxRadius;
+ m_pNearestToActor = NULL;
+
+ m_hActor = pActor;
+ if (pActor)
+ {
+ m_vecPos1 = pActor->GetAbsOrigin();
+ m_flMaxSegmentDistance = MIN( flMaxRadius, (m_vecPos1 - m_vecPos2).Length() + 1.0 );
+ if (m_flMaxSegmentDistance <= 1.0)
+ {
+ // must be closest to self
+ m_flMaxSegmentDistance = MIN( flMaxRadius, MAX_TRACE_LENGTH );
+ }
+ }
+ }
+
+ bool ShouldFindEntity( CBaseEntity *pEntity )
+ {
+ if ( !m_hActor )
+ return true;
+
+ // If we find no truly valid marks, we'll just use the first.
+ if ( m_pNearestToActor == NULL )
+ {
+ m_pNearestToActor = pEntity;
+ }
+
+ // We only want marks that are clear
+ trace_t tr;
+ Vector vecOrigin = pEntity->GetAbsOrigin();
+ AI_TraceHull( vecOrigin, vecOrigin, m_hActor->WorldAlignMins(), m_hActor->WorldAlignMaxs(), MASK_SOLID, m_hActor, COLLISION_GROUP_NONE, &tr );
+ if ( !tr.startsolid || tr.m_pEnt == m_hActor)
+ {
+ float dist1 = (m_vecPos1 - pEntity->GetAbsOrigin()).Length();
+ float dist2 = (m_vecPos2 - pEntity->GetAbsOrigin()).Length();
+ /*
+ char text[256];
+ Q_snprintf( text, sizeof( text ), "%.0f : %.0f", dist1, dist2 );
+ NDebugOverlay::Text( pEntity->GetAbsOrigin() + Vector( 0, 0, 8 ), text, false, 5.0f );
+ */
+ // find the point closest to the actor
+ if (dist1 <= m_flNearestToActor)
+ {
+ m_pNearestToActor = pEntity;
+ m_flNearestToActor = dist2;
+ }
+ // find that node that's closest to both, but the distance to it from the actor isn't farther than
+ // the distance to the second node. This should keep the actor from walking past their point of interest
+ if (dist1 <= m_flMaxSegmentDistance && dist2 <= m_flMaxSegmentDistance && dist2 < m_flNearestToTarget)
+ {
+ m_pNearestToTarget = pEntity;
+ m_flNearestToTarget = dist2;
+ }
+ }
+
+ return false;
+ }
+
+ CBaseEntity *GetFilterResult( void )
+ {
+ if (m_pNearestToTarget)
+ return m_pNearestToTarget;
+ return m_pNearestToActor;
+ }
+
+private:
+ EHANDLE m_hActor;
+ Vector m_vecPos1;
+ Vector m_vecPos2;
+ float m_flMaxSegmentDistance;
+ float m_flNearestToTarget;
+ CBaseEntity *m_pNearestToTarget;
+ float m_flNearestToActor;
+ CBaseEntity *m_pNearestToActor;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Search for an actor by name, make sure it can do face poses
+// Input : *name -
+// Output : CBaseFlex
+//-----------------------------------------------------------------------------
+CBaseEntity *CSceneEntity::FindNamedEntity( const char *name, CBaseEntity *pActor, bool bBaseFlexOnly, bool bUseClear )
+{
+ CBaseEntity *entity = NULL;
+
+ if ( !stricmp( name, "Player" ) || !stricmp( name, "!player" ))
+ {
+ entity = ( gpGlobals->maxClients == 1 ) ? ( CBaseEntity * )UTIL_GetLocalPlayer() : NULL;
+ }
+ else if ( !stricmp( name, "!target1" ) )
+ {
+ if (m_hTarget1 == NULL)
+ {
+ m_hTarget1 = FindNamedTarget( m_iszTarget1, bBaseFlexOnly );
+ }
+ return m_hTarget1;
+ }
+ else if ( !stricmp( name, "!target2" ) )
+ {
+ if (m_hTarget2 == NULL)
+ {
+ m_hTarget2 = FindNamedTarget( m_iszTarget2, bBaseFlexOnly );
+ }
+ return m_hTarget2;
+ }
+ else if ( !stricmp( name, "!target3" ) )
+ {
+ if (m_hTarget3 == NULL)
+ {
+ m_hTarget3 = FindNamedTarget( m_iszTarget3, bBaseFlexOnly );
+ }
+ return m_hTarget3;
+ }
+ else if ( !stricmp( name, "!target4" ) )
+ {
+ if (m_hTarget4 == NULL)
+ {
+ m_hTarget4 = FindNamedTarget( m_iszTarget4, bBaseFlexOnly );
+ }
+ return m_hTarget4;
+ }
+ else if ( !stricmp( name, "!target5" ) )
+ {
+ if (m_hTarget5 == NULL)
+ {
+ m_hTarget5 = FindNamedTarget( m_iszTarget5, bBaseFlexOnly );
+ }
+ return m_hTarget5;
+ }
+ else if ( !stricmp( name, "!target6" ) )
+ {
+ if (m_hTarget6 == NULL)
+ {
+ m_hTarget6 = FindNamedTarget( m_iszTarget6, bBaseFlexOnly );
+ }
+ return m_hTarget6;
+ }
+ else if ( !stricmp( name, "!target7" ) )
+ {
+ if (m_hTarget7 == NULL)
+ {
+ m_hTarget7 = FindNamedTarget( m_iszTarget7, bBaseFlexOnly );
+ }
+ return m_hTarget7;
+ }
+ else if ( !stricmp( name, "!target8" ) )
+ {
+ if (m_hTarget8 == NULL)
+ {
+ m_hTarget8 = FindNamedTarget( m_iszTarget8, bBaseFlexOnly );
+ }
+ return m_hTarget8;
+ }
+ else if (pActor && pActor->MyNPCPointer())
+ {
+ CSceneFindMarkFilter *pFilter = NULL;
+ if ( bUseClear )
+ {
+ pFilter = new CSceneFindMarkFilter();
+ pFilter->SetActor( pActor );
+ }
+
+ entity = pActor->MyNPCPointer()->FindNamedEntity( name, pFilter );
+ if ( !entity && pFilter )
+ {
+ entity = pFilter->GetFilterResult();
+ }
+ }
+ else
+ {
+ // search for up to 32 entities with the same name and choose one randomly
+ CBaseEntity *entityList[ FINDNAMEDENTITY_MAX_ENTITIES ];
+ int iCount;
+
+ entity = NULL;
+ for( iCount = 0; iCount < FINDNAMEDENTITY_MAX_ENTITIES; iCount++ )
+ {
+ entity = gEntList.FindEntityByName( entity, name, NULL, pActor );
+ if ( !entity )
+ {
+ break;
+ }
+ entityList[ iCount ] = entity;
+ }
+
+ if ( iCount > 0 )
+ {
+ entity = entityList[ RandomInt( 0, iCount - 1 ) ];
+ }
+ else
+ {
+ entity = NULL;
+ }
+ }
+
+ return entity;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Search for an actor by name, make sure it can do face poses
+// Input : *name -
+// Output : CBaseFlex
+//-----------------------------------------------------------------------------
+CBaseEntity *CSceneEntity::FindNamedEntityClosest( const char *name, CBaseEntity *pActor, bool bBaseFlexOnly, bool bUseClear, const char *pszSecondary )
+{
+ CBaseEntity *entity = NULL;
+
+ if ( !stricmp( name, "!activator" ) )
+ {
+ return m_hActivator;
+ }
+ else if ( !stricmp( name, "Player" ) || !stricmp( name, "!player" ))
+ {
+ entity = ( gpGlobals->maxClients == 1 ) ? ( CBaseEntity * )UTIL_GetLocalPlayer() : NULL;
+ return entity;
+ }
+ else if ( !stricmp( name, "!target1" ) )
+ {
+ name = STRING( m_iszTarget1 );
+ }
+ else if ( !stricmp( name, "!target2" ) )
+ {
+ name = STRING( m_iszTarget2 );
+ }
+ else if ( !stricmp( name, "!target3" ) )
+ {
+ name = STRING( m_iszTarget3 );
+ }
+ else if ( !stricmp( name, "!target4" ) )
+ {
+ name = STRING( m_iszTarget4 );
+ }
+ else if ( !stricmp( name, "!target5" ) )
+ {
+ name = STRING( m_iszTarget5 );
+ }
+ else if ( !stricmp( name, "!target6" ) )
+ {
+ name = STRING( m_iszTarget6 );
+ }
+ else if ( !stricmp( name, "!target7" ) )
+ {
+ name = STRING( m_iszTarget7 );
+ }
+
+ if (pActor && pActor->MyNPCPointer())
+ {
+ if (pszSecondary && strlen( pszSecondary ) > 0)
+ {
+ CBaseEntity *pActor2 = FindNamedEntityClosest( pszSecondary, pActor, false, false, NULL );
+
+ if (pActor2)
+ {
+ CSceneFindNearestMarkFilter *pFilter = new CSceneFindNearestMarkFilter( pActor, pActor2->GetAbsOrigin() );
+
+ entity = pActor->MyNPCPointer()->FindNamedEntity( name, pFilter );
+ if (!entity && pFilter)
+ {
+ entity = pFilter->GetFilterResult();
+ }
+ }
+ }
+ if (!entity)
+ {
+ CSceneFindMarkFilter *pFilter = NULL;
+ if ( bUseClear )
+ {
+ pFilter = new CSceneFindMarkFilter();
+ pFilter->SetActor( pActor );
+ }
+
+ entity = pActor->MyNPCPointer()->FindNamedEntity( name, pFilter );
+ if (!entity && pFilter)
+ {
+ entity = pFilter->GetFilterResult();
+ }
+ }
+ }
+ else
+ {
+ // search for up to 32 entities with the same name and choose one randomly
+ int iCount;
+ entity = NULL;
+ CBaseEntity *current = NULL;
+ for( iCount = 0; iCount < FINDNAMEDENTITY_MAX_ENTITIES; iCount++ )
+ {
+ current = gEntList.FindEntityByName( current, name, NULL, pActor );
+ if ( current )
+ {
+ if (RandomInt( 0, iCount ) == 0)
+ entity = current;
+ }
+ }
+
+ entity = NULL;
+ }
+
+ return entity;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove all "scene" expressions from all actors in this scene
+//-----------------------------------------------------------------------------
+void CSceneEntity::ClearSceneEvents( CChoreoScene *scene, bool canceled )
+{
+ if ( !m_pScene )
+ return;
+
+ LocalScene_Printf( "%s : %8.2f: clearing events\n", STRING( m_iszSceneFile ), m_flCurrentTime );
+
+ int i;
+ for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ CBaseFlex *pActor = FindNamedActor( i );
+ if ( !pActor )
+ continue;
+
+ // Clear any existing expressions
+ pActor->ClearSceneEvents( scene, canceled );
+ }
+
+ // Iterate events and precache necessary resources
+ for ( i = 0; i < scene->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ // load any necessary data
+ switch (event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ // Only allow a single level of subscenes for now
+ if ( !scene->IsSubScene() )
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( subscene )
+ {
+ ClearSceneEvents( subscene, canceled );
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove all imposed schedules from all actors in this scene
+//-----------------------------------------------------------------------------
+void CSceneEntity::ClearSchedules( CChoreoScene *scene )
+{
+ if ( !m_pScene )
+ return;
+
+ int i;
+ for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ CBaseFlex *pActor = FindNamedActor( i );
+ if ( !pActor )
+ continue;
+
+ CAI_BaseNPC *pNPC = pActor->MyNPCPointer();
+
+ if ( pNPC )
+ {
+ /*
+ if ( pNPC->IsCurSchedule( SCHED_SCENE_GENERIC ) )
+ pNPC->ClearSchedule( "Scene entity clearing all actor schedules" );
+ */
+ }
+ else
+ {
+ pActor->ResetSequence( pActor->SelectWeightedSequence( ACT_IDLE ) );
+ pActor->SetCycle( 0 );
+ }
+ // Clear any existing expressions
+ }
+
+ // Iterate events and precache necessary resources
+ for ( i = 0; i < scene->GetNumEvents(); i++ )
+ {
+ CChoreoEvent *event = scene->GetEvent( i );
+ if ( !event )
+ continue;
+
+ // load any necessary data
+ switch (event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SUBSCENE:
+ {
+ // Only allow a single level of subscenes for now
+ if ( !scene->IsSubScene() )
+ {
+ CChoreoScene *subscene = event->GetSubScene();
+ if ( subscene )
+ {
+ ClearSchedules( subscene );
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If we are currently interruptable, pause this scene and wait for the other
+// scene to finish
+// Input : *otherScene -
+//-----------------------------------------------------------------------------
+bool CSceneEntity::InterruptThisScene( CSceneEntity *otherScene )
+{
+ Assert( otherScene );
+
+ if ( !IsInterruptable() )
+ {
+ return false;
+ }
+
+ // Already interrupted
+ if ( m_bInterrupted )
+ {
+ return false;
+ }
+
+ m_bInterrupted = true;
+ m_hInterruptScene = otherScene;
+
+ // Ask other scene to tell us when it's finished or canceled
+ otherScene->RequestCompletionNotification( this );
+
+ PausePlayback();
+ return true;
+}
+
+/*
+void scene_interrupt( const CCommand &args )
+{
+ if ( args.ArgC() != 3 )
+ return;
+
+ const char *scene1 = args[1];
+ const char *scene2 = args[2];
+
+ CSceneEntity *s1 = dynamic_cast< CSceneEntity * >( gEntList.FindEntityByName( NULL, scene1 ) );
+ CSceneEntity *s2 = dynamic_cast< CSceneEntity * >( gEntList.FindEntityByName( NULL, scene2 ) );
+
+ if ( !s1 || !s2 )
+ return;
+
+ if ( s1->InterruptThisScene( s2 ) )
+ {
+ s2->StartPlayback();
+ }
+}
+
+static ConCommand interruptscene( "int", scene_interrupt, "interrupt scene 1 with scene 2.", FCVAR_CHEAT );
+*/
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::CheckInterruptCompletion()
+{
+ if ( !m_bInterrupted )
+ return;
+
+ // If the interruptor goes away it's the same as having that scene finish up...
+ if ( m_hInterruptScene != NULL &&
+ !m_bInterruptSceneFinished )
+ {
+ return;
+ }
+
+ m_bInterrupted = false;
+ m_hInterruptScene = NULL;
+
+ ResumePlayback();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::ClearInterrupt()
+{
+ m_nInterruptCount = 0;
+ m_bInterrupted = false;
+ m_hInterruptScene = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Another scene is asking us to notify upon completion
+// Input : *notify -
+//-----------------------------------------------------------------------------
+void CSceneEntity::RequestCompletionNotification( CSceneEntity *notify )
+{
+ CHandle< CSceneEntity > h;
+ h = notify;
+ // Only add it once
+ if ( m_hNotifySceneCompletion.Find( h ) == m_hNotifySceneCompletion.InvalidIndex() )
+ {
+ m_hNotifySceneCompletion.AddToTail( h );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: An interrupt scene has finished or been canceled, we can resume once we pick up this state in CheckInterruptCompletion
+// Input : *interruptor -
+//-----------------------------------------------------------------------------
+void CSceneEntity::NotifyOfCompletion( CSceneEntity *interruptor )
+{
+ Assert( m_bInterrupted );
+ Assert( m_hInterruptScene == interruptor );
+ m_bInterruptSceneFinished = true;
+
+ CheckInterruptCompletion();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneEntity::AddListManager( CSceneListManager *pManager )
+{
+ CHandle< CSceneListManager > h;
+ h = pManager;
+ // Only add it once
+ if ( m_hListManagers.Find( h ) == m_hListManagers.InvalidIndex() )
+ {
+ m_hListManagers.AddToTail( h );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Clear any targets that a referencing !activator
+//-----------------------------------------------------------------------------
+void CSceneEntity::ClearActivatorTargets( void )
+{
+ if ( !stricmp( STRING(m_iszTarget1), "!activator" ) )
+ {
+ // We need to clear out actors so they're re-evaluated
+ m_hActorList.Purge();
+ NetworkProp()->NetworkStateForceUpdate();
+ m_hTarget1 = NULL;
+ }
+ if ( !stricmp( STRING(m_iszTarget2), "!activator" ) )
+ {
+ // We need to clear out actors so they're re-evaluated
+ m_hActorList.Purge();
+ NetworkProp()->NetworkStateForceUpdate();
+ m_hTarget2 = NULL;
+ }
+ if ( !stricmp( STRING(m_iszTarget3), "!activator" ) )
+ {
+ // We need to clear out actors so they're re-evaluated
+ m_hActorList.Purge();
+ NetworkProp()->NetworkStateForceUpdate();
+ m_hTarget3 = NULL;
+ }
+ if ( !stricmp( STRING(m_iszTarget4), "!activator" ) )
+ {
+ // We need to clear out actors so they're re-evaluated
+ m_hActorList.Purge();
+ NetworkProp()->NetworkStateForceUpdate();
+ m_hTarget4 = NULL;
+ }
+ if ( !stricmp( STRING(m_iszTarget5), "!activator" ) )
+ {
+ // We need to clear out actors so they're re-evaluated
+ m_hActorList.Purge();
+ NetworkProp()->NetworkStateForceUpdate();
+ m_hTarget5 = NULL;
+ }
+ if ( !stricmp( STRING(m_iszTarget6), "!activator" ) )
+ {
+ // We need to clear out actors so they're re-evaluated
+ m_hActorList.Purge();
+ NetworkProp()->NetworkStateForceUpdate();
+ m_hTarget6 = NULL;
+ }
+ if ( !stricmp( STRING(m_iszTarget7), "!activator" ) )
+ {
+ // We need to clear out actors so they're re-evaluated
+ m_hActorList.Purge();
+ NetworkProp()->NetworkStateForceUpdate();
+ m_hTarget7 = NULL;
+ }
+ if ( !stricmp( STRING(m_iszTarget8), "!activator" ) )
+ {
+ // We need to clear out actors so they're re-evaluated
+ m_hActorList.Purge();
+ NetworkProp()->NetworkStateForceUpdate();
+ m_hTarget8 = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a scene is completed or canceled
+//-----------------------------------------------------------------------------
+void CSceneEntity::OnSceneFinished( bool canceled, bool fireoutput )
+{
+ if ( !m_pScene )
+ return;
+
+ LocalScene_Printf( "%s : %8.2f: finished\n", STRING( m_iszSceneFile ), m_flCurrentTime );
+
+ // Notify any listeners
+ int c = m_hNotifySceneCompletion.Count();
+ int i;
+ for ( i = 0; i < c; i++ )
+ {
+ CSceneEntity *ent = m_hNotifySceneCompletion[ i ].Get();
+ if ( !ent )
+ continue;
+
+ ent->NotifyOfCompletion( this );
+ }
+ m_hNotifySceneCompletion.RemoveAll();
+
+ // Clear simulation
+ m_pScene->ResetSimulation();
+ m_bIsPlayingBack = false;
+ m_bPaused = false;
+ SetCurrentTime( 0.0f, false );
+
+ // Clear interrupt state if we were interrupted for some reason
+ ClearInterrupt();
+
+ if ( fireoutput && !m_bCompletedEarly)
+ {
+ m_OnCompletion.FireOutput( this, this, 0 );
+ }
+
+ // Put face back in neutral pose
+ ClearSceneEvents( m_pScene, canceled );
+
+ for ( i = 0 ; i < m_pScene->GetNumActors(); i++ )
+ {
+ CBaseFlex *pTestActor = FindNamedActor( i );
+
+ if ( !pTestActor )
+ continue;
+
+ pTestActor->RemoveChoreoScene( m_pScene, canceled );
+
+ // If we interrupted the actor's previous scenes, resume them
+ if ( m_bInterruptedActorsScenes )
+ {
+ QueueActorsScriptedScenesToResume( pTestActor, false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Should we transmit it to the client?
+//-----------------------------------------------------------------------------
+int CSceneEntity::UpdateTransmitState()
+{
+ if ( !ShouldNetwork() )
+ {
+ return SetTransmitState( FL_EDICT_DONTSEND );
+ }
+
+ if ( m_pRecipientFilter )
+ {
+ return SetTransmitState( FL_EDICT_FULLCHECK );
+ }
+
+ return SetTransmitState( FL_EDICT_ALWAYS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Which clients should we be transmitting to?
+//-----------------------------------------------------------------------------
+int CSceneEntity::ShouldTransmit( const CCheckTransmitInfo *pInfo )
+{
+ int result = BaseClass::ShouldTransmit( pInfo );
+
+ // if we have excluded them via our recipient filter, don't send
+ if ( m_pRecipientFilter && result != FL_EDICT_DONTSEND )
+ {
+ bool bFound = false;
+
+ // If we can't find them in the recipient list, exclude
+ int i;
+ for ( i=0; i<m_pRecipientFilter->GetRecipientCount();i++ )
+ {
+ int iRecipient = m_pRecipientFilter->GetRecipientIndex(i);
+
+ CBasePlayer *player = static_cast< CBasePlayer * >( CBaseEntity::Instance( iRecipient ) );
+
+ if ( player && player->edict() == pInfo->m_pClientEnt )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if ( !bFound )
+ {
+ result = FL_EDICT_DONTSEND;
+ }
+ }
+
+ return result;
+}
+
+void CSceneEntity::SetRecipientFilter( IRecipientFilter *filter )
+{
+ // create a copy of this filter
+ if ( filter )
+ {
+ m_pRecipientFilter = new CRecipientFilter();
+ m_pRecipientFilter->CopyFrom( (CRecipientFilter &)( *filter ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+class CInstancedSceneEntity : public CSceneEntity
+{
+ DECLARE_DATADESC();
+ DECLARE_CLASS( CInstancedSceneEntity, CSceneEntity );
+public:
+ EHANDLE m_hOwner;
+ bool m_bHadOwner;
+ float m_flPostSpeakDelay;
+ float m_flPreDelay;
+ char m_szInstanceFilename[ CChoreoScene::MAX_SCENE_FILENAME ];
+ bool m_bIsBackground;
+
+ virtual void StartPlayback( void );
+ virtual void DoThink( float frametime );
+ virtual CBaseFlex *FindNamedActor( const char *name );
+ virtual CBaseEntity *FindNamedEntity( const char *name );
+ virtual float GetPostSpeakDelay() { return m_flPostSpeakDelay; }
+ virtual void SetPostSpeakDelay( float flDelay ) { m_flPostSpeakDelay = flDelay; }
+ virtual float GetPreDelay() { return m_flPreDelay; }
+ virtual void SetPreDelay( float flDelay ) { m_flPreDelay = flDelay; }
+
+ virtual void OnLoaded();
+
+ virtual void DispatchStartMoveTo( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
+ {
+ if (PassThrough( actor )) BaseClass::DispatchStartMoveTo( scene, actor, actor2, event );
+ };
+
+ virtual void DispatchEndMoveTo( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+ {
+ if (PassThrough( actor )) BaseClass::DispatchEndMoveTo( scene, actor, event );
+ };
+
+ virtual void DispatchStartFace( CChoreoScene *scene, CBaseFlex *actor, CBaseEntity *actor2, CChoreoEvent *event )
+ {
+ if (PassThrough( actor )) BaseClass::DispatchStartFace( scene, actor, actor2, event );
+ };
+
+ virtual void DispatchEndFace( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+ {
+ if (PassThrough( actor )) BaseClass::DispatchEndFace( scene, actor, event );
+ };
+
+ virtual void DispatchStartSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+ {
+ if ( IsMultiplayer() )
+ {
+ BaseClass::DispatchStartSequence( scene, actor, event );
+ }
+ };
+ virtual void DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event )
+ {
+ if ( IsMultiplayer() )
+ {
+ BaseClass::DispatchEndSequence( scene, actor, event );
+ }
+ };
+ virtual void DispatchPauseScene( CChoreoScene *scene, const char *parameters ) { /* suppress */ };
+
+ void OnRestore();
+
+ virtual float EstimateLength( void );
+
+private:
+ bool PassThrough( CBaseFlex *actor );
+};
+
+LINK_ENTITY_TO_CLASS( instanced_scripted_scene, CInstancedSceneEntity );
+
+//---------------------------------------------------------
+// Save/Restore
+//---------------------------------------------------------
+BEGIN_DATADESC( CInstancedSceneEntity )
+
+ DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bHadOwner, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flPostSpeakDelay, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flPreDelay, FIELD_FLOAT ),
+ DEFINE_AUTO_ARRAY( m_szInstanceFilename, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_bIsBackground, FIELD_BOOLEAN ),
+
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose: create a one-shot scene, no movement, sequences, etc.
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+float InstancedScriptedScene( CBaseFlex *pActor, const char *pszScene, EHANDLE *phSceneEnt,
+ float flPostDelay, bool bIsBackground, AI_Response *response,
+ bool bMultiplayer, IRecipientFilter *filter /* = NULL */ )
+{
+ VPROF( "InstancedScriptedScene" );
+
+ CInstancedSceneEntity *pScene = (CInstancedSceneEntity *)CBaseEntity::CreateNoSpawn( "instanced_scripted_scene", vec3_origin, vec3_angle );
+
+ // This code expands any $gender tags into male or female tags based on the gender of the actor (based on his/her .mdl)
+ if ( pActor )
+ {
+ pActor->GenderExpandString( pszScene, pScene->m_szInstanceFilename, sizeof( pScene->m_szInstanceFilename ) );
+ }
+ else
+ {
+ Q_strncpy( pScene->m_szInstanceFilename, pszScene, sizeof( pScene->m_szInstanceFilename ) );
+ }
+ pScene->m_iszSceneFile = MAKE_STRING( pScene->m_szInstanceFilename );
+
+ // FIXME: I should set my output to fire something that kills me....
+
+ // FIXME: add a proper initialization function
+ pScene->m_hOwner = pActor;
+ pScene->m_bHadOwner = pActor != NULL;
+ pScene->m_bMultiplayer = bMultiplayer;
+ pScene->SetPostSpeakDelay( flPostDelay );
+ DispatchSpawn( pScene );
+ pScene->Activate();
+ pScene->m_bIsBackground = bIsBackground;
+
+ pScene->SetBackground( bIsBackground );
+ pScene->SetRecipientFilter( filter );
+
+ if ( response )
+ {
+ float flPreDelay = response->GetPreDelay();
+ if ( flPreDelay )
+ {
+ pScene->SetPreDelay( flPreDelay );
+ }
+ }
+
+ pScene->StartPlayback();
+
+ if ( response )
+ {
+ // If the response wants us to abort on NPC state switch, remember that
+ pScene->SetBreakOnNonIdle( response->ShouldBreakOnNonIdle() );
+ }
+
+ if ( phSceneEnt )
+ {
+ *phSceneEnt = pScene;
+ }
+
+ return pScene->EstimateLength();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pActor -
+// *soundnmame -
+// *phSceneEnt -
+// Output : float
+//-----------------------------------------------------------------------------
+float InstancedAutoGeneratedSoundScene( CBaseFlex *pActor, char const *soundname, EHANDLE *phSceneEnt /*= NULL*/ )
+{
+ if ( !pActor )
+ {
+ Warning( "InstancedAutoGeneratedSoundScene: Expecting non-NULL pActor for sound %s\n", soundname );
+ return 0;
+ }
+
+ CInstancedSceneEntity *pScene = (CInstancedSceneEntity *)CBaseEntity::CreateNoSpawn( "instanced_scripted_scene", vec3_origin, vec3_angle );
+
+ Q_strncpy( pScene->m_szInstanceFilename, UTIL_VarArgs( "AutoGenerated(%s)", soundname ), sizeof( pScene->m_szInstanceFilename ) );
+ pScene->m_iszSceneFile = MAKE_STRING( pScene->m_szInstanceFilename );
+
+ pScene->m_hOwner = pActor;
+ pScene->m_bHadOwner = pActor != NULL;
+
+ pScene->GenerateSoundScene( pActor, soundname );
+
+ pScene->Spawn();
+ pScene->Activate();
+ pScene->StartPlayback();
+
+ if ( phSceneEnt )
+ {
+ *phSceneEnt = pScene;
+ }
+
+ return pScene->EstimateLength();
+}
+
+//-----------------------------------------------------------------------------
+
+void StopScriptedScene( CBaseFlex *pActor, EHANDLE hSceneEnt )
+{
+ CBaseEntity *pEntity = hSceneEnt;
+ CSceneEntity *pScene = dynamic_cast<CSceneEntity *>(pEntity);
+
+ if ( pScene )
+ {
+ LocalScene_Printf( "%s : stop scripted scene\n", STRING( pScene->m_iszSceneFile ) );
+ pScene->CancelPlayback();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pszScene -
+// Output : float
+//-----------------------------------------------------------------------------
+float GetSceneDuration( char const *pszScene )
+{
+ unsigned int msecs = 0;
+
+ SceneCachedData_t cachedData;
+ if ( scenefilecache->GetSceneCachedData( pszScene, &cachedData ) )
+ {
+ msecs = cachedData.msecs;
+ }
+
+ return (float)msecs * 0.001f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pszScene -
+// Output : int
+//-----------------------------------------------------------------------------
+int GetSceneSpeechCount( char const *pszScene )
+{
+ SceneCachedData_t cachedData;
+ if ( scenefilecache->GetSceneCachedData( pszScene, &cachedData ) )
+ {
+ return cachedData.numSounds;
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used for precaching instanced scenes
+// Input : *pszScene -
+//-----------------------------------------------------------------------------
+void PrecacheInstancedScene( char const *pszScene )
+{
+ static int nMakingReslists = -1;
+
+ if ( nMakingReslists == -1 )
+ {
+ nMakingReslists = CommandLine()->FindParm( "-makereslists" ) > 0 ? 1 : 0;
+ }
+
+ if ( nMakingReslists == 1 )
+ {
+ // Just stat the file to add to reslist
+ g_pFullFileSystem->Size( pszScene );
+ }
+
+ // verify existence, cache is pre-populated, should be there
+ SceneCachedData_t sceneData;
+ if ( !scenefilecache->GetSceneCachedData( pszScene, &sceneData ) )
+ {
+ // Scenes are sloppy and don't always exist.
+ // A scene that is not in the pre-built cache image, but on disk, is a true error.
+ if ( developer.GetInt() && ( IsX360() && ( g_pFullFileSystem->GetDVDMode() != DVDMODE_STRICT ) && g_pFullFileSystem->FileExists( pszScene, "GAME" ) ) )
+ {
+ Warning( "PrecacheInstancedScene: Missing scene '%s' from scene image cache.\nRebuild scene image cache!\n", pszScene );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < sceneData.numSounds; ++i )
+ {
+ short stringId = scenefilecache->GetSceneCachedSound( sceneData.sceneId, i );
+ CBaseEntity::PrecacheScriptSound( scenefilecache->GetSceneString( stringId ) );
+ }
+ }
+
+ g_pStringTableClientSideChoreoScenes->AddString( CBaseEntity::IsServer(), pszScene );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CInstancedSceneEntity::StartPlayback( void )
+{
+ // Wait until our pre delay is over
+ if ( GetPreDelay() )
+ return;
+
+ BaseClass::StartPlayback();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CInstancedSceneEntity::DoThink( float frametime )
+{
+ CheckInterruptCompletion();
+
+ if ( m_flPreDelay > 0 )
+ {
+ m_flPreDelay = MAX( 0, m_flPreDelay - frametime );
+ StartPlayback();
+ if ( !m_bIsPlayingBack )
+ return;
+ }
+
+ if ( !m_pScene || !m_bIsPlayingBack || ( m_bHadOwner && m_hOwner == NULL ) )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ // catch bad pitch shifting from old save games
+ Assert( m_fPitch >= SCENE_MIN_PITCH && m_fPitch <= SCENE_MAX_PITCH );
+ m_fPitch = clamp( m_fPitch, SCENE_MIN_PITCH, SCENE_MAX_PITCH );
+
+ if ( m_bPaused )
+ {
+ PauseThink();
+ return;
+ }
+
+ float dt = frametime;
+
+ m_pScene->SetSoundFileStartupLatency( GetSoundSystemLatency() );
+
+ // Tell scene to go
+ m_pScene->Think( m_flCurrentTime );
+ // Drive simulation time for scene
+ SetCurrentTime( m_flCurrentTime + dt * m_fPitch, false );
+
+ // Did we get to the end
+ if ( m_pScene->SimulationFinished() )
+ {
+ OnSceneFinished( false, false );
+
+ UTIL_Remove( this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Search for an actor by name, make sure it can do face poses
+// Input : *name -
+// Output : CBaseFlex
+//-----------------------------------------------------------------------------
+CBaseFlex *CInstancedSceneEntity::FindNamedActor( const char *name )
+{
+ if ( m_pScene->GetNumActors() == 1 || stricmp( name, "!self" ) == 0 )
+ {
+ if ( m_hOwner != NULL )
+ {
+ CBaseCombatCharacter *pCharacter = m_hOwner->MyCombatCharacterPointer();
+ if ( pCharacter )
+ {
+ return pCharacter;
+ }
+ }
+ }
+ return BaseClass::FindNamedActor( name );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Search for an actor by name, make sure it can do face poses
+// Input : *name -
+// Output : CBaseFlex
+//-----------------------------------------------------------------------------
+CBaseEntity *CInstancedSceneEntity::FindNamedEntity( const char *name )
+{
+ CBaseEntity *pOther = NULL;
+
+ if (m_hOwner != NULL)
+ {
+ CAI_BaseNPC *npc = m_hOwner->MyNPCPointer();
+
+ if (npc)
+ {
+ pOther = npc->FindNamedEntity( name );
+ }
+ else if ( m_hOwner->MyCombatCharacterPointer() )
+ {
+ pOther = m_hOwner;
+ }
+ }
+
+ if (!pOther)
+ {
+ pOther = BaseClass::FindNamedEntity( name );
+ }
+ return pOther;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Suppress certain events when it's instanced since they're can cause odd problems
+// Input : actor
+// Output : true - the event should happen, false - it shouldn't
+//-----------------------------------------------------------------------------
+
+bool CInstancedSceneEntity::PassThrough( CBaseFlex *actor )
+{
+ if (!actor)
+ return false;
+
+ CAI_BaseNPC *myNpc = actor->MyNPCPointer( );
+
+ if (!myNpc)
+ return false;
+
+ if (myNpc->IsCurSchedule( SCHED_SCENE_GENERIC ))
+ {
+ return true;
+ }
+
+ if (myNpc->GetCurSchedule())
+ {
+ CAI_ScheduleBits testBits;
+ myNpc->GetCurSchedule()->GetInterruptMask( &testBits );
+
+ if (testBits.IsBitSet( COND_IDLE_INTERRUPT ))
+ {
+ return true;
+ }
+ }
+
+ LocalScene_Printf( "%s : event suppressed\n", STRING( m_iszSceneFile ) );
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+void CInstancedSceneEntity::OnRestore()
+{
+ if ( m_bHadOwner && !m_hOwner )
+ {
+ // probably just came back from a level transition
+ UTIL_Remove( this );
+ return;
+ }
+ // reset background state
+ if ( m_pScene )
+ {
+ m_pScene->SetBackground( m_bIsBackground );
+ }
+ BaseClass::OnRestore();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CInstancedSceneEntity::EstimateLength( void )
+{
+ return (BaseClass::EstimateLength() + GetPreDelay());
+}
+
+
+void CInstancedSceneEntity::OnLoaded()
+{
+ BaseClass::OnLoaded();
+ SetBackground( m_bIsBackground );
+}
+
+bool g_bClientFlex = true;
+
+LINK_ENTITY_TO_CLASS( scene_manager, CSceneManager );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneManager::Think()
+{
+ // Latch this only once per frame...
+ g_bClientFlex = scene_clientflex.GetBool();
+
+ // The manager is always thinking at 20 hz
+ SetNextThink( gpGlobals->curtime + SCENE_THINK_INTERVAL );
+ float frameTime = ( gpGlobals->curtime - GetLastThink() );
+ frameTime = MIN( 0.1, frameTime );
+
+ // stop if AI is diabled
+ if (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
+ return;
+
+ bool needCleanupPass = false;
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *scene = m_ActiveScenes[ i ].Get();
+ if ( !scene )
+ {
+ needCleanupPass = true;
+ continue;
+ }
+
+ scene->DoThink( frameTime );
+
+ if ( m_ActiveScenes.Count() < c )
+ {
+ // Scene removed self while thinking. Adjust iteration.
+ c = m_ActiveScenes.Count();
+ i--;
+ }
+ }
+
+ // Now delete any invalid ones
+ if ( needCleanupPass )
+ {
+ for ( int i = c - 1; i >= 0; i-- )
+ {
+ CSceneEntity *scene = m_ActiveScenes[ i ].Get();
+ if ( scene )
+ continue;
+
+ m_ActiveScenes.Remove( i );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneManager::ClearAllScenes()
+{
+ m_ActiveScenes.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+//-----------------------------------------------------------------------------
+void CSceneManager::AddSceneEntity( CSceneEntity *scene )
+{
+ CHandle< CSceneEntity > h;
+
+ h = scene;
+
+ // Already added/activated
+ if ( m_ActiveScenes.Find( h ) != m_ActiveScenes.InvalidIndex() )
+ {
+ return;
+ }
+
+ m_ActiveScenes.AddToTail( h );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *scene -
+//-----------------------------------------------------------------------------
+void CSceneManager::RemoveSceneEntity( CSceneEntity *scene )
+{
+ CHandle< CSceneEntity > h;
+
+ h = scene;
+
+ m_ActiveScenes.FindAndRemove( h );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *player -
+//-----------------------------------------------------------------------------
+void CSceneManager::OnClientActive( CBasePlayer *player )
+{
+ int c = m_QueuedSceneSounds.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CRestoreSceneSound *sound = &m_QueuedSceneSounds[ i ];
+
+ if ( sound->actor == NULL )
+ continue;
+
+ // Blow off sounds too far in past to encode over networking layer
+ if ( fabs( 1000.0f * sound->time_in_past ) > MAX_SOUND_DELAY_MSEC )
+ continue;
+
+ CPASAttenuationFilter filter( sound->actor );
+
+ EmitSound_t es;
+ es.m_nChannel = CHAN_VOICE;
+ es.m_flVolume = 1;
+ es.m_pSoundName = sound->soundname;
+ es.m_SoundLevel = sound->soundlevel;
+ es.m_flSoundTime = gpGlobals->curtime - sound->time_in_past;
+
+ EmitSound( filter, sound->actor->entindex(), es );
+ }
+
+ m_QueuedSceneSounds.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deletes scenes involving the specified actor
+//-----------------------------------------------------------------------------
+void CSceneManager::RemoveScenesInvolvingActor( CBaseFlex *pActor )
+{
+ if ( !pActor )
+ return;
+
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene )
+ {
+ continue;
+ }
+
+ if ( pScene->InvolvesActor( pActor ) ) // NOTE: returns false if scene hasn't loaded yet
+ {
+ LocalScene_Printf( "%s : removed for '%s'\n", STRING( pScene->m_iszSceneFile ), pActor ? pActor->GetDebugName() : "NULL" );
+ pScene->CancelPlayback();
+ }
+ else
+ {
+ CInstancedSceneEntity *pInstancedScene = dynamic_cast< CInstancedSceneEntity * >( pScene );
+ if ( pInstancedScene && pInstancedScene->m_hOwner )
+ {
+ if ( pInstancedScene->m_hOwner == pActor )
+ {
+ if ( pInstancedScene->m_bIsPlayingBack )
+ {
+ pInstancedScene->OnSceneFinished( true, false );
+ }
+
+ LocalScene_Printf( "%s : removed for '%s'\n", STRING( pInstancedScene->m_iszSceneFile ), pActor ? pActor->GetDebugName() : "NULL" );
+ UTIL_Remove( pInstancedScene );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stops scenes involving the specified actor
+//-----------------------------------------------------------------------------
+void CSceneManager::RemoveActorFromScenes( CBaseFlex *pActor, bool bInstancedOnly, bool bNonIdleOnly, const char *pszThisSceneOnly )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene )
+ {
+ continue;
+ }
+
+ // If only stopping instanced scenes, then skip it if it can't cast to an instanced scene
+ if ( bInstancedOnly &&
+ ( dynamic_cast< CInstancedSceneEntity * >( pScene ) == NULL ) )
+ {
+ continue;
+ }
+
+ if ( bNonIdleOnly && !pScene->ShouldBreakOnNonIdle() )
+ continue;
+
+ if ( pScene->InvolvesActor( pActor ) )
+ {
+ if ( pszThisSceneOnly && pszThisSceneOnly[0] )
+ {
+ if ( Q_strcmp( pszThisSceneOnly, STRING(pScene->m_iszSceneFile) ) )
+ continue;
+ }
+
+ LocalScene_Printf( "%s : removed for '%s'\n", STRING( pScene->m_iszSceneFile ), pActor ? pActor->GetDebugName() : "NULL" );
+ pScene->CancelPlayback();
+ }
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pause scenes involving the specified actor
+//-----------------------------------------------------------------------------
+void CSceneManager::PauseActorsScenes( CBaseFlex *pActor, bool bInstancedOnly )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene )
+ {
+ continue;
+ }
+
+ // If only stopping instanced scenes, then skip it if it can't cast to an instanced scene
+ if ( bInstancedOnly &&
+ ( dynamic_cast< CInstancedSceneEntity * >( pScene ) == NULL ) )
+ {
+ continue;
+ }
+
+ if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() )
+ {
+ LocalScene_Printf( "Pausing actor %s scripted scene: %s\n", pActor->GetDebugName(), STRING(pScene->m_iszSceneFile) );
+
+ variant_t emptyVariant;
+ pScene->AcceptInput( "Pause", pScene, pScene, emptyVariant, 0 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this Actor is only in scenes that are interruptable right now
+//-----------------------------------------------------------------------------
+bool CSceneManager::IsInInterruptableScenes( CBaseFlex *pActor )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene )
+ continue;
+
+ //Ignore background scenes since they're harmless.
+ if ( pScene->IsBackground() == true )
+ continue;
+
+ if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() )
+ {
+ if ( pScene->IsInterruptable() == false )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resume any paused scenes involving the specified actor
+//-----------------------------------------------------------------------------
+void CSceneManager::ResumeActorsScenes( CBaseFlex *pActor, bool bInstancedOnly )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene )
+ {
+ continue;
+ }
+
+ // If only stopping instanced scenes, then skip it if it can't cast to an instanced scene
+ if ( bInstancedOnly &&
+ ( dynamic_cast< CInstancedSceneEntity * >( pScene ) == NULL ) )
+ {
+ continue;
+ }
+
+ if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() )
+ {
+ LocalScene_Printf( "Resuming actor %s scripted scene: %s\n", pActor->GetDebugName(), STRING(pScene->m_iszSceneFile) );
+
+ variant_t emptyVariant;
+ pScene->AcceptInput( "Resume", pScene, pScene, emptyVariant, 0 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set all paused, in-playback scenes to resume when the actor is ready
+//-----------------------------------------------------------------------------
+void CSceneManager::QueueActorsScenesToResume( CBaseFlex *pActor, bool bInstancedOnly )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene )
+ {
+ continue;
+ }
+
+ // If only stopping instanced scenes, then skip it if it can't cast to an instanced scene
+ if ( bInstancedOnly &&
+ ( dynamic_cast< CInstancedSceneEntity * >( pScene ) == NULL ) )
+ {
+ continue;
+ }
+
+ if ( pScene->InvolvesActor( pActor ) && pScene->IsPlayingBack() && pScene->IsPaused() )
+ {
+ pScene->QueueResumePlayback();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns if there are scenes involving the specified actor
+//-----------------------------------------------------------------------------
+bool CSceneManager::IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene ||
+ !pScene->IsPlayingBack() ||
+ ( bIgnoreInstancedScenes && dynamic_cast<CInstancedSceneEntity *>(pScene) != NULL )
+ )
+ {
+ continue;
+ }
+
+ if ( pScene->InvolvesActor( pActor ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CSceneManager::IsRunningScriptedSceneAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene ||
+ !pScene->IsPlayingBack() ||
+ pScene->IsPaused() ||
+ ( bIgnoreInstancedScenes && dynamic_cast<CInstancedSceneEntity *>(pScene) != NULL )
+ )
+ {
+ continue;
+ }
+
+ if ( pScene->InvolvesActor( pActor ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pActor -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CSceneManager::IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene ||
+ !pScene->IsPlayingBack() ||
+ ( bIgnoreInstancedScenes && dynamic_cast<CInstancedSceneEntity *>(pScene) != NULL )
+ )
+ {
+ continue;
+ }
+
+ if ( pScene->InvolvesActor( pActor ) )
+ {
+ if ( pScene->HasUnplayedSpeech() )
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool CSceneManager::IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
+{
+ int c = m_ActiveScenes.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CSceneEntity *pScene = m_ActiveScenes[ i ].Get();
+ if ( !pScene ||
+ !pScene->IsPlayingBack() ||
+ pScene->IsPaused() ||
+ ( bIgnoreInstancedScenes && dynamic_cast<CInstancedSceneEntity *>(pScene) != NULL )
+ )
+ {
+ continue;
+ }
+
+ if ( pScene->InvolvesActor( pActor ) )
+ {
+ if ( pScene->HasUnplayedSpeech() )
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *soundname -
+// soundlevel -
+// soundtime -
+//-----------------------------------------------------------------------------
+void CSceneManager::QueueRestoredSound( CBaseFlex *actor, char const *soundname, soundlevel_t soundlevel, float time_in_past )
+{
+ CRestoreSceneSound e;
+ e.actor = actor;
+ Q_strncpy( e.soundname, soundname, sizeof( e.soundname ) );
+ e.soundlevel = soundlevel;
+ e.time_in_past = time_in_past;
+
+ m_QueuedSceneSounds.AddToTail( e );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void RemoveActorFromScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly, bool nonidlescenesonly, const char *pszThisSceneOnly )
+{
+ GetSceneManager()->RemoveActorFromScenes( pActor, instancedscenesonly, nonidlescenesonly, pszThisSceneOnly );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void RemoveAllScenesInvolvingActor( CBaseFlex *pActor )
+{
+ GetSceneManager()->RemoveScenesInvolvingActor( pActor );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void PauseActorsScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly )
+{
+ GetSceneManager()->PauseActorsScenes( pActor, instancedscenesonly );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool IsInInterruptableScenes( CBaseFlex *pActor )
+{
+ return GetSceneManager()->IsInInterruptableScenes( pActor );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void ResumeActorsScriptedScenes( CBaseFlex *pActor, bool instancedscenesonly )
+{
+ GetSceneManager()->ResumeActorsScenes( pActor, instancedscenesonly );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void QueueActorsScriptedScenesToResume( CBaseFlex *pActor, bool instancedscenesonly )
+{
+ GetSceneManager()->QueueActorsScenesToResume( pActor, instancedscenesonly );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool IsRunningScriptedScene( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
+{
+ return GetSceneManager()->IsRunningScriptedScene( pActor, bIgnoreInstancedScenes );
+}
+
+bool IsRunningScriptedSceneAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
+{
+ return GetSceneManager()->IsRunningScriptedSceneAndNotPaused( pActor, bIgnoreInstancedScenes );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool IsRunningScriptedSceneWithSpeech( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
+{
+ return GetSceneManager()->IsRunningScriptedSceneWithSpeech( pActor, bIgnoreInstancedScenes );
+}
+
+bool IsRunningScriptedSceneWithSpeechAndNotPaused( CBaseFlex *pActor, bool bIgnoreInstancedScenes )
+{
+ return GetSceneManager()->IsRunningScriptedSceneWithSpeechAndNotPaused( pActor, bIgnoreInstancedScenes );
+}
+
+
+//===========================================================================================================
+// SCENE LIST MANAGER
+//===========================================================================================================
+LINK_ENTITY_TO_CLASS( logic_scene_list_manager, CSceneListManager );
+
+BEGIN_DATADESC( CSceneListManager )
+ DEFINE_UTLVECTOR( m_hListManagers, FIELD_EHANDLE ),
+
+ // Keys
+ DEFINE_KEYFIELD( m_iszScenes[0], FIELD_STRING, "scene0" ),
+ DEFINE_KEYFIELD( m_iszScenes[1], FIELD_STRING, "scene1" ),
+ DEFINE_KEYFIELD( m_iszScenes[2], FIELD_STRING, "scene2" ),
+ DEFINE_KEYFIELD( m_iszScenes[3], FIELD_STRING, "scene3" ),
+ DEFINE_KEYFIELD( m_iszScenes[4], FIELD_STRING, "scene4" ),
+ DEFINE_KEYFIELD( m_iszScenes[5], FIELD_STRING, "scene5" ),
+ DEFINE_KEYFIELD( m_iszScenes[6], FIELD_STRING, "scene6" ),
+ DEFINE_KEYFIELD( m_iszScenes[7], FIELD_STRING, "scene7" ),
+ DEFINE_KEYFIELD( m_iszScenes[8], FIELD_STRING, "scene8" ),
+ DEFINE_KEYFIELD( m_iszScenes[9], FIELD_STRING, "scene9" ),
+ DEFINE_KEYFIELD( m_iszScenes[10], FIELD_STRING, "scene10" ),
+ DEFINE_KEYFIELD( m_iszScenes[11], FIELD_STRING, "scene11" ),
+ DEFINE_KEYFIELD( m_iszScenes[12], FIELD_STRING, "scene12" ),
+ DEFINE_KEYFIELD( m_iszScenes[13], FIELD_STRING, "scene13" ),
+ DEFINE_KEYFIELD( m_iszScenes[14], FIELD_STRING, "scene14" ),
+ DEFINE_KEYFIELD( m_iszScenes[15], FIELD_STRING, "scene15" ),
+
+ DEFINE_FIELD( m_hScenes[0], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[1], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[2], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[3], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[4], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[5], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[6], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[7], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[8], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[9], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[10], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[11], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[12], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[13], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[14], FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hScenes[15], FIELD_EHANDLE ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_VOID, "Shutdown", InputShutdown ),
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneListManager::Activate( void )
+{
+ BaseClass::Activate();
+
+ // Hook up scenes, but not after loading a game because they're saved.
+ if ( gpGlobals->eLoadType != MapLoad_LoadGame )
+ {
+ for ( int i = 0; i < SCENE_LIST_MANAGER_MAX_SCENES; i++ )
+ {
+ if ( m_iszScenes[i] != NULL_STRING )
+ {
+ m_hScenes[i] = gEntList.FindEntityByName( NULL, STRING(m_iszScenes[i]) );
+ if ( m_hScenes[i] )
+ {
+ CSceneEntity *pScene = dynamic_cast<CSceneEntity*>(m_hScenes[i].Get());
+ if ( pScene )
+ {
+ pScene->AddListManager( this );
+ }
+ else
+ {
+ CSceneListManager *pList = dynamic_cast<CSceneListManager*>(m_hScenes[i].Get());
+ if ( pList )
+ {
+ pList->AddListManager( this );
+ }
+ else
+ {
+ Warning( "%s(%s) found an entity that wasn't a logic_choreographed_scene or logic_scene_list_manager in slot %d, named %s\n", GetDebugName(), GetClassname(), i, STRING(m_iszScenes[i]) );
+ m_hScenes[i] = NULL;
+ }
+ }
+ }
+ else
+ {
+ Warning( "%s(%s) could not find scene %d, named %s\n", GetDebugName(), GetClassname(), i, STRING(m_iszScenes[i]) );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: A scene or manager in our list has started playing.
+// Remove all scenes earlier in the list.
+//-----------------------------------------------------------------------------
+void CSceneListManager::SceneStarted( CBaseEntity *pSceneOrManager )
+{
+ // Move backwards and call remove on all scenes / managers earlier in the list to the fired one
+ bool bFoundStart = false;
+ for ( int i = SCENE_LIST_MANAGER_MAX_SCENES-1; i >= 0; i-- )
+ {
+ if ( !m_hScenes[i] )
+ continue;
+
+ if ( bFoundStart )
+ {
+ RemoveScene( i );
+ }
+ else if ( m_hScenes[i] == pSceneOrManager )
+ {
+ bFoundStart = true;
+ }
+ }
+
+ // Tell any managers we're within that we've started a scene
+ if ( bFoundStart )
+ {
+ int c = m_hListManagers.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ if ( m_hListManagers[i] )
+ {
+ m_hListManagers[i]->SceneStarted( this );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneListManager::AddListManager( CSceneListManager *pManager )
+{
+ CHandle< CSceneListManager > h;
+ h = pManager;
+ // Only add it once
+ if ( m_hListManagers.Find( h ) == m_hListManagers.InvalidIndex() )
+ {
+ m_hListManagers.AddToTail( h );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shut down all scenes, and then remove this entity
+//-----------------------------------------------------------------------------
+void CSceneListManager::InputShutdown( inputdata_t &inputdata )
+{
+ ShutdownList();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneListManager::ShutdownList( void )
+{
+ for ( int i = 0; i < SCENE_LIST_MANAGER_MAX_SCENES; i++ )
+ {
+ if ( m_hScenes[i] )
+ {
+ RemoveScene(i);
+ }
+ }
+
+ UTIL_Remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSceneListManager::RemoveScene( int iIndex )
+{
+ CSceneEntity *pScene = dynamic_cast<CSceneEntity*>(m_hScenes[iIndex].Get());
+ if ( pScene )
+ {
+ // Remove the scene
+ UTIL_Remove( pScene );
+ return;
+ }
+
+ // Tell the list manager to shut down all scenes
+ CSceneListManager *pList = dynamic_cast<CSceneListManager*>(m_hScenes[iIndex].Get());
+ if ( pList )
+ {
+ pList->ShutdownList();
+ }
+}
+
+void ReloadSceneFromDisk( CBaseEntity *ent )
+{
+ CSceneEntity *scene = dynamic_cast< CSceneEntity * >( ent );
+ if ( !scene )
+ return;
+
+ Assert( 0 );
+}
+
+// Purpose:
+// Input : *ent -
+// Output : char const
+//-----------------------------------------------------------------------------
+char const *GetSceneFilename( CBaseEntity *ent )
+{
+ CSceneEntity *scene = dynamic_cast< CSceneEntity * >( ent );
+ if ( !scene )
+ return "";
+
+ return STRING( scene->m_iszSceneFile );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return a list of the last 5 lines of speech from NPCs for bug reports
+// Input :
+// Output : speech - last 5 sound files played as speech
+// returns the number of sounds in the returned list
+//-----------------------------------------------------------------------------
+
+int GetRecentNPCSpeech( recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ] )
+{
+ int i;
+ int num;
+ int index;
+
+ // clear out the output list
+ for( i = 0; i < SPEECH_LIST_MAX_SOUNDS; i++ )
+ {
+ speech[ i ].time = 0.0f;
+ speech[ i ].name[ 0 ] = 0;
+ speech[ i ].sceneName[ 0 ] = 0;
+ }
+
+ // copy the sound names into the list in order they were played
+ num = 0;
+ index = speechListIndex;
+ for( i = 0; i < SPEECH_LIST_MAX_SOUNDS; i++ )
+ {
+ if ( speechListSounds[ index ].name[ 0 ] )
+ {
+ // only copy names that are not zero length
+ speech[ num ] = speechListSounds[ index ];
+ num++;
+ }
+
+ index++;
+ if ( index >= SPEECH_LIST_MAX_SOUNDS )
+ {
+ index = 0;
+ }
+ }
+
+ return num;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Displays a list of the last 5 lines of speech from NPCs
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+
+static void ListRecentNPCSpeech( void )
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ recentNPCSpeech_t speech[ SPEECH_LIST_MAX_SOUNDS ];
+ int num;
+ int i;
+
+ // get any sounds that were spoken by NPCs recently
+ num = GetRecentNPCSpeech( speech );
+ Msg( "Recent NPC speech:\n" );
+ for( i = 0; i < num; i++ )
+ {
+ Msg( " time: %6.3f sound name: %s scene: %s\n", speech[ i ].time, speech[ i ].name, speech[ i ].sceneName );
+ }
+ Msg( "Current time: %6.3f\n", gpGlobals->curtime );
+}
+
+static ConCommand ListRecentNPCSpeechCmd( "listRecentNPCSpeech", ListRecentNPCSpeech, "Displays a list of the last 5 lines of speech from NPCs.", FCVAR_DONTRECORD|FCVAR_GAMEDLL );
+
+CON_COMMAND( scene_flush, "Flush all .vcds from the cache and reload from disk." )
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ Msg( "Reloading\n" );
+ scenefilecache->Reload();
+ Msg( " done\n" );
+}