aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/baseflex.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /sp/src/game/server/baseflex.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'sp/src/game/server/baseflex.cpp')
-rw-r--r--sp/src/game/server/baseflex.cpp2796
1 files changed, 2796 insertions, 0 deletions
diff --git a/sp/src/game/server/baseflex.cpp b/sp/src/game/server/baseflex.cpp
new file mode 100644
index 00000000..f173a04d
--- /dev/null
+++ b/sp/src/game/server/baseflex.cpp
@@ -0,0 +1,2796 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "animation.h"
+#include "baseflex.h"
+#include "filesystem.h"
+#include "studio.h"
+#include "choreoevent.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "tier1/strtools.h"
+#include "KeyValues.h"
+#include "ai_basenpc.h"
+#include "ai_navigator.h"
+#include "ai_moveprobe.h"
+#include "sceneentity.h"
+#include "ai_baseactor.h"
+#include "datacache/imdlcache.h"
+#include "tier1/byteswap.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+static ConVar scene_showlook( "scene_showlook", "0", FCVAR_ARCHIVE, "When playing back, show the directions of look events." );
+static ConVar scene_showmoveto( "scene_showmoveto", "0", FCVAR_ARCHIVE, "When moving, show the end location." );
+static ConVar scene_showunlock( "scene_showunlock", "0", FCVAR_ARCHIVE, "Show when a vcd is playing but normal AI is running." );
+
+// static ConVar scene_checktagposition( "scene_checktagposition", "0", FCVAR_ARCHIVE, "When playing back a choreographed scene, check the current position of the tags relative to where they were authored." );
+
+// Fake layer # to force HandleProcessSceneEvent to actually allocate the layer during npc think time instead of in between.
+#define REQUEST_DEFERRED_LAYER_ALLOCATION -2
+
+extern bool g_bClientFlex;
+
+// ---------------------------------------------------------------------
+//
+// CBaseFlex -- physically simulated brush rectangular solid
+//
+// ---------------------------------------------------------------------
+
+void* SendProxy_FlexWeights( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
+{
+ // Don't any flexweights to client unless scene_clientflex.GetBool() is false
+ if ( !g_bClientFlex )
+ return (void*)pVarData;
+ else
+ return NULL;
+}
+
+REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_FlexWeights );
+
+// SendTable stuff.
+IMPLEMENT_SERVERCLASS_ST(CBaseFlex, DT_BaseFlex)
+// Note we can't totally disabled flexweights transmission since some things like blink and eye tracking are still done by the server
+ SendPropArray3 (SENDINFO_ARRAY3(m_flexWeight), SendPropFloat(SENDINFO_ARRAY(m_flexWeight), 12, SPROP_ROUNDDOWN, 0.0f, 1.0f ) /*, SendProxy_FlexWeights*/ ),
+ SendPropInt (SENDINFO(m_blinktoggle), 1, SPROP_UNSIGNED ),
+ SendPropVector (SENDINFO(m_viewtarget), -1, SPROP_COORD),
+#ifdef HL2_DLL
+ SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 0), 0, SPROP_NOSCALE ),
+ SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 1), 0, SPROP_NOSCALE ),
+ SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 2), 0, SPROP_NOSCALE ),
+
+ SendPropVector ( SENDINFO(m_vecLean), -1, SPROP_COORD ),
+ SendPropVector ( SENDINFO(m_vecShift), -1, SPROP_COORD ),
+#endif
+
+END_SEND_TABLE()
+
+
+BEGIN_DATADESC( CBaseFlex )
+
+ // m_blinktoggle
+ DEFINE_ARRAY( m_flexWeight, FIELD_FLOAT, MAXSTUDIOFLEXCTRL ),
+ DEFINE_FIELD( m_viewtarget, FIELD_POSITION_VECTOR ),
+ // m_SceneEvents
+ // m_FileList
+ DEFINE_FIELD( m_flAllowResponsesEndTime, FIELD_TIME ),
+ // m_ActiveChoreoScenes
+ // DEFINE_FIELD( m_LocalToGlobal, CUtlRBTree < FS_LocalToGlobal_t , unsigned short > ),
+ // m_bUpdateLayerPriorities
+ DEFINE_FIELD( m_flLastFlexAnimationTime, FIELD_TIME ),
+
+#ifdef HL2_DLL
+ //DEFINE_FIELD( m_vecPrevOrigin, FIELD_POSITION_VECTOR ),
+ //DEFINE_FIELD( m_vecPrevVelocity, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vecLean, FIELD_VECTOR ),
+ DEFINE_FIELD( m_vecShift, FIELD_VECTOR ),
+#endif
+
+END_DATADESC()
+
+
+
+LINK_ENTITY_TO_CLASS( funCBaseFlex, CBaseFlex ); // meaningless independant class!!
+
+CBaseFlex::CBaseFlex( void ) :
+ m_LocalToGlobal( 0, 0, FlexSettingLessFunc )
+{
+#ifdef _DEBUG
+ // default constructor sets the viewtarget to NAN
+ m_viewtarget.Init();
+#endif
+ m_bUpdateLayerPriorities = true;
+ m_flLastFlexAnimationTime = 0.0;
+}
+
+CBaseFlex::~CBaseFlex( void )
+{
+ m_LocalToGlobal.RemoveAll();
+ Assert( m_SceneEvents.Count() == 0 );
+}
+
+void CBaseFlex::SetModel( const char *szModelName )
+{
+ MDLCACHE_CRITICAL_SECTION();
+
+ BaseClass::SetModel( szModelName );
+
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
+ {
+ SetFlexWeight( i, 0.0f );
+ }
+}
+
+
+void CBaseFlex::SetViewtarget( const Vector &viewtarget )
+{
+ m_viewtarget = viewtarget; // bah
+}
+
+void CBaseFlex::SetFlexWeight( LocalFlexController_t index, float value )
+{
+ if (index >= 0 && index < GetNumFlexControllers())
+ {
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return;
+
+ mstudioflexcontroller_t *pflexcontroller = pstudiohdr->pFlexcontroller( index );
+
+ if (pflexcontroller->max != pflexcontroller->min)
+ {
+ value = (value - pflexcontroller->min) / (pflexcontroller->max - pflexcontroller->min);
+ value = clamp( value, 0.0f, 1.0f );
+ }
+
+ m_flexWeight.Set( index, value );
+ }
+}
+
+float CBaseFlex::GetFlexWeight( LocalFlexController_t index )
+{
+ if (index >= 0 && index < GetNumFlexControllers())
+ {
+ CStudioHdr *pstudiohdr = GetModelPtr( );
+ if (! pstudiohdr)
+ return 0;
+
+ mstudioflexcontroller_t *pflexcontroller = pstudiohdr->pFlexcontroller( index );
+
+ if (pflexcontroller->max != pflexcontroller->min)
+ {
+ return m_flexWeight[index] * (pflexcontroller->max - pflexcontroller->min) + pflexcontroller->min;
+ }
+
+ return m_flexWeight[index];
+ }
+ return 0.0;
+}
+
+LocalFlexController_t CBaseFlex::FindFlexController( const char *szName )
+{
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
+ {
+ if (stricmp( GetFlexControllerName( i ), szName ) == 0)
+ {
+ return i;
+ }
+ }
+
+ // AssertMsg( 0, UTIL_VarArgs( "flexcontroller %s couldn't be mapped!!!\n", szName ) );
+ return LocalFlexController_t(0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseFlex::StartChoreoScene( CChoreoScene *scene )
+{
+ if ( m_ActiveChoreoScenes.Find( scene ) != m_ActiveChoreoScenes.InvalidIndex() )
+ {
+ return;
+ }
+
+ m_ActiveChoreoScenes.AddToTail( scene );
+ m_bUpdateLayerPriorities = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseFlex::RemoveChoreoScene( CChoreoScene *scene, bool canceled )
+{
+ // Assert( m_ActiveChoreoScenes.Find( scene ) != m_ActiveChoreoScenes.InvalidIndex() );
+
+ m_ActiveChoreoScenes.FindAndRemove( scene );
+ m_bUpdateLayerPriorities = true;
+
+ if (canceled)
+ {
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if ( myNpc )
+ {
+ myNpc->ClearSceneLock( );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+
+int CBaseFlex::GetScenePriority( CChoreoScene *scene )
+{
+ int iPriority = 0;
+ int c = m_ActiveChoreoScenes.Count();
+ // count number of channels in scenes older than current
+ for ( int i = 0; i < c; i++ )
+ {
+ CChoreoScene *pScene = m_ActiveChoreoScenes[ i ];
+ if ( !pScene )
+ {
+ continue;
+ }
+
+ if ( pScene == scene )
+ {
+ break;
+ }
+
+ iPriority += pScene->GetNumChannels( );
+ }
+ return iPriority;
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove all active SceneEvents
+//-----------------------------------------------------------------------------
+void CBaseFlex::ClearSceneEvents( CChoreoScene *scene, bool canceled )
+{
+ if ( !scene )
+ {
+ m_SceneEvents.RemoveAll();
+ return;
+ }
+
+ for ( int i = m_SceneEvents.Count() - 1; i >= 0; i-- )
+ {
+ CSceneEventInfo *info = &m_SceneEvents[ i ];
+
+ Assert( info );
+ Assert( info->m_pScene );
+ Assert( info->m_pEvent );
+
+ if ( info->m_pScene != scene )
+ continue;
+
+ if ( !ClearSceneEvent( info, false, canceled ))
+ {
+ // unknown expression to clear!!
+ Assert( 0 );
+ }
+
+ // Free this slot
+ info->m_pEvent = NULL;
+ info->m_pScene = NULL;
+ info->m_bStarted = false;
+
+ m_SceneEvents.Remove( i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop specifics of expression
+//-----------------------------------------------------------------------------
+
+bool CBaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled )
+{
+ Assert( info );
+ Assert( info->m_pScene );
+ Assert( info->m_pEvent );
+
+ // FIXME: this code looks duplicated
+ switch ( info->m_pEvent->GetType() )
+ {
+ case CChoreoEvent::GESTURE:
+ case CChoreoEvent::SEQUENCE:
+ {
+ if (info->m_iLayer >= 0)
+ {
+ if ( fastKill )
+ {
+ FastRemoveLayer( info->m_iLayer );
+ }
+ else if (info->m_pEvent->GetType() == CChoreoEvent::GESTURE)
+ {
+ if (canceled)
+ {
+ // remove slower if interrupted
+ RemoveLayer( info->m_iLayer, 0.5 );
+ }
+ else
+ {
+ RemoveLayer( info->m_iLayer, 0.1 );
+ }
+ }
+ else
+ {
+ RemoveLayer( info->m_iLayer, 0.3 );
+ }
+ }
+ }
+ return true;
+
+ case CChoreoEvent::MOVETO:
+ {
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if (!myNpc)
+ return true;
+
+ // cancel moveto if it's distance based, of if the event was part of a canceled vcd
+ if (IsMoving() && (canceled || info->m_pEvent->GetDistanceToTarget() > 0.0))
+ {
+ if (!info->m_bHasArrived)
+ {
+ if (info->m_pScene)
+ {
+ Scene_Printf( "%s : %8.2f: MOVETO canceled but actor %s not at goal\n", info->m_pScene->GetFilename(), info->m_pScene->GetTime(), info->m_pEvent->GetActor()->GetName() );
+ }
+ }
+ myNpc->GetNavigator()->StopMoving( false ); // Stop moving
+ }
+ }
+ return true;
+ case CChoreoEvent::FACE:
+ case CChoreoEvent::FLEXANIMATION:
+ case CChoreoEvent::EXPRESSION:
+ case CChoreoEvent::LOOKAT:
+ case CChoreoEvent::GENERIC:
+ {
+ // no special rules
+ }
+ return true;
+ case CChoreoEvent::SPEAK:
+ {
+ // Tracker 15420: Issue stopsound if we need to cut this short...
+ if ( canceled )
+ {
+ StopSound( info->m_pEvent->GetParameters() );
+
+#ifdef HL2_EPISODIC
+ // If we were holding the semaphore because of this speech, release it
+ CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(this);
+ if ( pBaseActor )
+ {
+ pBaseActor->GetExpresser()->ForceNotSpeaking();
+ }
+#endif
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Add string indexed scene/expression/duration to list of active SceneEvents
+// Input : scenefile -
+// expression -
+// duration -
+//-----------------------------------------------------------------------------
+void CBaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget )
+{
+ if ( !scene || !event )
+ {
+ Msg( "CBaseFlex::AddSceneEvent: scene or event was NULL!!!\n" );
+ return;
+ }
+
+ CChoreoActor *actor = event->GetActor();
+ if ( !actor )
+ {
+ Msg( "CBaseFlex::AddSceneEvent: event->GetActor() was NULL!!!\n" );
+ return;
+ }
+
+
+ CSceneEventInfo info;
+
+ memset( (void *)&info, 0, sizeof( info ) );
+
+ info.m_pEvent = event;
+ info.m_pScene = scene;
+ info.m_hTarget = pTarget;
+ info.m_bStarted = false;
+
+ if (StartSceneEvent( &info, scene, event, actor, pTarget ))
+ {
+ m_SceneEvents.AddToTail( info );
+ }
+ else
+ {
+ Scene_Printf( "CBaseFlex::AddSceneEvent: event failed\n" );
+ // Assert( 0 ); // expression failed to start
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Starting various expression types
+//-----------------------------------------------------------------------------
+
+bool CBaseFlex::RequestStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
+{
+ info->m_nSequence = LookupSequence( event->GetParameters() );
+
+ // make sure sequence exists
+ if (info->m_nSequence < 0)
+ {
+ Warning( "CSceneEntity %s :\"%s\" unable to find sequence \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
+ return false;
+ }
+
+ // This is a bit of a hack, but we need to defer the actual allocation until Process which will sync the layer allocation
+ // to the NPCs think/m_flAnimTime instead of some arbitrary tick
+ info->m_iLayer = REQUEST_DEFERRED_LAYER_ALLOCATION;
+ info->m_pActor = actor;
+ return true;
+}
+
+bool CBaseFlex::RequestStartGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
+{
+ info->m_nSequence = LookupSequence( event->GetParameters() );
+
+ // make sure sequence exists
+ if (info->m_nSequence < 0)
+ {
+ Warning( "CSceneEntity %s :\"%s\" unable to find gesture \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
+ return false;
+ }
+
+ // This is a bit of a hack, but we need to defer the actual allocation until Process which will sync the layer allocation
+ // to the NPCs think/m_flAnimTime instead of some arbitrary tick
+ info->m_iLayer = REQUEST_DEFERRED_LAYER_ALLOCATION;
+ info->m_pActor = actor;
+ return true;
+}
+
+bool CBaseFlex::HandleStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor )
+{
+ Assert( info->m_iLayer == REQUEST_DEFERRED_LAYER_ALLOCATION );
+
+ info->m_nSequence = LookupSequence( event->GetParameters() );
+ info->m_iLayer = -1;
+
+ if (info->m_nSequence < 0)
+ {
+ Warning( "CSceneEntity %s :\"%s\" unable to find sequence \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
+ return false;
+ }
+
+ if (!EnterSceneSequence( scene, event ))
+ {
+ if (!event->GetPlayOverScript())
+ {
+ // this has failed to start
+ Warning( "CSceneEntity %s :\"%s\" failed to start sequence \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
+ return false;
+ }
+ // Start anyways, just use normal no-movement, must be in IDLE rules
+ }
+
+ info->m_iPriority = actor->FindChannelIndex( event->GetChannel() );
+ info->m_iLayer = AddLayeredSequence( info->m_nSequence, info->m_iPriority + GetScenePriority( scene ) );
+ SetLayerNoRestore( info->m_iLayer, true );
+ SetLayerWeight( info->m_iLayer, 0.0 );
+
+ bool looping = ((GetSequenceFlags( GetModelPtr(), info->m_nSequence ) & STUDIO_LOOPING) != 0);
+ if (!looping)
+ {
+ // figure out the animtime when this was frame 0
+ float dt = scene->GetTime() - event->GetStartTime();
+ float seq_duration = SequenceDuration( info->m_nSequence );
+ float flCycle = dt / seq_duration;
+ flCycle = flCycle - (int)flCycle; // loop
+ SetLayerCycle( info->m_iLayer, flCycle, flCycle );
+
+ SetLayerPlaybackRate( info->m_iLayer, 0.0 );
+ }
+ else
+ {
+ SetLayerPlaybackRate( info->m_iLayer, 1.0 );
+ }
+
+ if (IsMoving())
+ {
+ info->m_flWeight = 0.0;
+ }
+ else
+ {
+ info->m_flWeight = 1.0;
+ }
+
+ return true;
+}
+
+bool CBaseFlex::HandleStartGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor )
+{
+ Assert( info->m_iLayer == REQUEST_DEFERRED_LAYER_ALLOCATION );
+
+ info->m_nSequence = LookupSequence( event->GetParameters() );
+ info->m_iLayer = -1;
+
+ if (info->m_nSequence < 0)
+ {
+ Warning( "CSceneEntity %s :\"%s\" unable to find gesture \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
+ return false;
+ }
+
+ // FIXME: this seems like way too much code
+ info->m_bIsGesture = false;
+ KeyValues *seqKeyValues = GetSequenceKeyValues( info->m_nSequence );
+ if (seqKeyValues)
+ {
+ // Do we have a build point section?
+ KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
+ if ( pkvAllFaceposer )
+ {
+ KeyValues *pkvType = pkvAllFaceposer->FindKey("type");
+
+ if (pkvType)
+ {
+ info->m_bIsGesture = (stricmp( pkvType->GetString(), "gesture" ) == 0) ? true : false;
+ }
+ }
+
+ // FIXME: fixup tags that should be set as "linear", should be done in faceposer
+ char szStartLoop[CEventAbsoluteTag::MAX_EVENTTAG_LENGTH] = { "loop" };
+ char szEndLoop[CEventAbsoluteTag::MAX_EVENTTAG_LENGTH] = { "end" };
+
+ // check in the tag indexes
+ KeyValues *pkvFaceposer;
+ for ( pkvFaceposer = pkvAllFaceposer->GetFirstSubKey(); pkvFaceposer; pkvFaceposer = pkvFaceposer->GetNextKey() )
+ {
+ if (!stricmp( pkvFaceposer->GetName(), "startloop" ))
+ {
+ V_strcpy_safe( szStartLoop, pkvFaceposer->GetString() );
+ }
+ else if (!stricmp( pkvFaceposer->GetName(), "endloop" ))
+ {
+ V_strcpy_safe( szEndLoop, pkvFaceposer->GetString() );
+ }
+ }
+
+ CEventAbsoluteTag *ptag;
+ ptag = event->FindAbsoluteTag( CChoreoEvent::ORIGINAL, szStartLoop );
+ if (ptag)
+ {
+ ptag->SetLinear( true );
+ }
+ ptag = event->FindAbsoluteTag( CChoreoEvent::PLAYBACK, szStartLoop );
+ if (ptag)
+ {
+ ptag->SetLinear( true );
+ }
+ ptag = event->FindAbsoluteTag( CChoreoEvent::ORIGINAL, szEndLoop );
+ if (ptag)
+ {
+ ptag->SetLinear( true );
+ }
+ ptag = event->FindAbsoluteTag( CChoreoEvent::PLAYBACK, szEndLoop );
+ if (ptag)
+ {
+ ptag->SetLinear( true );
+ }
+
+ if ( pkvAllFaceposer )
+ {
+ CStudioHdr *pstudiohdr = GetModelPtr();
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( info->m_nSequence );
+ mstudioanimdesc_t &animdesc = pstudiohdr->pAnimdesc( pstudiohdr->iRelativeAnim( info->m_nSequence, seqdesc.anim(0,0) ) );
+
+ // check in the tag indexes
+ KeyValues *pkvFaceposer;
+ for ( pkvFaceposer = pkvAllFaceposer->GetFirstSubKey(); pkvFaceposer; pkvFaceposer = pkvFaceposer->GetNextKey() )
+ {
+ if (!stricmp( pkvFaceposer->GetName(), "tags" ))
+ {
+ KeyValues *pkvTags;
+ for ( pkvTags = pkvFaceposer->GetFirstSubKey(); pkvTags; pkvTags = pkvTags->GetNextKey() )
+ {
+ int maxFrame = animdesc.numframes - 2; // FIXME: this is off by one!
+
+ if ( maxFrame > 0)
+ {
+ float percentage = (float)pkvTags->GetInt() / maxFrame;
+
+ CEventAbsoluteTag *ptag = event->FindAbsoluteTag( CChoreoEvent::ORIGINAL, pkvTags->GetName() );
+ if (ptag)
+ {
+ if (fabs(ptag->GetPercentage() - percentage) > 0.05)
+ {
+ DevWarning("%s repositioned tag: %s : %.3f -> %.3f (%s:%s:%s)\n", scene->GetFilename(), pkvTags->GetName(), ptag->GetPercentage(), percentage, scene->GetFilename(), actor->GetName(), event->GetParameters() );
+ // reposition tag
+ ptag->SetPercentage( percentage );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!event->VerifyTagOrder())
+ {
+ DevWarning("out of order tags : %s : (%s:%s:%s)\n", scene->GetFilename(), actor->GetName(), event->GetName(), event->GetParameters() );
+ }
+ }
+
+ seqKeyValues->deleteThis();
+ }
+
+ // initialize posture suppression
+ // FIXME: move priority of base animation so that layers can be inserted before
+ // FIXME: query stopping, post idle layer to figure out correct weight
+ // GetIdleLayerWeight()?
+ if (!info->m_bIsGesture && IsMoving())
+ {
+ info->m_flWeight = 0.0;
+ }
+ else
+ {
+ info->m_flWeight = 1.0;
+ }
+
+ // this happens before StudioFrameAdvance()
+ info->m_iPriority = actor->FindChannelIndex( event->GetChannel() );
+ info->m_iLayer = AddLayeredSequence( info->m_nSequence, info->m_iPriority + GetScenePriority( scene ) );
+ SetLayerNoRestore( info->m_iLayer, true );
+ SetLayerDuration( info->m_iLayer, event->GetDuration() );
+ SetLayerWeight( info->m_iLayer, 0.0 );
+
+ bool looping = ((GetSequenceFlags( GetModelPtr(), info->m_nSequence ) & STUDIO_LOOPING) != 0);
+ if ( looping )
+ {
+ DevMsg( 1, "vcd error, gesture %s of model %s is marked as STUDIO_LOOPING!\n",
+ event->GetParameters(), STRING(GetModelName()) );
+ }
+
+ SetLayerLooping( info->m_iLayer, false ); // force to not loop
+
+ float duration = event->GetDuration( );
+
+ // figure out the animtime when this was frame 0
+ float flEventCycle = (scene->GetTime() - event->GetStartTime()) / duration;
+ float flCycle = event->GetOriginalPercentageFromPlaybackPercentage( flEventCycle );
+ SetLayerCycle( info->m_iLayer, flCycle, 0.0 );
+ SetLayerPlaybackRate( info->m_iLayer, 0.0 );
+
+ return true;
+}
+
+bool CBaseFlex::StartFacingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
+{
+ if ( pTarget )
+ {
+ // Don't allow FACE commands while sitting in the vehicle
+ CAI_BaseNPC *myNpc = MyNPCPointer();
+ if ( myNpc && myNpc->IsInAVehicle() )
+ return false;
+
+ info->m_bIsMoving = false;
+ return true;
+ }
+ return false;
+}
+
+
+bool CBaseFlex::StartMoveToSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
+{
+ if (pTarget)
+ {
+ info->m_bIsMoving = false;
+ info->m_bHasArrived = false;
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if (!myNpc)
+ {
+ return false;
+ }
+
+ EnterSceneSequence( scene, event, true );
+
+ // If they're already moving, stop them
+ //
+ // Don't stop them during restore because that will set a stopping path very
+ // nearby, causing us to signal arrival prematurely in CheckSceneEventCompletion.
+ // BEWARE: the behavior of this bug depended on the order in which the entities were restored!!
+ if ( myNpc->IsMoving() && !scene->IsRestoring() )
+ {
+ myNpc->GetNavigator()->StopMoving( false );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
+{
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::SEQUENCE:
+ return RequestStartSequenceSceneEvent( info, scene, event, actor, pTarget );
+
+ case CChoreoEvent::GESTURE:
+ return RequestStartGestureSceneEvent( info, scene, event, actor, pTarget );
+
+ case CChoreoEvent::FACE:
+ return StartFacingSceneEvent( info, scene, event, actor, pTarget );
+
+ // FIXME: move this to an CBaseActor
+ case CChoreoEvent::MOVETO:
+ return StartMoveToSceneEvent( info, scene, event, actor, pTarget );
+
+ case CChoreoEvent::LOOKAT:
+ info->m_hTarget = pTarget;
+ return true;
+
+ case CChoreoEvent::FLEXANIMATION:
+ info->InitWeight( this );
+ return true;
+
+ case CChoreoEvent::SPEAK:
+ return true;
+
+ case CChoreoEvent::EXPRESSION: // These are handled client-side
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove expression
+// Input : scenefile -
+// expression -
+//-----------------------------------------------------------------------------
+void CBaseFlex::RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill )
+{
+ Assert( event );
+
+ for ( int i = 0 ; i < m_SceneEvents.Count(); i++ )
+ {
+ CSceneEventInfo *info = &m_SceneEvents[ i ];
+
+ Assert( info );
+ Assert( info->m_pEvent );
+
+ if ( info->m_pScene != scene )
+ continue;
+
+ if ( info->m_pEvent != event)
+ continue;
+
+ if (ClearSceneEvent( info, fastKill, false ))
+ {
+ // Free this slot
+ info->m_pEvent = NULL;
+ info->m_pScene = NULL;
+ info->m_bStarted = false;
+
+ m_SceneEvents.Remove( i );
+ return;
+ }
+ }
+
+ // many events refuse to start due to bogus parameters
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks to see if the event should be considered "completed"
+//-----------------------------------------------------------------------------
+bool CBaseFlex::CheckSceneEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ for ( int i = 0 ; i < m_SceneEvents.Count(); i++ )
+ {
+ CSceneEventInfo *info = &m_SceneEvents[ i ];
+
+ Assert( info );
+ Assert( info->m_pEvent );
+
+ if ( info->m_pScene != scene )
+ continue;
+
+ if ( info->m_pEvent != event)
+ continue;
+
+ return CheckSceneEventCompletion( info, currenttime, scene, event );
+ }
+ return true;
+}
+
+
+
+bool CBaseFlex::CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event )
+{
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::MOVETO:
+ {
+ CAI_BaseNPC *npc = MyNPCPointer( );
+
+ if (npc)
+ {
+ // check movement, check arrival
+ if (npc->GetNavigator()->IsGoalActive())
+ {
+ const Task_t *pCurTask = npc->GetTask();
+ if ( pCurTask && (pCurTask->iTask == TASK_PLAY_SCENE || pCurTask->iTask == TASK_WAIT_FOR_MOVEMENT ) )
+ {
+ float preload = event->GetEndTime() - currenttime;
+ if (preload < 0)
+ {
+ //Msg("%.1f: no preload\n", currenttime );
+ return false;
+ }
+ float t = npc->GetTimeToNavGoal();
+
+ // Msg("%.1f: preload (%s:%.1f) %.1f %.1f\n", currenttime, event->GetName(), event->GetEndTime(), preload, t );
+
+ // FIXME: t is zero if no path can be built!
+
+ if (t > 0.0f && t <= preload)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+ else if (info->m_bHasArrived)
+ {
+ return true;
+ }
+ else if (info->m_bStarted && !npc->IsCurSchedule( SCHED_SCENE_GENERIC ))
+ {
+ // FIXME: There's still a hole in the logic is the save happens immediately after the SS steals the npc but before their AI has run again
+ Warning( "%s : %8.2f: waiting for actor %s to complete MOVETO but actor not in SCHED_SCENE_GENERIC\n", scene->GetFilename(), scene->GetTime(), event->GetActor()->GetName() );
+ // no longer in a scene :P
+ return true;
+ }
+ // still trying
+ return false;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Default implementation
+//-----------------------------------------------------------------------------
+void CBaseFlex::ProcessSceneEvents( void )
+{
+ VPROF( "CBaseFlex::ProcessSceneEvents" );
+ // slowly decay to netural expression
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
+ {
+ SetFlexWeight( i, GetFlexWeight( i ) * 0.95 );
+ }
+
+ bool bHasForegroundEvents = false;
+ // Iterate SceneEvents and look for active slots
+ for ( int i = 0; i < m_SceneEvents.Count(); i++ )
+ {
+ CSceneEventInfo *info = &m_SceneEvents[ i ];
+ Assert( info );
+
+ // FIXME: Need a safe handle to m_pEvent in case of memory deletion?
+ CChoreoEvent *event = info->m_pEvent;
+ Assert( event );
+
+ CChoreoScene *scene = info->m_pScene;
+ Assert( scene );
+
+ if ( scene && !scene->IsBackground() )
+ {
+ bHasForegroundEvents = true;
+ }
+
+ if (ProcessSceneEvent( info, scene, event ))
+ {
+ info->m_bStarted = true;
+ }
+ }
+
+ if ( bHasForegroundEvents && scene_showunlock.GetBool())
+ {
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if ( myNpc && !(myNpc->GetState() == NPC_STATE_SCRIPT || myNpc->IsCurSchedule( SCHED_SCENE_GENERIC )) )
+ {
+ Vector p0 = myNpc->GetHullMins();
+ Vector p1 = myNpc->GetHullMaxs();
+ p0.z = p1.z + 2;
+ p1.z = p1.z + 2;
+ NDebugOverlay::Box( myNpc->GetAbsOrigin(), p0, p1, 255, 0, 0, 0, 0.12 );
+ }
+ }
+
+
+ // any needed layer priorites have now been reset
+ m_bUpdateLayerPriorities = false;
+}
+
+class CFlexSceneFileManager : CAutoGameSystem
+{
+public:
+
+ CFlexSceneFileManager( char const *name ) : CAutoGameSystem( name )
+ {
+ }
+
+ virtual bool Init()
+ {
+ // Trakcer 16692: Preload these at startup to avoid hitch first time we try to load them during actual gameplay
+ FindSceneFile( NULL, "phonemes", true );
+ FindSceneFile( NULL, "phonemes_weak", true );
+ FindSceneFile( NULL, "phonemes_strong", true );
+#if defined( HL2_DLL )
+ FindSceneFile( NULL, "random", true );
+ FindSceneFile( NULL, "randomAlert", true );
+#endif
+ return true;
+ }
+
+ // Tracker 14992: We used to load 18K of .vfes for every CBaseFlex who lipsynced, but now we only load those files once globally.
+ // Note, we could wipe these between levels, but they don't ever load more than the weak/normal/strong phoneme classes that I can tell
+ // so I'll just leave them loaded forever for now
+ virtual void Shutdown()
+ {
+ DeleteSceneFiles();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Sets up translations
+ // Input : *instance -
+ // *pSettinghdr -
+ // Output : void
+ //-----------------------------------------------------------------------------
+ void EnsureTranslations( CBaseFlex *instance, const flexsettinghdr_t *pSettinghdr )
+ {
+ // The only time instance is NULL is in Init() above, where we're just loading the .vfe files off of the hard disk.
+ if ( instance )
+ {
+ instance->EnsureTranslations( pSettinghdr );
+ }
+ }
+
+ const void *FindSceneFile( CBaseFlex *instance, const char *filename, bool allowBlockingIO )
+ {
+ // See if it's already loaded
+ int i;
+ for ( i = 0; i < m_FileList.Size(); i++ )
+ {
+ CFlexSceneFile *file = m_FileList[ i ];
+ if ( file && !stricmp( file->filename, filename ) )
+ {
+ // Make sure translations (local to global flex controller) are set up for this instance
+ EnsureTranslations( instance, ( const flexsettinghdr_t * )file->buffer );
+ return file->buffer;
+ }
+ }
+
+ if ( !allowBlockingIO )
+ {
+ return NULL;
+ }
+
+ // Load file into memory
+ void *buffer = NULL;
+ int len = filesystem->ReadFileEx( UTIL_VarArgs( "expressions/%s.vfe", filename ), "GAME", &buffer, false, true );
+
+ if ( !len )
+ return NULL;
+
+ // Create scene entry
+ CFlexSceneFile *pfile = new CFlexSceneFile;
+ // Remember filename
+ Q_strncpy( pfile->filename, filename, sizeof( pfile->filename ) );
+ // Remember data pointer
+ pfile->buffer = buffer;
+ // Add to list
+ m_FileList.AddToTail( pfile );
+
+ // Swap the entire file
+ if ( IsX360() )
+ {
+ CByteswap swap;
+ swap.ActivateByteSwapping( true );
+ byte *pData = (byte*)buffer;
+ flexsettinghdr_t *pHdr = (flexsettinghdr_t*)pData;
+ swap.SwapFieldsToTargetEndian( pHdr );
+
+ // Flex Settings
+ flexsetting_t *pFlexSetting = (flexsetting_t*)((byte*)pHdr + pHdr->flexsettingindex);
+ for ( int i = 0; i < pHdr->numflexsettings; ++i, ++pFlexSetting )
+ {
+ swap.SwapFieldsToTargetEndian( pFlexSetting );
+
+ flexweight_t *pWeight = (flexweight_t*)(((byte*)pFlexSetting) + pFlexSetting->settingindex );
+ for ( int j = 0; j < pFlexSetting->numsettings; ++j, ++pWeight )
+ {
+ swap.SwapFieldsToTargetEndian( pWeight );
+ }
+ }
+
+ // indexes
+ pData = (byte*)pHdr + pHdr->indexindex;
+ swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numindexes );
+
+ // keymappings
+ pData = (byte*)pHdr + pHdr->keymappingindex;
+ swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numkeys );
+
+ // keyname indices
+ pData = (byte*)pHdr + pHdr->keynameindex;
+ swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numkeys );
+ }
+
+ // Fill in translation table
+ EnsureTranslations( instance, ( const flexsettinghdr_t * )pfile->buffer );
+
+ // Return data
+ return pfile->buffer;
+ }
+
+private:
+
+ void DeleteSceneFiles()
+ {
+ while ( m_FileList.Size() > 0 )
+ {
+ CFlexSceneFile *file = m_FileList[ 0 ];
+ m_FileList.Remove( 0 );
+ filesystem->FreeOptimalReadBuffer( file->buffer );
+ delete file;
+ }
+ }
+
+ CUtlVector< CFlexSceneFile * > m_FileList;
+};
+
+// Singleton manager
+CFlexSceneFileManager g_FlexSceneFileManager( "CFlexSceneFileManager" );
+
+//-----------------------------------------------------------------------------
+// Purpose: Each CBaseFlex maintains a UtlRBTree of mappings, one for each loaded flex scene file it uses. This is used to
+// sort the entries in the RBTree
+// Input : lhs -
+// rhs -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseFlex::FlexSettingLessFunc( const FS_LocalToGlobal_t& lhs, const FS_LocalToGlobal_t& rhs )
+{
+ return lhs.m_Key < rhs.m_Key;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Since everyone shared a pSettinghdr now, we need to set up the localtoglobal mapping per entity, but
+// we just do this in memory with an array of integers (could be shorts, I suppose)
+// Input : *pSettinghdr -
+//-----------------------------------------------------------------------------
+void CBaseFlex::EnsureTranslations( const flexsettinghdr_t *pSettinghdr )
+{
+ Assert( pSettinghdr );
+
+ FS_LocalToGlobal_t entry( pSettinghdr );
+
+ unsigned short idx = m_LocalToGlobal.Find( entry );
+ if ( idx != m_LocalToGlobal.InvalidIndex() )
+ return;
+
+ entry.SetCount( pSettinghdr->numkeys );
+
+ for ( int i = 0; i < pSettinghdr->numkeys; ++i )
+ {
+ entry.m_Mapping[ i ] = FindFlexController( pSettinghdr->pLocalName( i ) );
+ }
+
+ m_LocalToGlobal.Insert( entry );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Look up instance specific mapping
+// Input : *pSettinghdr -
+// key -
+// Output : int
+//-----------------------------------------------------------------------------
+LocalFlexController_t CBaseFlex::FlexControllerLocalToGlobal( const flexsettinghdr_t *pSettinghdr, int key )
+{
+ FS_LocalToGlobal_t entry( pSettinghdr );
+
+ int idx = m_LocalToGlobal.Find( entry );
+ if ( idx == m_LocalToGlobal.InvalidIndex() )
+ {
+ // This should never happen!!!
+ Assert( 0 );
+ Warning( "Unable to find mapping for flexcontroller %i, settings %p on %i/%s\n", key, pSettinghdr, entindex(), GetClassname() );
+ EnsureTranslations( pSettinghdr );
+ idx = m_LocalToGlobal.Find( entry );
+ if ( idx == m_LocalToGlobal.InvalidIndex() )
+ {
+ Error( "CBaseFlex::FlexControllerLocalToGlobal failed!\n" );
+ }
+ }
+
+ FS_LocalToGlobal_t& result = m_LocalToGlobal[ idx ];
+ // Validate lookup
+ Assert( result.m_nCount != 0 && key < result.m_nCount );
+ LocalFlexController_t index = result.m_Mapping[ key ];
+ return index;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *filename -
+//-----------------------------------------------------------------------------
+const void *CBaseFlex::FindSceneFile( const char *filename )
+{
+ // Ask manager to get the globally cached scene instead.
+ return g_FlexSceneFileManager.FindSceneFile( this, filename, false );
+}
+
+ConVar ai_expression_optimization( "ai_expression_optimization", "0", FCVAR_NONE, "Disable npc background expressions when you can't see them." );
+ConVar ai_expression_frametime( "ai_expression_frametime", "0.05", FCVAR_NONE, "Maximum frametime to still play background expressions." );
+
+//-----------------------------------------------------------------------------
+// Various methods to process facial SceneEvents:
+//-----------------------------------------------------------------------------
+bool CBaseFlex::ProcessFlexAnimationSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
+{
+ VPROF( "CBaseFlex::ProcessFlexAnimationSceneEvent" );
+
+ if ( event->HasEndTime() )
+ {
+ // don't bother with flex animation if the player can't see you
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if (myNpc)
+ {
+ if (!myNpc->HasCondition( COND_IN_PVS ))
+ return true;
+
+ if (ai_expression_optimization.GetBool())
+ {
+ if (scene->IsBackground())
+ {
+ // if framerate too slow, disable
+ if (gpGlobals->frametime > ai_expression_frametime.GetFloat())
+ {
+ info->m_bHasArrived = true;
+ info->m_flNext = gpGlobals->curtime + RandomFloat( 0.7, 1.2 );
+ }
+ // only check occasionally
+ else if (info->m_flNext <= gpGlobals->curtime)
+ {
+ CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
+
+ // if not in view, disable
+ info->m_bHasArrived = (pPlayer && !pPlayer->FInViewCone( this ) );
+ info->m_flNext = gpGlobals->curtime + RandomFloat( 0.7, 1.2 );
+ }
+
+ if (info->m_bHasArrived)
+ {
+ // NDebugOverlay::Box( myNpc->GetAbsOrigin(), myNpc->GetHullMins(), myNpc->GetHullMaxs(), 255, 0, 0, 0, 0.22 );
+ return true;
+ }
+ // NDebugOverlay::Box( myNpc->GetAbsOrigin(), myNpc->GetHullMins(), myNpc->GetHullMaxs(), 0, 255, 0, 0, 0.22 );
+ }
+ }
+ }
+
+ AddFlexAnimation( info );
+ }
+ return true;
+}
+
+bool CBaseFlex::ProcessFlexSettingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
+{
+ // Flexanimations have to have an end time!!!
+ if ( !event->HasEndTime() )
+ return true;
+
+ VPROF( "CBaseFlex::ProcessFlexSettingSceneEvent" );
+
+ // Look up the actual strings
+ const char *scenefile = event->GetParameters();
+ const char *name = event->GetParameters2();
+
+ // Have to find both strings
+ if ( scenefile && name )
+ {
+ // Find the scene file
+ const flexsettinghdr_t *pExpHdr = ( const flexsettinghdr_t * )FindSceneFile( scenefile );
+ if ( pExpHdr )
+ {
+ float scenetime = scene->GetTime();
+
+ float scale = event->GetIntensity( scenetime );
+
+ // Add the named expression
+ AddFlexSetting( name, scale, pExpHdr, !info->m_bStarted );
+ }
+ }
+
+ return true;
+}
+
+bool CBaseFlex::ProcessFacingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
+{
+ // make sure target exists
+ if (info->m_hTarget == NULL)
+ return false;
+
+ VPROF( "CBaseFlex::ProcessFacingSceneEvent" );
+
+ // make sure we're still able to play this command
+ if (!EnterSceneSequence( scene, event, true ))
+ {
+ return false;
+ }
+
+ if (!info->m_bStarted)
+ {
+ info->m_flInitialYaw = GetLocalAngles().y;
+ }
+
+ // lock in place if aiming at self
+ if (info->m_hTarget == this)
+ {
+ return true;
+ }
+
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if (myNpc)
+ {
+ if (info->m_bIsMoving != IsMoving())
+ {
+ info->m_flInitialYaw = GetLocalAngles().y;
+ }
+ info->m_bIsMoving = IsMoving();
+
+ // Msg("%f : %f - %f\n", scene->GetTime(), event->GetStartTime(), event->GetEndTime() );
+ // FIXME: why are the splines ill behaved at the end?
+ float intensity = event->GetIntensity( scene->GetTime() );
+ if (info->m_bIsMoving)
+ {
+ myNpc->AddFacingTarget( info->m_hTarget, intensity, 0.2 );
+ }
+ else
+ {
+ float goalYaw = myNpc->CalcIdealYaw( info->m_hTarget->EyePosition() );
+
+ float diff = UTIL_AngleDiff( goalYaw, info->m_flInitialYaw );
+
+ float idealYaw = UTIL_AngleMod( info->m_flInitialYaw + diff * intensity );
+
+ // Msg("yaw %.1f : %.1f (%.1f)\n", info->m_flInitialYaw, idealYaw, intensity );
+
+ myNpc->GetMotor()->SetIdealYawAndUpdate( idealYaw );
+ }
+
+ return true;
+ }
+ return false;
+}
+
+static Activity DetermineExpressionMoveActivity( CChoreoEvent *event, CAI_BaseNPC *pNPC )
+{
+ Activity activity = ACT_WALK;
+ const char *sParam2 = event->GetParameters2();
+ if ( !sParam2 || !sParam2[0] )
+ return activity;
+
+ // Custom distance styles are appended to param2 with a space as a separator
+ const char *pszAct = Q_strstr( sParam2, " " );
+ if ( pszAct )
+ {
+ char szActName[256];
+ Q_strncpy( szActName, sParam2, sizeof(szActName) );
+ szActName[ (pszAct-sParam2) ] = '\0';
+ pszAct = szActName;
+ }
+ else
+ {
+ pszAct = sParam2;
+ }
+
+ if ( !Q_stricmp( pszAct, "Walk" ) )
+ {
+ activity = ACT_WALK;
+ }
+ else if ( !Q_stricmp( pszAct, "Run" ) )
+ {
+ activity = ACT_RUN;
+ }
+ else if ( !Q_stricmp( pszAct, "CrouchWalk" ) )
+ {
+ activity = ACT_WALK_CROUCH;
+ }
+ else
+ {
+ // Try and resolve the activity name
+ activity = (Activity)ActivityList_IndexForName( pszAct );
+ if ( activity == ACT_INVALID )
+ {
+ // Assume it's a sequence name
+ pNPC->m_iszSceneCustomMoveSeq = AllocPooledString( pszAct );
+ activity = ACT_SCRIPT_CUSTOM_MOVE;
+ }
+ }
+
+ return activity;
+}
+
+bool CBaseFlex::ProcessMoveToSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
+{
+ // make sure target exists
+ if (info->m_hTarget == NULL)
+ return false;
+
+ // FIXME: move to CBaseActor or BaseNPC
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if (!myNpc)
+ return false;
+
+ VPROF( "CBaseFlex::ProcessMoveToSceneEvent" );
+
+ // make sure we're still able to play this command
+ if (!EnterSceneSequence( scene, event, true ))
+ {
+ return false;
+ }
+
+ // lock in place if aiming at self
+ if (info->m_hTarget == this)
+ {
+ return true;
+ }
+
+ // If we're in a vehicle, make us exit and *then* begin the run
+ if ( myNpc->IsInAVehicle() )
+ {
+ // Make us exit and wait
+ myNpc->ExitVehicle();
+ return false;
+ }
+
+ const Task_t *pCurTask = myNpc->GetTask();
+ if (!info->m_bIsMoving && (!IsMoving() || pCurTask->iTask == TASK_STOP_MOVING) )
+ {
+ if ( pCurTask && (pCurTask->iTask == TASK_PLAY_SCENE || pCurTask->iTask == TASK_WAIT_FOR_MOVEMENT || pCurTask->iTask == TASK_STOP_MOVING ) )
+ {
+ Activity moveActivity = DetermineExpressionMoveActivity( event, myNpc );
+ // AI_NavGoal_t goal( info->m_hTarget->EyePosition(), moveActivity, AIN_HULL_TOLERANCE );
+ myNpc->SetTarget( info->m_hTarget );
+
+ float flDistTolerance;
+ flDistTolerance = myNpc->GetHullWidth() / 2.0;
+ // flDistTolerance = AIN_HULL_TOLERANCE;
+
+ if (event->m_bForceShortMovement)
+ {
+ flDistTolerance = 0.1f;
+ }
+
+ AI_NavGoal_t goal( GOALTYPE_TARGETENT, moveActivity, flDistTolerance, AIN_UPDATE_TARGET_POS );
+
+ float flDist = (info->m_hTarget->EyePosition() - GetAbsOrigin()).Length2D();
+
+ if (flDist > MAX( MAX( flDistTolerance, 0.1 ), event->GetDistanceToTarget()))
+ {
+ // Msg("flDist %.1f\n", flDist );
+ int result = false;
+
+ if ( !myNpc->IsUnreachable( info->m_hTarget ) )
+ {
+ result = myNpc->GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET );
+ if ( !result )
+ {
+ myNpc->RememberUnreachable( info->m_hTarget, 1.5 );
+ }
+ }
+
+ if (result)
+ {
+ myNpc->GetNavigator()->SetMovementActivity( moveActivity );
+ myNpc->GetNavigator()->SetArrivalDistance( event->GetDistanceToTarget() );
+ info->m_bIsMoving = true;
+ }
+ else
+ {
+ // need route build failure case
+ // Msg("actor %s unable to build route\n", STRING( myNpc->GetEntityName() ) );
+ // Assert(0);
+
+ if (developer.GetInt() > 0 && scene_showmoveto.GetBool())
+ {
+ Vector vTestPoint;
+ myNpc->GetMoveProbe()->FloorPoint( info->m_hTarget->EyePosition(), MASK_NPCSOLID, 0, -64, &vTestPoint );
+ NDebugOverlay::HorzArrow( GetAbsOrigin() + Vector( 0, 0, 1 ), vTestPoint + Vector( 0, 0, 1 ), 4, 255, 0, 255, 0, false, 0.12 );
+ NDebugOverlay::Box( vTestPoint, myNpc->GetHullMins(), myNpc->GetHullMaxs(), 255, 0, 255, 0, 0.12 );
+ }
+ }
+ }
+ else
+ {
+ info->m_bHasArrived = true;
+ }
+ }
+ }
+ else if (IsMoving())
+ {
+ // float flDist = (myNpc->GetNavigator()->GetGoalPos() - GetAbsOrigin()).Length2D();
+ float flDist = (info->m_hTarget->EyePosition() - GetAbsOrigin()).Length2D();
+
+ if (flDist <= event->GetDistanceToTarget())
+ {
+ myNpc->GetNavigator()->StopMoving( false ); // Stop moving
+ info->m_bHasArrived = true;
+ }
+ }
+ else
+ {
+ info->m_bIsMoving = false;
+ }
+
+ // show movement target
+ if (developer.GetInt() > 0 && scene_showmoveto.GetBool() && IsMoving())
+ {
+ Vector vecStart, vTestPoint;
+ vecStart = myNpc->GetNavigator()->GetGoalPos();
+
+ myNpc->GetMoveProbe()->FloorPoint( vecStart, MASK_NPCSOLID, 0, -64, &vTestPoint );
+
+ int r, g, b;
+ r = b = g = 0;
+ if ( myNpc->GetNavigator()->CanFitAtPosition( vTestPoint, MASK_NPCSOLID ) )
+ {
+ if ( myNpc->GetMoveProbe()->CheckStandPosition( vTestPoint, MASK_NPCSOLID ) )
+ {
+ if (event->IsResumeCondition())
+ {
+ g = 255;
+ }
+ else
+ {
+ r = 255; g = 255;
+ }
+ }
+ else
+ {
+ b = 255; g = 255;
+ }
+ }
+ else
+ {
+ r = 255;
+ }
+
+ NDebugOverlay::HorzArrow( GetAbsOrigin() + Vector( 0, 0, 1 ), vTestPoint + Vector( 0, 0, 1 ), 4, r, g, b, 0, false, 0.12 );
+ NDebugOverlay::Box( vTestPoint, myNpc->GetHullMins(), myNpc->GetHullMaxs(), r, g, b, 0, 0.12 );
+ }
+
+ // handled in task
+ return true;
+}
+
+bool CBaseFlex::ProcessLookAtSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
+{
+ VPROF( "CBaseFlex::ProcessLookAtSceneEvent" );
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if (myNpc && info->m_hTarget != NULL)
+ {
+ float intensity = event->GetIntensity( scene->GetTime() );
+
+ // clamp in-ramp to 0.3 seconds
+ float flDuration = scene->GetTime() - event->GetStartTime();
+ float flMaxIntensity = flDuration < 0.3f ? SimpleSpline( flDuration / 0.3f ) : 1.0f;
+ intensity = clamp( intensity, 0.0f, flMaxIntensity );
+
+ myNpc->AddLookTarget( info->m_hTarget, intensity, 0.1 );
+ if (developer.GetInt() > 0 && scene_showlook.GetBool() && info->m_hTarget)
+ {
+ Vector tmp = info->m_hTarget->EyePosition() - myNpc->EyePosition();
+ VectorNormalize( tmp );
+ Vector p0 = myNpc->EyePosition();
+ NDebugOverlay::VertArrow( p0, p0 + tmp * (4 + 16 * intensity ), 4, 255, 255, 255, 0, true, 0.12 );
+ }
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseFlex::ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
+{
+ VPROF( "CBaseFlex::ProcessSceneEvent" );
+ switch ( event->GetType() )
+ {
+ case CChoreoEvent::FLEXANIMATION:
+ return ProcessFlexAnimationSceneEvent( info, scene, event );
+
+ case CChoreoEvent::EXPRESSION:
+ return ProcessFlexSettingSceneEvent( info, scene, event );
+
+ case CChoreoEvent::SEQUENCE:
+ return ProcessSequenceSceneEvent( info, scene, event );
+
+ case CChoreoEvent::GESTURE:
+ return ProcessGestureSceneEvent( info, scene, event );
+
+ case CChoreoEvent::FACE:
+ return ProcessFacingSceneEvent( info, scene, event );
+
+ case CChoreoEvent::MOVETO:
+ return ProcessMoveToSceneEvent( info, scene, event );
+
+ case CChoreoEvent::LOOKAT:
+ return ProcessLookAtSceneEvent( info, scene, event );
+
+ case CChoreoEvent::SPEAK:
+ return true;
+
+ default:
+ {
+ Msg( "unknown type %d in ProcessSceneEvent()\n", event->GetType() );
+ Assert( 0 );
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseFlex::IsRunningSceneMoveToEvent()
+{
+ for ( int i = m_SceneEvents.Count() - 1; i >= 0; i-- )
+ {
+ CSceneEventInfo *info = &m_SceneEvents[ i ];
+ CChoreoEvent *event = info->m_pEvent;
+ if ( event && event->GetType() == CChoreoEvent::MOVETO )
+ return true;
+ }
+
+ return false;
+}
+
+
+flexsetting_t const *CBaseFlex::FindNamedSetting( flexsettinghdr_t const *pSettinghdr, const char *expr )
+{
+ int i;
+ const flexsetting_t *pSetting = NULL;
+
+ for ( i = 0; i < pSettinghdr->numflexsettings; i++ )
+ {
+ pSetting = pSettinghdr->pSetting( i );
+ if ( !pSetting )
+ continue;
+
+ const char *name = pSetting->pszName();
+
+ if ( !stricmp( name, expr ) )
+ break;
+ }
+
+ if ( i>=pSettinghdr->numflexsettings )
+ {
+ return NULL;
+ }
+
+ return pSetting;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *event -
+//-----------------------------------------------------------------------------
+void CBaseFlex::AddFlexAnimation( CSceneEventInfo *info )
+{
+ if ( !info )
+ return;
+
+ // don't bother with flex animation if the player can't see you
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+ if (myNpc && !myNpc->HasCondition( COND_IN_PVS ))
+ return;
+
+ CChoreoEvent *event = info->m_pEvent;
+ if ( !event )
+ return;
+
+ CChoreoScene *scene = info->m_pScene;
+ if ( !scene )
+ return;
+
+ if ( !event->GetTrackLookupSet() )
+ {
+ // Create lookup data
+ for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ if ( track->IsComboType() )
+ {
+ char name[ 512 ];
+ Q_strncpy( name, "right_" ,sizeof(name));
+ Q_strncat( name, track->GetFlexControllerName(),sizeof(name), COPY_ALL_CHARACTERS );
+
+ track->SetFlexControllerIndex( FindFlexController( name ), 0, 0 );
+
+ if ( CAI_BaseActor::IsServerSideFlexController( name ) )
+ {
+ Assert( !"Should stereo controllers ever be server side only?" );
+ track->SetServerSide( true );
+ }
+
+ Q_strncpy( name, "left_" ,sizeof(name));
+ Q_strncat( name, track->GetFlexControllerName(),sizeof(name), COPY_ALL_CHARACTERS );
+
+ track->SetFlexControllerIndex( FindFlexController( name ), 0, 1 );
+
+ if ( CAI_BaseActor::IsServerSideFlexController( name ) )
+ {
+ Assert( !"Should stereo controllers ever be server side only?" );
+ track->SetServerSide( true );
+ }
+ }
+ else
+ {
+ track->SetFlexControllerIndex( FindFlexController( (char *)track->GetFlexControllerName() ), 0 );
+
+ // Only non-combo tracks can be server side
+ track->SetServerSide( CAI_BaseActor::IsServerSideFlexController( track->GetFlexControllerName() ) );
+ }
+ }
+
+ event->SetTrackLookupSet( true );
+ }
+
+ float scenetime = scene->GetTime();
+ // decay if this is a background scene and there's other flex animations playing
+ float weight = event->GetIntensity( scenetime ) * info->UpdateWeight( this );
+ {
+ VPROF( "AddFlexAnimation_SetFlexWeight" );
+
+ // Compute intensity for each track in animation and apply
+ // Iterate animation tracks
+ for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
+ {
+ CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
+ if ( !track )
+ continue;
+
+ // Disabled
+ if ( !track->IsTrackActive() )
+ continue;
+
+ // If we are doing client side flexing, skip all tracks which are not server side
+ if ( g_bClientFlex && !track->IsServerSide() )
+ continue;
+
+ // Map track flex controller to global name
+ if ( track->IsComboType() )
+ {
+ for ( int side = 0; side < 2; side++ )
+ {
+ LocalFlexController_t controller = track->GetRawFlexControllerIndex( side );
+
+ // Get spline intensity for controller
+ float flIntensity = track->GetIntensity( scenetime, side );
+ if ( controller >= LocalFlexController_t(0) )
+ {
+ float orig = GetFlexWeight( controller );
+ SetFlexWeight( controller, orig * (1 - weight) + flIntensity * weight );
+ }
+ }
+ }
+ else
+ {
+ LocalFlexController_t controller = track->GetRawFlexControllerIndex( 0 );
+
+ // Get spline intensity for controller
+ float flIntensity = track->GetIntensity( scenetime, 0 );
+ if ( controller >= LocalFlexController_t(0) )
+ {
+ float orig = GetFlexWeight( controller );
+ SetFlexWeight( controller, orig * (1 - weight) + flIntensity * weight );
+ }
+ }
+ }
+ }
+
+ info->m_bStarted = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *expr -
+// scale -
+// *pSettinghdr -
+// newexpression -
+//-----------------------------------------------------------------------------
+void CBaseFlex::AddFlexSetting( const char *expr, float scale,
+ const flexsettinghdr_t *pSettinghdr, bool newexpression )
+{
+ int i;
+ const flexsetting_t *pSetting = NULL;
+
+ // Find the named setting in the base
+ for ( i = 0; i < pSettinghdr->numflexsettings; i++ )
+ {
+ pSetting = pSettinghdr->pSetting( i );
+ if ( !pSetting )
+ continue;
+
+ const char *name = pSetting->pszName();
+
+ if ( !stricmp( name, expr ) )
+ break;
+ }
+
+ if ( i>=pSettinghdr->numflexsettings )
+ {
+ return;
+ }
+
+ flexweight_t *pWeights = NULL;
+ int truecount = pSetting->psetting( (byte *)pSettinghdr, 0, &pWeights );
+ if ( !pWeights )
+ return;
+
+ for (i = 0; i < truecount; i++, pWeights++)
+ {
+ // Translate to local flex controller
+ // this is translating from the settings's local index to the models local index
+ LocalFlexController_t index = FlexControllerLocalToGlobal( pSettinghdr, pWeights->key );
+
+ // blend scaled weighting in to total
+ float s = clamp( scale * pWeights->influence, 0.0f, 1.0f );
+ float value = GetFlexWeight( index ) * (1.0f - s ) + pWeights->weight * s;
+ SetFlexWeight( index, value );
+ }
+}
+
+
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+bool CBaseFlex::ProcessGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !info || !event || !scene )
+ return false;
+
+ if ( info->m_iLayer == REQUEST_DEFERRED_LAYER_ALLOCATION )
+ {
+ HandleStartGestureSceneEvent( info, scene, event, info->m_pActor );
+ }
+
+ if (info->m_iLayer >= 0)
+ {
+ // this happens after StudioFrameAdvance()
+ // FIXME; this needs to be adjusted by npc offset to scene time? Known?
+ // FIXME: what should this do when the scene loops?
+ float duration = event->GetDuration( );
+ float flEventCycle = (scene->GetTime() - event->GetStartTime()) / duration;
+ float flCycle = event->GetOriginalPercentageFromPlaybackPercentage( flEventCycle );
+
+ SetLayerCycle( info->m_iLayer, flCycle );
+
+ float flWeight = event->GetIntensity( scene->GetTime() );
+
+ /*
+ if (stricmp( event->GetParameters(), "m_g_arms_crossed" ) == 0)
+ {
+ Msg("%.2f (%.2f) : %s : %.3f (%.3f) %.2f\n", scene->GetTime(), scene->GetTime() - event->GetStartTime(), event->GetParameters(), flCycle, flEventCycle, flWeight );
+ }
+ */
+
+ // fade out/in if npc is moving
+ if (!info->m_bIsGesture)
+ {
+ if (IsMoving())
+ {
+ info->m_flWeight = MAX( info->m_flWeight - 0.2, 0.0 );
+ }
+ else
+ {
+ info->m_flWeight = MIN( info->m_flWeight + 0.2, 1.0 );
+ }
+ }
+
+ // 3x^2-2x^3
+ float spline = 3 * info->m_flWeight * info->m_flWeight - 2 * info->m_flWeight * info->m_flWeight * info->m_flWeight;
+ SetLayerWeight( info->m_iLayer, flWeight * spline );
+
+ // update layer priority
+ if (m_bUpdateLayerPriorities)
+ {
+ SetLayerPriority( info->m_iLayer, info->m_iPriority + GetScenePriority( scene ) );
+ }
+
+ /*
+ Msg( "%d : %.2f (%.2f) : %.3f %.3f : %.3f\n",
+ info->m_iLayer,
+ scene->GetTime(),
+ (scene->GetTime() - event->GetStartTime()) / duration,
+ flCycle,
+ flNextCycle,
+ rate );
+ */
+ }
+
+ return true;
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *actor -
+// *parameters -
+//-----------------------------------------------------------------------------
+bool CBaseFlex::ProcessSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
+{
+ if ( !info || !event || !scene )
+ return false;
+
+ bool bNewlyAllocated = false;
+ if ( info->m_iLayer == REQUEST_DEFERRED_LAYER_ALLOCATION )
+ {
+ bool result = HandleStartSequenceSceneEvent( info, scene, event, info->m_pActor );
+ if (!result)
+ return false;
+ bNewlyAllocated = true;
+ }
+
+ if (info->m_iLayer >= 0)
+ {
+ float flWeight = event->GetIntensity( scene->GetTime() );
+
+ // force layer to zero weight in newly allocated, fixed bug with inter-think spawned sequences blending in badly
+ if (bNewlyAllocated)
+ flWeight = 0.0;
+
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+
+ // fade out/in if npc is moving
+
+ bool bFadeOut = IsMoving();
+ if (myNpc && !(myNpc->IsCurSchedule( SCHED_SCENE_GENERIC ) || myNpc->GetActivity() == ACT_IDLE_ANGRY || myNpc->GetActivity() == ACT_IDLE) )
+ {
+ bFadeOut = true;
+ if (info->m_flWeight == 1.0)
+ {
+ Warning( "%s playing CChoreoEvent::SEQUENCE but AI has forced them to do something different\n", STRING(GetEntityName()) );
+ }
+ }
+
+ if (bFadeOut)
+ {
+ info->m_flWeight = MAX( info->m_flWeight - 0.2, 0.0 );
+ }
+ else
+ {
+ info->m_flWeight = MIN( info->m_flWeight + 0.2, 1.0 );
+ }
+
+ float spline = 3 * info->m_flWeight * info->m_flWeight - 2 * info->m_flWeight * info->m_flWeight * info->m_flWeight;
+ SetLayerWeight( info->m_iLayer, flWeight * spline );
+
+ bool looping = ((GetSequenceFlags( GetModelPtr(), info->m_nSequence ) & STUDIO_LOOPING) != 0);
+ if (!looping)
+ {
+ float dt = scene->GetTime() - event->GetStartTime();
+ float seq_duration = SequenceDuration( info->m_nSequence );
+ float flCycle = dt / seq_duration;
+ flCycle = clamp( flCycle, 0.f, 1.0f );
+ SetLayerCycle( info->m_iLayer, flCycle );
+ }
+
+ if (myNpc)
+ {
+ myNpc->AddSceneLock( 0.2 );
+ }
+
+ // update layer priority
+ if (m_bUpdateLayerPriorities)
+ {
+ SetLayerPriority( info->m_iLayer, info->m_iPriority + GetScenePriority( scene ) );
+ }
+ }
+
+ // FIXME: clean up cycle index from restart
+ return true;
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if the actor is not currently in a scene OR if the actor
+// is in a scene (checked externally), but a PERMIT_RESPONSES event is active and
+// the permit time period has enough time remaining to handle the response in full.
+// Input : response_length -
+//-----------------------------------------------------------------------------
+bool CBaseFlex::PermitResponse( float response_length )
+{
+ // Nothing set, disallow it
+ if ( m_flAllowResponsesEndTime <= 0.0f )
+ {
+ return false;
+ }
+
+ // If response ends before end of allow time, then that's okay
+ if ( gpGlobals->curtime + response_length <= m_flAllowResponsesEndTime )
+ {
+ return true;
+ }
+
+ // Disallow responses for now
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set response end time (0 to clear response blocking)
+// Input : endtime -
+//-----------------------------------------------------------------------------
+void CBaseFlex::SetPermitResponse( float endtime )
+{
+ // Mark time after which we'll be occupied again (if in a scene)
+ // Note a value of <= 0.0f means unset (always allow actor to speak if not in a scene,
+ // and always disallow if in a scene)
+ m_flAllowResponsesEndTime = endtime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play a one-shot scene
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+float CBaseFlex::PlayScene( const char *pszScene, float flDelay, AI_Response *response, IRecipientFilter *filter /* = NULL */ )
+{
+ return InstancedScriptedScene( this, pszScene, NULL, flDelay, false, response, false, filter );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a one-shot scene in memory with one track which is to play the named sound on the actor
+// Input : *soundname -
+// Output : float
+//-----------------------------------------------------------------------------
+float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname )
+{
+ return InstancedAutoGeneratedSoundScene( this, soundname );
+}
+
+
+
+
+// FIXME: move to CBaseActor
+bool CBaseFlex::EnterSceneSequence( CChoreoScene *scene, CChoreoEvent *event, bool bRestart )
+{
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+
+ if (!myNpc)
+ {
+ // In multiplayer, we allow players to play scenes
+ if ( IsPlayer() )
+ return true;
+
+ return false;
+ }
+
+ // 2 seconds past current event, or 0.2 seconds past end of scene, whichever is shorter
+ float flDuration = MIN( 2.0, MIN( event->GetEndTime() - scene->GetTime() + 2.0, scene->FindStopTime() - scene->GetTime() + 0.2 ) );
+
+ if (myNpc->IsCurSchedule( SCHED_SCENE_GENERIC ))
+ {
+ myNpc->AddSceneLock( flDuration );
+ return true;
+ }
+
+ // for now, don't interrupt sequences that don't understand being interrupted
+ if (myNpc->GetCurSchedule())
+ {
+ CAI_ScheduleBits testBits;
+ myNpc->GetCurSchedule()->GetInterruptMask( &testBits );
+
+ testBits.Clear( COND_PROVOKED );
+
+ if (testBits.IsAllClear())
+ {
+ return false;
+ }
+ }
+
+ if (myNpc->IsInterruptable())
+ {
+ if (myNpc->m_hCine)
+ {
+ // Assert( !(myNpc->GetFlags() & FL_FLY ) );
+ myNpc->ExitScriptedSequence( );
+ }
+
+ myNpc->OnStartScene();
+ myNpc->SetSchedule( SCHED_SCENE_GENERIC );
+ myNpc->AddSceneLock( flDuration );
+ return true;
+ }
+
+ return false;
+}
+
+bool CBaseFlex::ExitSceneSequence( void )
+{
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: keep track of last valid flex animation time and returns if the current info should play theirs
+//-----------------------------------------------------------------------------
+
+bool CBaseFlex::IsSuppressedFlexAnimation( CSceneEventInfo *info )
+{
+ // check for suppression if the current info is a background
+ if (info->m_pScene && info->m_pScene->IsBackground())
+ {
+ // allow for slight jitter
+ return m_flLastFlexAnimationTime > gpGlobals->curtime - GetAnimTimeInterval() * 1.5;
+ }
+ // keep track of last non-suppressable flex animation
+ m_flLastFlexAnimationTime = gpGlobals->curtime;
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Clear out body lean states that are invalidated with Teleport
+//-----------------------------------------------------------------------------
+
+void CBaseFlex::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
+{
+ BaseClass::Teleport( newPosition, newAngles, newVelocity );
+#ifdef HL2_DLL
+
+ // clear out Body Lean
+ m_vecPrevOrigin = vec3_origin;
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: keep track of accel/decal and lean the body
+//-----------------------------------------------------------------------------
+
+void CBaseFlex::DoBodyLean( void )
+{
+#ifdef HL2_DLL
+ CAI_BaseNPC *myNpc = MyNPCPointer( );
+
+ if (myNpc)
+ {
+ Vector vecDelta;
+ Vector vecPos;
+ Vector vecOrigin = GetAbsOrigin();
+
+ if (m_vecPrevOrigin == vec3_origin)
+ {
+ m_vecPrevOrigin = vecOrigin;
+ }
+
+ vecDelta = vecOrigin - m_vecPrevOrigin;
+ vecDelta.x = clamp( vecDelta.x, -50, 50 );
+ vecDelta.y = clamp( vecDelta.y, -50, 50 );
+ vecDelta.z = clamp( vecDelta.z, -50, 50 );
+
+ float dt = gpGlobals->curtime - GetLastThink();
+ bool bSkip = ((GetFlags() & (FL_FLY | FL_SWIM)) != 0) || (GetMoveParent() != NULL) || (GetGroundEntity() == NULL) || (GetGroundEntity()->IsMoving());
+ bSkip |= myNpc->TaskRanAutomovement() || (myNpc->GetVehicleEntity() != NULL);
+
+ if (!bSkip)
+ {
+ if (vecDelta.LengthSqr() > m_vecPrevVelocity.LengthSqr())
+ {
+ float decay = ExponentialDecay( 0.6, 0.1, dt );
+ m_vecPrevVelocity = m_vecPrevVelocity * (decay) + vecDelta * (1.f - decay);
+ }
+ else
+ {
+ float decay = ExponentialDecay( 0.4, 0.1, dt );
+ m_vecPrevVelocity = m_vecPrevVelocity * (decay) + vecDelta * (1.f - decay);
+ }
+
+ vecPos = m_vecPrevOrigin + m_vecPrevVelocity;
+
+ float decay = ExponentialDecay( 0.5, 0.1, dt );
+ m_vecShift = m_vecShift * (decay) + (vecOrigin - vecPos) * (1.f - decay); // FIXME: Scale this
+ m_vecLean = (vecOrigin - vecPos) * 1.0; // FIXME: Scale this
+ }
+ else
+ {
+ m_vecPrevVelocity = vecDelta;
+ float decay = ExponentialDecay( 0.5, 0.1, dt );
+ m_vecShift = m_vecLean * decay;
+ m_vecLean = m_vecShift * decay;
+ }
+
+ m_vecPrevOrigin = vecOrigin;
+
+ /*
+ DevMsg( "%.2f %.2f %.2f (%.2f %.2f %.2f)\n",
+ m_vecLean.Get().x, m_vecLean.Get().y, m_vecLean.Get().z,
+ vecDelta.x, vecDelta.y, vecDelta.z );
+ */
+ }
+#endif
+}
+
+
+
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: initialize weight for background events
+//-----------------------------------------------------------------------------
+
+void CSceneEventInfo::InitWeight( CBaseFlex *pActor )
+{
+ if (pActor->IsSuppressedFlexAnimation( this ))
+ {
+ m_flWeight = 0.0;
+ }
+ else
+ {
+ m_flWeight = 1.0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: update weight for background events. Only call once per think
+//-----------------------------------------------------------------------------
+
+float CSceneEventInfo::UpdateWeight( CBaseFlex *pActor )
+{
+ // decay if this is a background scene and there's other flex animations playing
+ if (pActor->IsSuppressedFlexAnimation( this ))
+ {
+ m_flWeight = MAX( m_flWeight - 0.2, 0.0 );
+ }
+ else
+ {
+ m_flWeight = MIN( m_flWeight + 0.1, 1.0 );
+ }
+ return m_flWeight;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+
+class CFlexCycler : public CBaseFlex
+{
+private:
+ DECLARE_CLASS( CFlexCycler, CBaseFlex );
+public:
+ DECLARE_DATADESC();
+
+ CFlexCycler() { m_iszSentence = NULL_STRING; m_sentence = 0; }
+ void GenericCyclerSpawn(char *szModel, Vector vecMin, Vector vecMax);
+ virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | FCAP_IMPULSE_USE); }
+ int OnTakeDamage( const CTakeDamageInfo &info );
+ void Spawn( void );
+ void Think( void );
+
+ virtual void ProcessSceneEvents( void );
+
+ // Don't treat as a live target
+ virtual bool IsAlive( void ) { return FALSE; }
+
+ float m_flextime;
+ LocalFlexController_t m_flexnum;
+ float m_flextarget[64];
+ float m_blinktime;
+ float m_looktime;
+ Vector m_lookTarget;
+ float m_speaktime;
+ int m_istalking;
+ int m_phoneme;
+
+ string_t m_iszSentence;
+ int m_sentence;
+
+ void SetFlexTarget( LocalFlexController_t flexnum );
+ LocalFlexController_t LookupFlex( const char *szTarget );
+};
+
+BEGIN_DATADESC( CFlexCycler )
+
+ DEFINE_FIELD( m_flextime, FIELD_TIME ),
+ DEFINE_FIELD( m_flexnum, FIELD_INTEGER ),
+ DEFINE_ARRAY( m_flextarget, FIELD_FLOAT, 64 ),
+ DEFINE_FIELD( m_blinktime, FIELD_TIME ),
+ DEFINE_FIELD( m_looktime, FIELD_TIME ),
+ DEFINE_FIELD( m_lookTarget, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_speaktime, FIELD_TIME ),
+ DEFINE_FIELD( m_istalking, FIELD_INTEGER ),
+ DEFINE_FIELD( m_phoneme, FIELD_INTEGER ),
+ DEFINE_KEYFIELD( m_iszSentence, FIELD_STRING, "Sentence" ),
+ DEFINE_FIELD( m_sentence, FIELD_INTEGER ),
+
+END_DATADESC()
+
+
+//
+// we should get rid of all the other cyclers and replace them with this.
+//
+class CGenericFlexCycler : public CFlexCycler
+{
+public:
+ DECLARE_CLASS( CGenericFlexCycler, CFlexCycler );
+
+ void Spawn( void ) { GenericCyclerSpawn( (char *)STRING( GetModelName() ), Vector(-16, -16, 0), Vector(16, 16, 72) ); }
+};
+
+LINK_ENTITY_TO_CLASS( cycler_flex, CGenericFlexCycler );
+
+
+
+ConVar flex_expression( "flex_expression","-" );
+ConVar flex_talk( "flex_talk","0" );
+
+// Cycler member functions
+
+void CFlexCycler::GenericCyclerSpawn(char *szModel, Vector vecMin, Vector vecMax)
+{
+ if (!szModel || !*szModel)
+ {
+ Warning( "cycler at %.0f %.0f %0.f missing modelname\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
+ UTIL_Remove( this );
+ return;
+ }
+
+ PrecacheModel( szModel );
+ SetModel( szModel );
+
+ CFlexCycler::Spawn( );
+
+ UTIL_SetSize(this, vecMin, vecMax);
+
+ Vector vecEyeOffset;
+ GetEyePosition( GetModelPtr(), vecEyeOffset );
+ SetViewOffset( vecEyeOffset );
+
+ InitBoneControllers();
+
+ if (GetNumFlexControllers() < 5)
+ Warning( "cycler_flex used on model %s without enough flexes.\n", szModel );
+}
+
+void CFlexCycler::Spawn( )
+{
+ Precache();
+ /*
+ if ( m_spawnflags & FCYCLER_NOTSOLID )
+ {
+ SetSolid( SOLID_NOT );
+ }
+ else
+ {
+ SetSolid( SOLID_SLIDEBOX );
+ }
+ */
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+
+ SetMoveType( MOVETYPE_NONE );
+ m_takedamage = DAMAGE_YES;
+ m_iHealth = 80000;// no cycler should die
+
+ m_flPlaybackRate = 1.0f;
+ m_flGroundSpeed = 0;
+
+
+ SetNextThink( gpGlobals->curtime + 1.0f );
+
+ ResetSequenceInfo( );
+
+ m_flCycle = random->RandomFloat( 0, 1.0 );
+}
+
+const char *predef_flexcontroller_names[] = {
+ "right_lid_raiser",
+ "left_lid_raiser",
+ "right_lid_tightener",
+ "left_lid_tightener",
+ "right_lid_droop",
+ "left_lid_droop",
+ "right_inner_raiser",
+ "left_inner_raiser",
+ "right_outer_raiser",
+ "left_outer_raiser",
+ "right_lowerer",
+ "left_lowerer",
+ "right_cheek_raiser",
+ "left_cheek_raiser",
+ "wrinkler",
+ "right_upper_raiser",
+ "left_upper_raiser",
+ "right_corner_puller",
+ "left_corner_puller",
+ "corner_depressor",
+ "chin_raiser",
+ "right_puckerer",
+ "left_puckerer",
+ "right_funneler",
+ "left_funneler",
+ "tightener",
+ "jaw_clencher",
+ "jaw_drop",
+ "right_mouth_drop",
+ "left_mouth_drop",
+ NULL };
+
+float predef_flexcontroller_values[7][30] = {
+/* 0 */ { 0.700,0.560,0.650,0.650,0.650,0.585,0.000,0.000,0.400,0.040,0.000,0.000,0.450,0.450,0.000,0.000,0.000,0.750,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.150,1.000,0.000,0.000,0.000 },
+/* 1 */ { 0.450,0.450,0.450,0.450,0.000,0.000,0.000,0.000,0.300,0.300,0.000,0.000,0.250,0.250,0.000,0.000,0.000,0.750,0.750,0.000,0.000,0.000,0.000,0.400,0.400,0.000,1.000,0.000,0.050,0.050 },
+/* 2 */ { 0.200,0.200,0.500,0.500,0.150,0.150,0.100,0.100,0.150,0.150,0.000,0.000,0.700,0.700,0.000,0.000,0.000,0.750,0.750,0.000,0.200,0.000,0.000,0.000,0.000,0.000,0.850,0.000,0.000,0.000 },
+/* 3 */ { 0.000,0.000,0.000,0.000,0.000,0.000,0.300,0.300,0.000,0.000,0.000,0.000,0.000,0.000,0.100,0.000,0.000,0.000,0.000,0.700,0.300,0.000,0.000,0.200,0.200,0.000,0.000,0.300,0.000,0.000 },
+/* 4 */ { 0.450,0.450,0.000,0.000,0.450,0.450,0.000,0.000,0.000,0.000,0.450,0.450,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.300,0.000,0.000,0.000,0.000 },
+/* 5 */ { 0.000,0.000,0.350,0.350,0.150,0.150,0.300,0.300,0.450,0.450,0.000,0.000,0.200,0.200,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.200,0.200,0.000,0.000,0.300,0.000,0.000,0.000,0.000 },
+/* 6 */ { 0.000,0.000,0.650,0.650,0.750,0.750,0.000,0.000,0.000,0.000,0.300,0.300,0.000,0.000,0.000,0.250,0.250,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000 }
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Changes sequences when shot
+//-----------------------------------------------------------------------------
+int CFlexCycler::OnTakeDamage( const CTakeDamageInfo &info )
+{
+ int nSequence = GetSequence() + 1;
+ if (!IsValidSequence( nSequence ))
+ {
+ nSequence = 0;
+ }
+
+ ResetSequence( nSequence );
+ m_flCycle = 0;
+
+ return 0;
+}
+
+
+void CFlexCycler::SetFlexTarget( LocalFlexController_t flexnum )
+{
+ m_flextarget[flexnum] = random->RandomFloat( 0.5, 1.0 );
+
+ const char *pszType = GetFlexControllerType( flexnum );
+
+ // zero out all other flexes of the same type
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
+ {
+ if (i != flexnum)
+ {
+ const char *pszOtherType = GetFlexControllerType( i );
+ if (stricmp( pszType, pszOtherType ) == 0)
+ {
+ m_flextarget[i] = 0;
+ }
+ }
+ }
+
+ // HACK, for now, consider then linked is named "right_" or "left_"
+ if (strncmp( "right_", GetFlexControllerName( flexnum ), 6 ) == 0)
+ {
+ m_flextarget[flexnum+1] = m_flextarget[flexnum];
+ }
+ else if (strncmp( "left_", GetFlexControllerName( flexnum ), 5 ) == 0)
+ {
+ m_flextarget[flexnum-1] = m_flextarget[flexnum];
+ }
+}
+
+
+LocalFlexController_t CFlexCycler::LookupFlex( const char *szTarget )
+{
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
+ {
+ const char *pszFlex = GetFlexControllerName( i );
+ if (stricmp( szTarget, pszFlex ) == 0)
+ {
+ return i;
+ }
+ }
+ return LocalFlexController_t(-1);
+}
+
+
+void CFlexCycler::Think( void )
+{
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ StudioFrameAdvance ( );
+
+ if (IsSequenceFinished() && !SequenceLoops())
+ {
+ // ResetSequenceInfo();
+ // hack to avoid reloading model every frame
+ m_flAnimTime = gpGlobals->curtime;
+ m_flPlaybackRate = 1.0;
+ m_bSequenceFinished = false;
+ m_flLastEventCheck = 0;
+ m_flCycle = 0;
+ }
+
+ // only do this if they have more than eyelid movement
+ if (GetNumFlexControllers() > 2)
+ {
+ const char *pszExpression = flex_expression.GetString();
+
+ if (pszExpression && pszExpression[0] == '+' && pszExpression[1] != '\0')
+ {
+ int i;
+ int j = atoi( &pszExpression[1] );
+ for ( i = 0; i < GetNumFlexControllers(); i++)
+ {
+ m_flextarget[m_flexnum] = 0;
+ }
+
+ for (i = 0; i < 35 && predef_flexcontroller_names[i]; i++)
+ {
+ m_flexnum = LookupFlex( predef_flexcontroller_names[i] );
+ m_flextarget[m_flexnum] = predef_flexcontroller_values[j][i];
+ // Msg( "%s %.3f\n", predef_flexcontroller_names[i], predef_flexcontroller_values[j][i] );
+ }
+ }
+ else if ( pszExpression && (pszExpression[0] == '1') && (pszExpression[1] == '\0') ) // 1 for maxed controller values
+ {
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++ )
+ {
+ // Max everything out...
+ m_flextarget[i] = 1.0f;
+ SetFlexWeight( i, m_flextarget[i] );
+ }
+ }
+ else if ( pszExpression && (pszExpression[0] == '^') && (pszExpression[1] == '\0') ) // ^ for sine wave
+ {
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++ )
+ {
+ // Throw a differently offset sine wave on all of the flex controllers
+ float fFlexTime = i * (1.0f / (float)GetNumFlexControllers()) + gpGlobals->curtime;
+ m_flextarget[i] = sinf( fFlexTime ) * 0.5f + 0.5f;
+ SetFlexWeight( i, m_flextarget[i] );
+ }
+ }
+ else if (pszExpression && pszExpression[0] != '\0' && strcmp(pszExpression, "+") != 0)
+ {
+ char szExpression[128];
+ char szTemp[32];
+
+ Q_strncpy( szExpression, pszExpression ,sizeof(szExpression));
+ char *pszExpression = szExpression;
+
+ while (*pszExpression != '\0')
+ {
+ if (*pszExpression == '+')
+ *pszExpression = ' ';
+
+ pszExpression++;
+ }
+
+ pszExpression = szExpression;
+
+ while (*pszExpression)
+ {
+ if (*pszExpression != ' ')
+ {
+ if (*pszExpression == '-')
+ {
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
+ {
+ m_flextarget[i] = 0;
+ }
+ }
+ else if (*pszExpression == '?')
+ {
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
+ {
+ Msg( "\"%s\" ", GetFlexControllerName( i ) );
+ }
+ Msg( "\n" );
+ flex_expression.SetValue( "" );
+ }
+ else
+ {
+ if (sscanf( pszExpression, "%31s", szTemp ) == 1)
+ {
+ m_flexnum = LookupFlex( szTemp );
+
+ if (m_flexnum != -1 && m_flextarget[m_flexnum] != 1)
+ {
+ m_flextarget[m_flexnum] = 1.0;
+ // SetFlexTarget( m_flexnum );
+ }
+ pszExpression += strlen( szTemp ) - 1;
+ }
+ }
+ }
+ pszExpression++;
+ }
+ }
+ else if (m_flextime < gpGlobals->curtime)
+ {
+ // m_flextime = gpGlobals->curtime + 1.0; // RandomFloat( 0.1, 0.5 );
+ m_flextime = gpGlobals->curtime + random->RandomFloat( 0.3, 0.5 ) * (30.0 / GetNumFlexControllers());
+ m_flexnum = (LocalFlexController_t)random->RandomInt( 0, GetNumFlexControllers() - 1 );
+
+ // m_flexnum = (pflex->num + 1) % r_psubmodel->numflexes;
+
+ if (m_flextarget[m_flexnum] == 1)
+ {
+ m_flextarget[m_flexnum] = 0;
+ // pflex->time = cl.time + 0.1;
+ }
+ else if (stricmp( GetFlexControllerType( m_flexnum ), "phoneme" ) != 0)
+ {
+ if (strstr( GetFlexControllerName( m_flexnum ), "upper_raiser" ) == NULL)
+ {
+ Msg( "%s:%s\n", GetFlexControllerType( m_flexnum ), GetFlexControllerName( m_flexnum ) );
+ SetFlexTarget( m_flexnum );
+ }
+ }
+
+#if 0
+ char szWhat[256];
+ szWhat[0] = '\0';
+ for (int i = 0; i < GetNumFlexControllers(); i++)
+ {
+ if (m_flextarget[i] == 1.0)
+ {
+ if (stricmp( GetFlexFacs( i ), "upper") != 0 && stricmp( GetFlexFacs( i ), "lower") != 0)
+ {
+ if (szWhat[0] == '\0')
+ Q_strncat( szWhat, "-", sizeof( szWhat ), COPY_ALL_CHARACTERS );
+ else
+ Q_strncat( szWhat, "+", sizeof( szWhat ), COPY_ALL_CHARACTERS );
+ Q_strncat( szWhat, GetFlexFacs( i ), sizeof( szWhat ), COPY_ALL_CHARACTERS );
+ }
+ }
+ }
+ Msg( "%s\n", szWhat );
+#endif
+ }
+
+ // slide it up.
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
+ {
+ float weight = GetFlexWeight( i );
+
+ if (weight != m_flextarget[i])
+ {
+ weight = weight + (m_flextarget[i] - weight) / random->RandomFloat( 2.0, 4.0 );
+ }
+ weight = clamp( weight, 0.0f, 1.0f );
+ SetFlexWeight( i, weight );
+ }
+
+#if 1
+ if (flex_talk.GetInt() == -1)
+ {
+ m_istalking = 1;
+ char pszSentence[256];
+ Q_snprintf( pszSentence,sizeof(pszSentence), "%s%d", STRING(m_iszSentence), m_sentence++ );
+ int sentenceIndex = engine->SentenceIndexFromName( pszSentence );
+ if (sentenceIndex >= 0)
+ {
+ Msg( "%d : %s\n", sentenceIndex, pszSentence );
+ CPASAttenuationFilter filter( this );
+ CBaseEntity::EmitSentenceByIndex( filter, entindex(), CHAN_VOICE, sentenceIndex, 1, SNDLVL_TALKING, 0, PITCH_NORM );
+ }
+ else
+ {
+ m_sentence = 0;
+ }
+
+ flex_talk.SetValue( "0" );
+ }
+ else if (!FStrEq( flex_talk.GetString(), "0") )
+ {
+ int sentenceIndex = engine->SentenceIndexFromName( flex_talk.GetString() );
+ if (sentenceIndex >= 0)
+ {
+ CPASAttenuationFilter filter( this );
+ CBaseEntity::EmitSentenceByIndex( filter, entindex(), CHAN_VOICE, sentenceIndex, 1, SNDLVL_TALKING, 0, PITCH_NORM );
+ }
+ flex_talk.SetValue( "0" );
+ }
+#else
+ if (flex_talk.GetInt())
+ {
+ if (m_speaktime < gpGlobals->curtime)
+ {
+ if (m_phoneme == 0)
+ {
+ for (m_phoneme = 0; m_phoneme < GetNumFlexControllers(); m_phoneme++)
+ {
+ if (stricmp( GetFlexFacs( m_phoneme ), "27") == 0)
+ break;
+ }
+ }
+ m_istalking = !m_istalking;
+ if (m_istalking)
+ {
+ m_looktime = gpGlobals->curtime - 1.0;
+ m_speaktime = gpGlobals->curtime + random->RandomFloat( 0.5, 2.0 );
+ }
+ else
+ {
+ m_speaktime = gpGlobals->curtime + random->RandomFloat( 1.0, 3.0 );
+ }
+ }
+
+ for (i = m_phoneme; i < GetNumFlexControllers(); i++)
+ {
+ SetFlexWeight( i, 0.0f );
+ }
+
+ if (m_istalking)
+ {
+ m_flextime = gpGlobals->curtime + random->RandomFloat( 0.0, 0.2 );
+ m_flexWeight[random->RandomInt(m_phoneme, GetNumFlexControllers()-1)] = random->RandomFloat( 0.5, 1.0 );
+ float mouth = random->RandomFloat( 0.0, 1.0 );
+ float jaw = random->RandomFloat( 0.0, 1.0 );
+
+ m_flexWeight[m_phoneme - 2] = jaw * (mouth);
+ m_flexWeight[m_phoneme - 1] = jaw * (1.0 - mouth);
+ }
+ }
+ else
+ {
+ m_istalking = 0;
+ }
+#endif
+
+ // blink
+ if (m_blinktime < gpGlobals->curtime)
+ {
+ Blink();
+ m_blinktime = gpGlobals->curtime + random->RandomFloat( 1.5, 4.5 );
+ }
+ }
+
+
+ Vector forward, right, up;
+ GetVectors( &forward, &right, &up );
+
+ CBaseEntity *pPlayer = (CBaseEntity *)UTIL_GetLocalPlayer();
+ if (pPlayer)
+ {
+ if (pPlayer->GetSmoothedVelocity().Length() != 0 && DotProduct( forward, pPlayer->EyePosition() - EyePosition()) > 0.5)
+ {
+ m_lookTarget = pPlayer->EyePosition();
+ m_looktime = gpGlobals->curtime + random->RandomFloat(2.0,4.0);
+ }
+ else if (m_looktime < gpGlobals->curtime)
+ {
+ if ((!m_istalking) && random->RandomInt( 0, 1 ) == 0)
+ {
+ m_lookTarget = EyePosition() + forward * 128 + right * random->RandomFloat(-64,64) + up * random->RandomFloat(-32,32);
+ m_looktime = gpGlobals->curtime + random->RandomFloat(0.3,1.0);
+
+ if (m_blinktime - 0.5 < gpGlobals->curtime)
+ {
+ Blink();
+ }
+ }
+ else
+ {
+ m_lookTarget = pPlayer->EyePosition();
+ m_looktime = gpGlobals->curtime + random->RandomFloat(1.0,4.0);
+ }
+ }
+
+#if 0
+ float dt = acos( DotProduct( (m_lookTarget - EyePosition()).Normalize(), (m_viewtarget - EyePosition()).Normalize() ) );
+
+ if (dt > M_PI / 4)
+ {
+ dt = (M_PI / 4) * dt;
+ m_viewtarget = ((1 - dt) * m_viewtarget + dt * m_lookTarget);
+ }
+#endif
+
+ SetViewtarget( m_lookTarget );
+ }
+
+ // Handle any facial animation from scene playback
+ // FIXME: do we still actually need flex cyclers?
+ // AddSceneSceneEvents();
+}
+
+
+void CFlexCycler::ProcessSceneEvents( void )
+{
+ // Don't do anything since we handle facial stuff in Think()
+}
+
+
+BEGIN_BYTESWAP_DATADESC( flexsettinghdr_t )
+ DEFINE_FIELD( id, FIELD_INTEGER ),
+ DEFINE_FIELD( version, FIELD_INTEGER ),
+ DEFINE_ARRAY( name, FIELD_CHARACTER, 64 ),
+ DEFINE_FIELD( length, FIELD_INTEGER ),
+ DEFINE_FIELD( numflexsettings, FIELD_INTEGER ),
+ DEFINE_FIELD( flexsettingindex, FIELD_INTEGER ),
+ DEFINE_FIELD( nameindex, FIELD_INTEGER ),
+ DEFINE_FIELD( numindexes, FIELD_INTEGER ),
+ DEFINE_FIELD( indexindex, FIELD_INTEGER ),
+ DEFINE_FIELD( numkeys, FIELD_INTEGER ),
+ DEFINE_FIELD( keynameindex, FIELD_INTEGER ),
+ DEFINE_FIELD( keymappingindex, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( flexsetting_t )
+ DEFINE_FIELD( nameindex, FIELD_INTEGER ),
+ DEFINE_FIELD( obsolete1, FIELD_INTEGER ),
+ DEFINE_FIELD( numsettings, FIELD_INTEGER ),
+ DEFINE_FIELD( index, FIELD_INTEGER ),
+ DEFINE_FIELD( obsolete2, FIELD_INTEGER ),
+ DEFINE_FIELD( settingindex, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( flexweight_t )
+ DEFINE_FIELD( key, FIELD_INTEGER ),
+ DEFINE_FIELD( weight, FIELD_FLOAT ),
+ DEFINE_FIELD( influence, FIELD_FLOAT ),
+END_BYTESWAP_DATADESC()
+