diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/sceneentity.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-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.cpp | 11312 |
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" ); +} |