You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5893 lines
162 KiB
5893 lines
162 KiB
//========= Copyright © 1996-2005, 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 "basemultiplayerplayer.h" |
|
#include "env_debughistory.h" |
|
#include "datacache/imdlcache.h" |
|
#include "recipientfilter.h" |
|
#include "team.h" |
|
#include "triggers.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; |
|
|
|
// From SoundEmitterSystem.cpp |
|
bool GetCaptionHash( char const *pchStringName, bool bWarnIfMissing, unsigned int &hash ); |
|
bool CanEmitCaption( unsigned int hash ); |
|
|
|
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)." ); |
|
ConVar scene_clientplayback( "scene_clientplayback", "1", 0, "Play all vcds on the clients." ); |
|
|
|
// 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 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, byte **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( byte *buffer ) |
|
{ |
|
delete[] 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(); |
|
// script description |
|
DECLARE_ENT_SCRIPTDESC(); |
|
|
|
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(); |
|
|
|
virtual int DrawDebugTextOverlays(); |
|
|
|
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 ); |
|
|
|
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 ); |
|
|
|
void AddBroadcastTeamTarget( int nTeamIndex ); |
|
void RemoveBroadcastTeamTarget( int nTeamIndex ); |
|
|
|
// 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 ); |
|
HSCRIPT ScriptFindNamedEntity( const char *name ); |
|
bool ScriptLoadSceneFromString( const char * pszFilename, const char *pszData ); |
|
|
|
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 ); |
|
}; |
|
|
|
const ConVar *CSceneEntity::m_pcvSndMixahead = NULL; |
|
|
|
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() |
|
|
|
|
|
BEGIN_ENT_SCRIPTDESC( CSceneEntity, CBaseEntity, "Choreographed scene which controls animation and/or dialog on one or more actors." ) |
|
DEFINE_SCRIPTFUNC( EstimateLength, "Returns length of this scene in seconds." ) |
|
DEFINE_SCRIPTFUNC( IsPlayingBack, "If this scene is currently playing." ) |
|
DEFINE_SCRIPTFUNC( IsPaused, "If this scene is currently paused." ) |
|
DEFINE_SCRIPTFUNC( AddBroadcastTeamTarget, "Adds a team (by index) to the broadcast list" ) |
|
DEFINE_SCRIPTFUNC( RemoveBroadcastTeamTarget, "Removes a team (by index) from the broadcast list" ) |
|
DEFINE_SCRIPTFUNC_NAMED( ScriptFindNamedEntity, "FindNamedEntity", "given an entity reference, such as !target, get actual entity from scene object" ) |
|
DEFINE_SCRIPTFUNC_NAMED( ScriptLoadSceneFromString, "LoadSceneFromString", "given a dummy scene name and a vcd string, load the scene" ) |
|
END_SCRIPTDESC(); |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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; |
|
|
|
m_BusyActor = SCENE_BUSYACTOR_DEFAULT; |
|
|
|
// FindVar is a very slow operation, too slow even to tolerate in a |
|
// constructor. We do it once and only once, and store the result in |
|
// static space. |
|
if (!m_pcvSndMixahead) |
|
{ |
|
m_pcvSndMixahead = cvar->FindVar( "snd_mixahead" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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, NULL, this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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, NULL, this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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, NULL, this ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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, this ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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, this ); |
|
} |
|
|
|
|
|
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, this ); |
|
} |
|
|
|
|
|
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 ) |
|
{ |
|
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 ) ); |
|
} |
|
|
|
// For english users, assume we emit the sound normally |
|
Q_strncpy( buf, event->GetParameters(), buflen ); |
|
|
|
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; |
|
} |
|
|
|
static CAI_BaseActor *ToBaseActor( CBaseFlex *actor ) |
|
{ |
|
return dynamic_cast<CAI_BaseActor*>( actor ); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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, iSoundlevel ); |
|
|
|
|
|
|
|
|
|
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 = ToBaseActor( actor ); |
|
if ( pBaseActor ) |
|
{ |
|
pBaseActor->NoteSpeaking( flDuration, GetPostSpeakDelay() ); |
|
} |
|
else if ( actor->IsNPC() && actor->MyNPCPointer() ) |
|
{ |
|
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; |
|
} |
|
|
|
// No CC since we do it manually |
|
// FIXME: This will change |
|
es.m_bEmitCloseCaption = false; |
|
|
|
// Captions only route to host player (there is only one closecaptioning HUD) |
|
filter.RemoveSplitScreenPlayers(); |
|
|
|
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 ) ) ) |
|
{ |
|
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, NULL, this ); |
|
} |
|
|
|
// Close captioning only on master token no matter what... |
|
if ( event->GetCloseCaptionType() == CChoreoEvent::CC_MASTER ) |
|
{ |
|
char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ]; |
|
bool validtoken = event->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ); |
|
if ( validtoken ) |
|
{ |
|
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 ( iSoundlevel != SNDLVL_NONE && 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 ); |
|
|
|
char const *pszActorModel = STRING( actor->GetModelName() ); |
|
gender_t gender = soundemitterbase->GetActorGender( pszActorModel ); |
|
|
|
char lowercase_nogender[ 256 ]; |
|
Q_strncpy( lowercase_nogender, lowercase, sizeof( lowercase_nogender ) ); |
|
bool bTriedGender = false; |
|
|
|
if ( gender == GENDER_MALE ) |
|
{ |
|
Q_strncat( lowercase, "_male", sizeof( lowercase ), COPY_ALL_CHARACTERS ); |
|
bTriedGender = true; |
|
} |
|
else if ( gender == GENDER_FEMALE ) |
|
{ |
|
Q_strncat( lowercase, "_female", sizeof( lowercase ), COPY_ALL_CHARACTERS ); |
|
bTriedGender = true; |
|
} |
|
|
|
unsigned int hash = 0u; |
|
bool bFound = GetCaptionHash( lowercase, true, hash ); |
|
|
|
// if not found, try the no-gender version |
|
if ( !bFound && bTriedGender ) |
|
{ |
|
bFound = GetCaptionHash( lowercase_nogender, true, hash ); |
|
} |
|
|
|
if ( bFound ) |
|
{ |
|
if ( CanEmitCaption( hash ) ) |
|
{ |
|
// Send caption and duration hint down to client |
|
UserMessageBegin( filter, "CloseCaption" ); |
|
WRITE_LONG( hash ); |
|
WRITE_UBITLONG( clamp( (int)( duration * 10.0f ), 0, 65535 ), 15 ), |
|
WRITE_UBITLONG( 0, 1 ), |
|
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, this ); |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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, NULL, this ); |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 = ToBaseActor( 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() ) ); |
|
|
|
AIConcept_t concept(inputdata.value.String()); |
|
for ( int i = 0; i < c; i++ ) |
|
{ |
|
CAI_BaseActor *npc = candidates[ i ]; |
|
Assert( npc ); |
|
|
|
AI_CriteriaSet set; |
|
npc->GatherCriteria( &set, concept, modifiers ); |
|
AI_Response response; |
|
if ( !npc->FindResponse( response, concept, &set ) ) |
|
continue; |
|
|
|
float duration = npc->GetResponseDuration( &response ); |
|
// Couldn't look it up |
|
if ( duration <= 0.0f ) |
|
continue; |
|
|
|
if ( !npc->PermitResponse( duration ) ) |
|
{ |
|
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, NULL ); |
|
} |
|
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; |
|
} |
|
|
|
static ConVar scene_async_prefetch_spew( "scene_async_prefetch_spew", "0", 0, "Display async .ani file loading info." ); |
|
|
|
void CSceneEntity::PrefetchAnimBlocks( CChoreoScene *scene ) |
|
{ |
|
Assert( scene ); |
|
|
|
// Build a fast lookup, too |
|
CUtlMap< CChoreoActor *, CBaseFlex *> actorMap( 0, 0, DefLessFunc( CChoreoActor * ) ); |
|
|
|
int spew = scene_async_prefetch_spew.GetInt(); |
|
int resident = 0; |
|
int checked = 0; |
|
|
|
MDLCACHE_CRITICAL_SECTION(); |
|
|
|
// 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 byte *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() |
|
{ |
|
m_bMultiplayer = gpGlobals->maxClients > 1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ) ) ) |
|
{ |
|
// 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 = ToBaseActor( pActor ); |
|
if ( pBaseActor ) |
|
{ |
|
AI_Response result ; |
|
AIConcept_t tmpConcept( STRING( m_iszResumeSceneFile ) ); |
|
if ( pBaseActor->FindResponse( result, tmpConcept, NULL ) ) |
|
{ |
|
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 && (event->GetType() != CChoreoEvent::SCRIPT) && (event->GetType() != CChoreoEvent::CAMERA) ) |
|
{ |
|
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() ); |
|
} |
|
|
|
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::CAMERA: |
|
{ |
|
// begin the camera shot |
|
const char *pszShotType = event->GetParameters(); |
|
|
|
CBaseEntity *pActor1 = FindNamedEntity( event->GetParameters2( ), pActor ); |
|
CBaseEntity *pActor2 = FindNamedEntity( event->GetParameters3( ), pActor ); |
|
float duration = event->GetDuration(); |
|
|
|
// grab any camera we find in the map |
|
// TODO: find camera that is nearest this scene entity? |
|
CTriggerCamera *pCamera = (CTriggerCamera *)gEntList.FindEntityByClassname( NULL, "point_viewcontrol" ); |
|
|
|
if ( !pCamera ) |
|
{ |
|
Warning( "CSceneEntity %s unable to find a camera (point_viewcontrol) in this map!\n", STRING(GetEntityName()) ); |
|
} |
|
else |
|
{ |
|
pCamera->StartCameraShot( pszShotType, this, pActor1, pActor2, duration ); |
|
} |
|
} |
|
break; |
|
|
|
case CChoreoEvent::SCRIPT: |
|
{ |
|
// NOTE: this is only used by auto-generated vcds to embed script commands to map entities. |
|
|
|
// vscript call - param1 is entity name, param2 is function name, param3 is function parameter string |
|
// calls a vscript function defined on the scope of the named CBaseEntity object/actor. |
|
// script call is of the format FunctionName(pActor, pThisSceneEntity, pszScriptParameters, duration) |
|
const char *pszActorName = event->GetParameters(); |
|
const char *pszFunctionName = event->GetParameters2(); |
|
const char *pszScriptParameters = event->GetParameters3(); |
|
|
|
float duration = event->GetDuration(); |
|
|
|
// TODO: should be new method CBaseEntity::CallScriptFunctionParams() |
|
CBaseEntity *pEntity = (CBaseEntity *)gEntList.FindEntityByName( NULL, pszActorName ); |
|
|
|
//START_VMPROFILE |
|
if ( !pEntity ) |
|
{ |
|
Warning( "CSceneEntity::SCRIPT event - unable to find entity named '%s' in this map!\n", pszActorName ); |
|
} |
|
else |
|
{ |
|
|
|
if( !pEntity->ValidateScriptScope() ) |
|
{ |
|
DevMsg("\n***\nCChoreoEvent::SCRIPT - FAILED to create private ScriptScope. ABORTING script call\n***\n"); |
|
break; |
|
} |
|
|
|
HSCRIPT hFunc = pEntity->m_ScriptScope.LookupFunction( pszFunctionName ); |
|
|
|
if( hFunc ) |
|
{ |
|
pEntity->m_ScriptScope.Call( hFunc, NULL, ToHScript(this), pszScriptParameters, duration ); |
|
pEntity->m_ScriptScope.ReleaseFunction( hFunc ); |
|
|
|
//UPDATE_VMPROFILE |
|
} |
|
else |
|
{ |
|
Warning("CSceneEntity::SCRIPT event - '%s' entity has no script function '%s' defined!\n", pszActorName,pszFunctionName); |
|
} |
|
} |
|
|
|
} |
|
break; |
|
|
|
case CChoreoEvent::FIRETRIGGER: |
|
{ |
|
// 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::CAMERA: |
|
{ |
|
// call the end of camera or call a dispatch function |
|
} |
|
break; |
|
|
|
case CChoreoEvent::SCRIPT: |
|
{ |
|
// call the end of script or call a dispatch function |
|
} |
|
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 |
|
byte *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: vscript - create a scene directly from a buffer containing |
|
// a vcd description, and load it into the scene entity. |
|
//----------------------------------------------------------------------------- |
|
|
|
bool CSceneEntity::ScriptLoadSceneFromString( const char * pszFilename, const char *pszData ) |
|
{ |
|
CChoreoScene *pScene = new CChoreoScene( NULL ); |
|
|
|
// CSceneTokenProcessor SceneTokenProcessor; |
|
// SceneTokenProcessor.SetBuffer( pszData ); |
|
g_TokenProcessor.SetBuffer( (char *)pszData ); |
|
|
|
if ( !pScene->ParseFromBuffer( pszFilename, &g_TokenProcessor ) ) //&SceneTokenProcessor ) ) |
|
{ |
|
Warning( "CSceneEntity::LoadSceneFromString: Unable to parse scene data '%s'\n", pszFilename ); |
|
delete pScene; |
|
pScene = NULL; |
|
} |
|
else |
|
{ |
|
pScene->SetPrintFunc( LocalScene_Printf ); |
|
pScene->SetEventCallbackInterface( this ); |
|
|
|
// precache all sounds for the newly constructed scene |
|
PrecacheScene( pScene ); |
|
} |
|
|
|
if ( pScene != NULL ) |
|
{ |
|
// release prior scene if present |
|
UnloadScene(); |
|
m_pScene = pScene; |
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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.Count() ); |
|
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; |
|
}; |
|
|
|
|
|
HSCRIPT CSceneEntity::ScriptFindNamedEntity( const char *name ) |
|
{ |
|
return ToHScript(FindNamedEntity( name, NULL, false, false )); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ); |
|
} |
|
|
|
{ |
|
CBaseFlex *pFlex = FindNamedActor( 0 ) ; |
|
if ( pFlex ) |
|
{ |
|
CBaseMultiplayerPlayer *pAsPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pFlex); |
|
if (pAsPlayer) |
|
{ |
|
CAI_Expresser *pExpresser = pAsPlayer->GetExpresser(); |
|
pExpresser->OnSpeechFinished(); |
|
} |
|
else if ( CAI_BaseActor *pActor = ToBaseActor( pFlex ) ) |
|
{ |
|
CAI_Expresser *pExpresser = pActor->GetExpresser(); |
|
pExpresser->OnSpeechFinished(); |
|
} |
|
} |
|
} |
|
|
|
|
|
// 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 || |
|
player->IsSplitScreenUserOnEdict( 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 ) ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
int CSceneEntity::DrawDebugTextOverlays() |
|
{ |
|
int nOffset = BaseClass::DrawDebugTextOverlays(); |
|
|
|
if (m_debugOverlays & OVERLAY_TEXT_BIT) |
|
{ |
|
char tempstr[512]; |
|
Q_snprintf( tempstr, sizeof( tempstr ), "Playing back: %s", m_bIsPlayingBack ? "yes" : "no" ); |
|
EntityText( nOffset, tempstr, 0 ); |
|
nOffset++; |
|
|
|
Q_snprintf( tempstr, sizeof( tempstr ), "Paused: %s", m_bPaused ? ( m_bPausedViaInput ? "yes - via input" : "yes" ) : "no" ); |
|
EntityText( nOffset, tempstr, 0 ); |
|
nOffset++; |
|
} |
|
|
|
return nOffset; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds a player (by index) to the recipient filter |
|
//----------------------------------------------------------------------------- |
|
void CSceneEntity::AddBroadcastTeamTarget( int nTeamIndex ) |
|
{ |
|
if ( m_pRecipientFilter == NULL ) |
|
{ |
|
CRecipientFilter filter; |
|
SetRecipientFilter( &filter ); |
|
} |
|
|
|
CTeam *pTeam = GetGlobalTeam( nTeamIndex ); |
|
Assert( pTeam ); |
|
if ( pTeam == NULL ) |
|
return; |
|
|
|
m_pRecipientFilter->AddRecipientsByTeam( pTeam ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Removes a player (by index) from the recipient filter |
|
//----------------------------------------------------------------------------- |
|
void CSceneEntity::RemoveBroadcastTeamTarget( int nTeamIndex ) |
|
{ |
|
if ( m_pRecipientFilter == NULL ) |
|
{ |
|
CRecipientFilter filter; |
|
SetRecipientFilter( &filter ); |
|
} |
|
|
|
CTeam *pTeam = GetGlobalTeam( nTeamIndex ); |
|
Assert( pTeam ); |
|
if ( pTeam == NULL ) |
|
return; |
|
|
|
m_pRecipientFilter->RemoveRecipientsByTeam( pTeam ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ) { /* suppress */ }; |
|
virtual void DispatchEndSequence( CChoreoScene *scene, CBaseFlex *actor, CChoreoEvent *event ) { /* suppress */ }; |
|
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() |
|
|
|
int SceneNameAutocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ) |
|
{ |
|
// chop the command off the begining of the string |
|
char *commandName = "scene_playvcd"; |
|
int numMatches = 0; |
|
partial += Q_strlen( commandName ) + 1; |
|
int partialLength = Q_strlen( partial ); |
|
|
|
// split the current path into the directory and the file. |
|
char dirName[ 512 ]; |
|
Q_snprintf( dirName, sizeof( dirName ), "%sjunk", partial ); |
|
Q_ExtractFilePath( dirName, dirName, sizeof( dirName ) ); |
|
partial += Q_strlen( dirName ); |
|
partialLength = Q_strlen( partial ); |
|
|
|
// now rebuild our path so we can pick up any directory that might have been entered. |
|
char path[ 512 ]; |
|
Q_snprintf( path, sizeof( path ), "scenes/%s%s*.*", dirName, partial ); |
|
|
|
FileFindHandle_t findHandle; |
|
char txtFilenameNoExtension[ MAX_PATH ]; |
|
const char *txtFilename = filesystem->FindFirstEx( path, "MOD", &findHandle ); |
|
while ( txtFilename ) |
|
{ |
|
if ( txtFilename[0] != '.' ) // skip the parent and current directories "." and ".." |
|
{ |
|
Q_FileBase( txtFilename, txtFilenameNoExtension, sizeof( txtFilenameNoExtension ) ); |
|
if ( !Q_strnicmp( txtFilenameNoExtension, partial, partialLength ) ) |
|
{ |
|
if ( filesystem->FindIsDirectory( findHandle ) ) |
|
{ |
|
// Add the directory name with a slash |
|
Q_snprintf( commands[ numMatches++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s%s/", commandName, dirName, txtFilenameNoExtension ); |
|
} |
|
else |
|
{ |
|
// Add the file name to the autocomplete array |
|
Q_snprintf( commands[ numMatches++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s%s", commandName, dirName, txtFilenameNoExtension ); |
|
} |
|
|
|
if ( numMatches == COMMAND_COMPLETION_MAXITEMS ) |
|
{ |
|
filesystem->FindClose( findHandle ); |
|
return numMatches; |
|
} |
|
} |
|
} |
|
|
|
txtFilename = filesystem->FindNext( findHandle ); |
|
} |
|
filesystem->FindClose( findHandle ); |
|
|
|
return numMatches; |
|
} |
|
|
|
CON_COMMAND_F_COMPLETION( scene_playvcd, "Play the given VCD as an instanced scripted scene.", FCVAR_CHEAT, SceneNameAutocomplete ) |
|
{ |
|
char vcdPath[ 512 ]; |
|
Q_snprintf( vcdPath, sizeof( vcdPath ), "scenes/%s.vcd", args[1] ); |
|
|
|
InstancedScriptedScene( NULL, vcdPath ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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; |
|
} |
|
|
|
|
|
HSCRIPT ScriptCreateSceneEntity( const char* pszScene ) |
|
{ |
|
if ( IsEntityCreationAllowedInScripts() == false ) |
|
{ |
|
Warning( "VScript error: A script attempted to create a scene entity mid-game. Entity creation from scripts is only allowed during map init.\n" ); |
|
return NULL; |
|
} |
|
|
|
g_pScriptVM->RegisterClass( GetScriptDescForClass( CSceneEntity ) ); |
|
CSceneEntity *pScene = (CSceneEntity *)CBaseEntity::CreateNoSpawn( "logic_choreographed_scene", vec3_origin, vec3_angle ); |
|
|
|
if ( pScene ) |
|
{ |
|
pScene->m_iszSceneFile = AllocPooledString( pszScene ); |
|
DispatchSpawn( pScene ); |
|
} |
|
|
|
return ToHScript( pScene ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// 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: 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 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 ) |
|
{ |
|
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." ) |
|
{ |
|
Msg( "Reloading\n" ); |
|
scenefilecache->Reload(); |
|
Msg( " done\n" ); |
|
}
|
|
|