text: //========= Copyright Valve Corporation, All rights reserved. ============//
  //
  // Purpose: 
  //
  // $Workfile:     $
  // $NoKeywords: $
  //=============================================================================//
  #ifndef C_BASEANIMATING_H
  #define C_BASEANIMATING_H
  
  #ifdef _WIN32
  #pragma once
  #endif
  
  #include "c_baseentity.h"
  #include "studio.h"
  #include "utlvector.h"
  #include "ragdoll.h"
  #include "mouthinfo.h"
  // Shared activities
  #include "ai_activity.h"
  #include "animationlayer.h"
  #include "sequence_Transitioner.h"
  #include "bone_accessor.h"
  #include "bone_merge_cache.h"
  #include "ragdoll_shared.h"
  #include "tier0/threadtools.h"
  #include "datacache/idatacache.h"
  
  #define LIPSYNC_POSEPARAM_NAME "mouth"
  #define NUM_HITBOX_FIRES	10
  
  /*
  class C_BaseClientShader
  {
  	virtual void RenderMaterial( C_BaseEntity *pEntity, int count, const vec4_t *verts, const vec4_t *normals, const vec2_t *texcoords, vec4_t *lightvalues );
  };
  */
  
  class IRagdoll;
  class CIKContext;
  class CIKState;
  class ConVar;
  class C_RopeKeyframe;
  class CBoneBitList;
  class CBoneList;
  class KeyValues;
  class CJiggleBones;
  class IBoneSetup;
  FORWARD_DECLARE_HANDLE( memhandle_t );
  typedef unsigned short MDLHandle_t;
  
  extern ConVar vcollide_wireframe;
  
  
  struct ClientModelRenderInfo_t : public ModelRenderInfo_t
  {
  	// Added space for lighting origin override. Just allocated space, need to set base pointer
  	matrix3x4_t lightingOffset;
  
  	// Added space for model to world matrix. Just allocated space, need to set base pointer
  	matrix3x4_t modelToWorld;
  };
  
  struct RagdollInfo_t
  {
  	bool		m_bActive;
  	float		m_flSaveTime;
  	int			m_nNumBones;
  	Vector		m_rgBonePos[MAXSTUDIOBONES];
  	Quaternion	m_rgBoneQuaternion[MAXSTUDIOBONES];
  };
  
  
  class CAttachmentData
  {
  public:
  	matrix3x4_t	m_AttachmentToWorld;
  	QAngle	m_angRotation;
  	Vector	m_vOriginVelocity;
  	int		m_nLastFramecount : 31;
  	int		m_bAnglesComputed : 1;
  };
  
  
  typedef unsigned int			ClientSideAnimationListHandle_t;
  
  #define		INVALID_CLIENTSIDEANIMATION_LIST_HANDLE	(ClientSideAnimationListHandle_t)~0
  
  
  class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback
  {
  public:
  	DECLARE_CLASS( C_BaseAnimating, C_BaseEntity );
  	DECLARE_CLIENTCLASS();
  	DECLARE_PREDICTABLE();
  	DECLARE_INTERPOLATION();
  
  	enum
  	{
  		NUM_POSEPAREMETERS = 24,
  		NUM_BONECTRLS = 4
  	};
  
  	C_BaseAnimating();
  	~C_BaseAnimating();
  
  	virtual C_BaseAnimating*		GetBaseAnimating() { return this; }
  
  	bool UsesPowerOfTwoFrameBufferTexture( void );
  
  	virtual bool	Interpolate( float currentTime );
  	virtual void	Simulate();	
  	virtual void	Release();	
  
  	float	GetAnimTimeInterval( void ) const;
  
  	virtual unsigned char	GetClientSideFade( void );
  
  	// Get bone controller values.
  	virtual void	GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS]);
  	virtual float	SetBoneController ( int iController, float flValue );
  
  	LocalFlexController_t GetNumFlexControllers( void );
  	const char *GetFlexDescFacs( int iFlexDesc );
  	const char *GetFlexControllerName( LocalFlexController_t iFlexController );
  	const char *GetFlexControllerType( LocalFlexController_t iFlexController );
  
  	virtual void	GetAimEntOrigin( IClientEntity *pAttachedTo, Vector *pAbsOrigin, QAngle *pAbsAngles );
  
  	// Computes a box that surrounds all hitboxes
  	bool ComputeHitboxSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs );
  	bool ComputeEntitySpaceHitboxSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs );
  
  	// Gets the hitbox-to-world transforms, returns false if there was a problem
  	bool HitboxToWorldTransforms( matrix3x4_t *pHitboxToWorld[MAXSTUDIOBONES] );
  
  	// base model functionality
  	float		  ClampCycle( float cycle, bool isLooping );
  	virtual void GetPoseParameters( CStudioHdr *pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM] );
  	virtual void BuildTransformations( CStudioHdr *pStudioHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed );
  	virtual void ApplyBoneMatrixTransform( matrix3x4_t& transform );
   	virtual int	VPhysicsGetObjectList( IPhysicsObject **pList, int listMax );
  
  	// model specific
  	virtual bool SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime );
  	virtual void UpdateIKLocks( float currentTime );
  	virtual void CalculateIKLocks( float currentTime );
  	virtual bool ShouldDraw();
  	virtual int DrawModel( int flags );
  	virtual int	InternalDrawModel( int flags );
  	virtual bool OnInternalDrawModel( ClientModelRenderInfo_t *pInfo );
  	virtual bool OnPostInternalDrawModel( ClientModelRenderInfo_t *pInfo );
  	void		DoInternalDrawModel( ClientModelRenderInfo_t *pInfo, DrawModelState_t *pState, matrix3x4_t *pBoneToWorldArray = NULL );
  
  	//
  	virtual CMouthInfo *GetMouth();
  	virtual void	ControlMouth( CStudioHdr *pStudioHdr );
  	
  	// override in sub-classes
  	virtual void DoAnimationEvents( CStudioHdr *pStudio );
  	virtual void FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options );
  	virtual void FireObsoleteEvent( const Vector& origin, const QAngle& angles, int event, const char *options );
  	virtual const char* ModifyEventParticles( const char* token ) { return token; }
  
  	// Parses and distributes muzzle flash events
  	virtual bool DispatchMuzzleEffect( const char *options, bool isFirstPerson );
  
  	// virtual	void AllocateMaterials( void );
  	// virtual	void FreeMaterials( void );
  
  	virtual void ValidateModelIndex( void );
  	virtual CStudioHdr *OnNewModel( void );
  	CStudioHdr	*GetModelPtr() const;
  	void InvalidateMdlCache();
  	
  	virtual void SetPredictable( bool state );
  	void UseClientSideAnimation();
  
  	// C_BaseClientShader **p_ClientShaders;
  
  	virtual	void StandardBlendingRules( CStudioHdr *pStudioHdr, Vector pos[], Quaternion q[], float currentTime, int boneMask );
  	void UnragdollBlend( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime );
  
  	void MaintainSequenceTransitions( IBoneSetup &boneSetup, float flCycle, Vector pos[], Quaternion q[] );
  	virtual void AccumulateLayers( IBoneSetup &boneSetup, Vector pos[], Quaternion q[], float currentTime );
  
  	virtual void ChildLayerBlend( Vector pos[], Quaternion q[], float currentTime, int boneMask );
  
  	// Attachments
  	int		LookupAttachment( const char *pAttachmentName );
  	int		LookupRandomAttachment( const char *pAttachmentNameSubstring );
  
  	int		LookupPoseParameter( CStudioHdr *pStudioHdr, const char *szName );
  	inline int LookupPoseParameter( const char *szName ) { return LookupPoseParameter(GetModelPtr(), szName); }
  
  	float	SetPoseParameter( CStudioHdr *pStudioHdr, const char *szName, float flValue );
  	inline float SetPoseParameter( const char *szName, float flValue ) { return SetPoseParameter( GetModelPtr(), szName, flValue ); }
  	float	SetPoseParameter( CStudioHdr *pStudioHdr, int iParameter, float flValue );
  	inline float SetPoseParameter( int iParameter, float flValue ) { return SetPoseParameter( GetModelPtr(), iParameter, flValue ); }
  
  	float	GetPoseParameter( int iPoseParameter );
  
  	bool	GetPoseParameterRange( int iPoseParameter, float &minValue, float &maxValue );
  
  	int		LookupBone( const char *szName );
  	void	GetBonePosition( int iBone, Vector &origin, QAngle &angles );
  	void	GetBoneTransform( int iBone, matrix3x4_t &pBoneToWorld );
  
  	//=============================================================================
  	// HPE_BEGIN:
  	// [menglish] Finds the bone associated with the given hitbox
  	//=============================================================================
  
  	int		GetHitboxBone( int hitboxIndex );
  
  	//=============================================================================
  	// HPE_END
  	//=============================================================================
  
  	// Bone attachments
  	virtual void		AttachEntityToBone( C_BaseAnimating* attachTarget, int boneIndexAttached=-1, Vector bonePosition=Vector(0,0,0), QAngle boneAngles=QAngle(0,0,0) );
  	void				AddBoneAttachment( C_BaseAnimating* newBoneAttachment );
  	void				RemoveBoneAttachment( C_BaseAnimating* boneAttachment );
  	void				RemoveBoneAttachments();
  	void				DestroyBoneAttachments();
  	void				MoveBoneAttachments( C_BaseAnimating* attachTarget );
  	int					GetNumBoneAttachments();
  	C_BaseAnimating*	GetBoneAttachment( int i );
  	virtual void		NotifyBoneAttached( C_BaseAnimating* attachTarget );
  	virtual void		UpdateBoneAttachments( int flags );
  
  	//bool solveIK(float a, float b, const Vector &Foot, const Vector &Knee1, Vector &Knee2);
  	//void DebugIK( mstudioikchain_t *pikchain );
  
  	virtual void					PreDataUpdate( DataUpdateType_t updateType );
  	virtual void					PostDataUpdate( DataUpdateType_t updateType );
  	virtual int						RestoreData( const char *context, int slot, int type );
  
  	virtual void					NotifyShouldTransmit( ShouldTransmitState_t state );
  	virtual void					OnPreDataChanged( DataUpdateType_t updateType );
  	virtual void					OnDataChanged( DataUpdateType_t updateType );
  	virtual void					AddEntity( void );
  
  	// This can be used to force client side animation to be on. Only use if you know what you're doing!
  	// Normally, the server entity should set this.
  	void							ForceClientSideAnimationOn();
  	
  	void							AddToClientSideAnimationList();
  	void							RemoveFromClientSideAnimationList();
  
  	virtual bool					IsSelfAnimating();
  	virtual void					ResetLatched();
  
  	// implements these so ragdolls can handle frustum culling & leaf visibility
  	virtual void					GetRenderBounds( Vector& theMins, Vector& theMaxs );
  	virtual const Vector&			GetRenderOrigin( void );
  	virtual const QAngle&			GetRenderAngles( void );
  
  	virtual bool					GetSoundSpatialization( SpatializationInfo_t& info );
  
  	// Attachments.
  	bool							GetAttachment( const char *szName, Vector &absOrigin );
  	bool							GetAttachment( const char *szName, Vector &absOrigin, QAngle &absAngles );
  
  	// Inherited from C_BaseEntity
  	virtual bool					GetAttachment( int number, Vector &origin );
  	virtual bool					GetAttachment( int number, Vector &origin, QAngle &angles );
  	virtual bool					GetAttachment( int number, matrix3x4_t &matrix );
  	virtual bool					GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel );
  	
  	// Returns the attachment in local space
  	bool							GetAttachmentLocal( int iAttachment, matrix3x4_t &attachmentToLocal );
  	bool							GetAttachmentLocal( int iAttachment, Vector &origin, QAngle &angles );
  	bool                            GetAttachmentLocal( int iAttachment, Vector &origin );
  
  	bool							GetRootBone( matrix3x4_t &rootBone );
  
  	// Should this object cast render-to-texture shadows?
  	virtual ShadowType_t			ShadowCastType();
  
  	// Should we collide?
  	virtual CollideType_t			GetCollideType( void );
  
  	virtual bool					TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr );
  	virtual bool					TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr );
  
  	// returns true if we're currently being ragdolled
  	bool							IsRagdoll() const;
  	bool							IsAboutToRagdoll() const;
  	virtual C_BaseAnimating			*BecomeRagdollOnClient();
  	C_BaseAnimating					*CreateRagdollCopy();
  	bool							InitAsClientRagdoll( const matrix3x4_t *pDeltaBones0, const matrix3x4_t *pDeltaBones1, const matrix3x4_t *pCurrentBonePosition, float boneDt, bool bFixedConstraints=false );
  	void							IgniteRagdoll( C_BaseAnimating *pSource );
  	void							TransferDissolveFrom( C_BaseAnimating *pSource );
  	virtual void					SaveRagdollInfo( int numbones, const matrix3x4_t &cameraTransform, CBoneAccessor &pBoneToWorld );
  	virtual bool					RetrieveRagdollInfo( Vector *pos, Quaternion *q );
  	virtual void					Clear( void );
  	void							ClearRagdoll();
  	void							CreateUnragdollInfo( C_BaseAnimating *pRagdoll );
  	void							ForceSetupBonesAtTime( matrix3x4_t *pBonesOut, float flTime );
  	virtual void					GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matrix3x4_t *pDeltaBones1, matrix3x4_t *pCurrentBones, float boneDt );
  
  	// For shadows rendering the correct body + sequence...
  	virtual int GetBody()			{ return m_nBody; }
  	virtual int GetSkin()			{ return m_nSkin; }
  
  	bool IsOnFire() { return ( (GetFlags() & FL_ONFIRE) != 0 ); }
  
  	inline float					GetPlaybackRate();
  	inline void						SetPlaybackRate( float rate );
  
  	void							SetModelScale( float scale, float change_duration = 0.0f  );
  	float							GetModelScale() const { return m_flModelScale; }
  	inline bool						IsModelScaleFractional() const;  /// very fast way to ask if the model scale is < 1.0f  (faster than if (GetModelScale() < 1.0f) )
  	inline bool						IsModelScaled() const;
  	void							UpdateModelScale( void );
  	virtual	void					RefreshCollisionBounds( void );
  
  	int								GetSequence();
  	virtual void					SetSequence(int nSequence);
  	inline void						ResetSequence(int nSequence);
  	float							GetSequenceGroundSpeed( CStudioHdr *pStudioHdr, int iSequence );
  	inline float					GetSequenceGroundSpeed( int iSequence ) { return GetSequenceGroundSpeed(GetModelPtr(), iSequence); }
  	bool							IsSequenceLooping( CStudioHdr *pStudioHdr, int iSequence );
  	inline bool						IsSequenceLooping( int iSequence ) { return IsSequenceLooping(GetModelPtr(),iSequence); }
  	float							GetSequenceMoveDist( CStudioHdr *pStudioHdr, int iSequence );
  	void							GetSequenceLinearMotion( int iSequence, Vector *pVec );
  	void							GetBlendedLinearVelocity( Vector *pVec );
  	int								LookupSequence ( const char *label );
  	int								LookupActivity( const char *label );
  	char const						*GetSequenceName( int iSequence ); 
  	char const						*GetSequenceActivityName( int iSequence );
  	Activity						GetSequenceActivity( int iSequence );
  	KeyValues						*GetSequenceKeyValues( int iSequence );
  	virtual void					StudioFrameAdvance(); // advance animation frame to some time in the future
  
  	// Clientside animation
  	virtual float					FrameAdvance( float flInterval = 0.0f );
  	virtual float					GetSequenceCycleRate( CStudioHdr *pStudioHdr, int iSequence );
  	virtual void					UpdateClientSideAnimation();
  	void							ClientSideAnimationChanged();
  	virtual unsigned int			ComputeClientSideAnimationFlags();
  
  	virtual void ResetClientsideFrame( void ) { SetCycle( 0 ); }
  
  	void SetCycle( float flCycle );
  	float GetCycle() const;
  
  	void SetBodygroup( int iGroup, int iValue );
  	int GetBodygroup( int iGroup );
  
  	const char *GetBodygroupName( int iGroup );
  	int FindBodygroupByName( const char *name );
  	int GetBodygroupCount( int iGroup );
  	int GetNumBodyGroups( void );
  
  	class CBoneCache				*GetBoneCache( CStudioHdr *pStudioHdr );
  	void							SetHitboxSet( int setnum );
  	void							SetHitboxSetByName( const char *setname );
  	int								GetHitboxSet( void );
  	char const						*GetHitboxSetName( void );
  	int								GetHitboxSetCount( void );
  	void							DrawClientHitboxes( float duration = 0.0f, bool monocolor = false );
  
  	C_BaseAnimating*				FindFollowedEntity();
  
  	virtual bool					IsActivityFinished( void ) { return m_bSequenceFinished; }
  	inline bool						IsSequenceFinished( void );
  	inline bool						SequenceLoops( void ) { return m_bSequenceLoops; }
  
  	// All view model attachments origins are stretched so you can place entities at them and
  	// they will match up with where the attachment winds up being drawn on the view model, since
  	// the view models are drawn with a different FOV.
  	//
  	// If you're drawing something inside of a view model's DrawModel() function, then you want the
  	// original attachment origin instead of the adjusted one. To get that, call this on the 
  	// adjusted attachment origin.
  	virtual void					UncorrectViewModelAttachment( Vector &vOrigin ) {}
  
  	// Call this if SetupBones() has already been called this frame but you need to move the
  	// entity and rerender.
  	void							InvalidateBoneCache();
  	bool							IsBoneCacheValid() const;	// Returns true if the bone cache is considered good for this frame.
  	void							GetCachedBoneMatrix( int boneIndex, matrix3x4_t &out );
  
  	// Wrappers for CBoneAccessor.
  	const matrix3x4_t&				GetBone( int iBone ) const;
  	matrix3x4_t&					GetBoneForWrite( int iBone );
  
  	// Used for debugging. Will produce asserts if someone tries to setup bones or
  	// attachments before it's allowed.
  	// Use the "AutoAllowBoneAccess" class to auto push/pop bone access.
  	// Use a distinct "tag" when pushing/popping - asserts when push/pop tags do not match.
  	struct AutoAllowBoneAccess
  	{
  		AutoAllowBoneAccess( bool bAllowForNormalModels, bool bAllowForViewModels );
  		~AutoAllowBoneAccess( void );
  	};
  	static void						PushAllowBoneAccess( bool bAllowForNormalModels, bool bAllowForViewModels, char const *tagPush );
  	static void						PopBoneAccess( char const *tagPop );
  	static void						ThreadedBoneSetup();
  	static void						InitBoneSetupThreadPool();
  	static void						ShutdownBoneSetupThreadPool();
  
  	// Invalidate bone caches so all SetupBones() calls force bone transforms to be regenerated.
  	static void						InvalidateBoneCaches();
  
  	// Purpose: My physics object has been updated, react or extract data
  	virtual void					VPhysicsUpdate( IPhysicsObject *pPhysics );
  
  	void DisableMuzzleFlash();		// Turn off the muzzle flash (ie: signal that we handled the server's event).
  	virtual void DoMuzzleFlash();	// Force a muzzle flash event. Note: this only QUEUES an event, so
  									// ProcessMuzzleFlashEvent will get called later.
  	bool ShouldMuzzleFlash() const;	// Is the muzzle flash event on?
  
  	// This is called to do the actual muzzle flash effect.
  	virtual void ProcessMuzzleFlashEvent();
  	
  	// Update client side animations
  	static void UpdateClientSideAnimations();
  
  	// Load the model's keyvalues section and create effects listed inside it
  	void InitModelEffects( void );
  
  	// Sometimes the server wants to update the client's cycle to get the two to run in sync (for proper hit detection)
  	virtual void SetServerIntendedCycle( float intended ) { (void)intended; }
  	virtual float GetServerIntendedCycle( void ) { return -1.0f; }
  
  	// For prediction
  	int								SelectWeightedSequence ( int activity );
  	void							ResetSequenceInfo( void );
  	float							SequenceDuration( void );
  	float							SequenceDuration( CStudioHdr *pStudioHdr, int iSequence );
  	inline float					SequenceDuration( int iSequence ) { return SequenceDuration(GetModelPtr(), iSequence); }
  	int								FindTransitionSequence( int iCurrentSequence, int iGoalSequence, int *piDir );
  
  	void							RagdollMoved( void );
  
  	virtual void					GetToolRecordingState( KeyValues *msg );
  	virtual void					CleanupToolRecordingState( KeyValues *msg );
  
  	void							SetReceivedSequence( void );
  	virtual bool					ShouldResetSequenceOnNewModel( void );
  
  	virtual bool					IsViewModel() const;
  
  protected:
  	// View models scale their attachment positions to account for FOV. To get the unmodified
  	// attachment position (like if you're rendering something else during the view model's DrawModel call),
  	// use TransformViewModelAttachmentToWorld.
  	virtual void					FormatViewModelAttachment( int nAttachment, matrix3x4_t &attachmentToWorld ) {}
  
  	// View models say yes to this.
  	bool							IsBoneAccessAllowed() const;
  	CMouthInfo&						MouthInfo();
  
  	// Models used in a ModelPanel say yes to this
  	virtual bool					IsMenuModel() const;
  
  	// Allow studio models to tell C_BaseEntity what their m_nBody value is
  	virtual int						GetStudioBody( void ) { return m_nBody; }
  
  	virtual bool					CalcAttachments();
  
  private:
  	// This method should return true if the bones have changed + SetupBones needs to be called
  	virtual float					LastBoneChangedTime() { return FLT_MAX; }
  
  	CBoneList*						RecordBones( CStudioHdr *hdr, matrix3x4_t *pBoneState );
  
  	bool							PutAttachment( int number, const matrix3x4_t &attachmentToWorld );
  	void							TermRopes();
  
  	void							DelayedInitModelEffects( void );
  
  	void							UpdateRelevantInterpolatedVars();
  	void							AddBaseAnimatingInterpolatedVars();
  	void							RemoveBaseAnimatingInterpolatedVars();
  
  public:
  	CRagdoll						*m_pRagdoll;
  
  	// Texture group to use
  	int								m_nSkin;
  
  	// Object bodygroup
  	int								m_nBody;
  
  	// Hitbox set to use (default 0)
  	int								m_nHitboxSet;
  
  	CSequenceTransitioner			m_SequenceTransitioner;
  
  protected:
  	CIKContext						*m_pIk;
  
  	int								m_iEyeAttachment;
  
  	// Animation playback framerate
  	float							m_flPlaybackRate;
  
  	// Decomposed ragdoll info
  	bool							m_bStoreRagdollInfo;
  	RagdollInfo_t					*m_pRagdollInfo;
  	Vector							m_vecForce;
  	int								m_nForceBone;
  
  	// Is bone cache valid
  	// bone transformation matrix
  	unsigned long					m_iMostRecentModelBoneCounter;
  	unsigned long					m_iMostRecentBoneSetupRequest;
  	int								m_iPrevBoneMask;
  	int								m_iAccumulatedBoneMask;
  
  	CBoneAccessor					m_BoneAccessor;
  	CThreadFastMutex				m_BoneSetupLock;
  
  	ClientSideAnimationListHandle_t	m_ClientSideAnimationListHandle;
  
  	// Client-side animation
  	bool							m_bClientSideFrameReset;
  
  	// Bone attachments. Used for attaching one BaseAnimating to another's bones.
  	// Client side only.
  	CUtlVector<CHandle<C_BaseAnimating> > m_BoneAttachments;
  	int								m_boneIndexAttached;
  	Vector							m_bonePosition;
  	QAngle							m_boneAngles;
  	CHandle<C_BaseAnimating>		m_pAttachedTo;
  
  protected:
  
  	float							m_fadeMinDist;
  	float							m_fadeMaxDist;
  	float							m_flFadeScale;
  
  private:
  
  	float							m_flGroundSpeed;	// computed linear movement rate for current sequence
  	float							m_flLastEventCheck;	// cycle index of when events were last checked
  	bool							m_bSequenceFinished;// flag set when StudioAdvanceFrame moves across a frame boundry
  	bool							m_bSequenceLoops;	// true if the sequence loops
  
  	// Mouth lipsync/envelope following values
  	CMouthInfo						m_mouth;
  
  	CNetworkVar( float, m_flModelScale );
  
  	// Animation blending factors
  	float							m_flPoseParameter[MAXSTUDIOPOSEPARAM];
  	CInterpolatedVarArray< float, MAXSTUDIOPOSEPARAM >		m_iv_flPoseParameter;
  	float							m_flOldPoseParameters[MAXSTUDIOPOSEPARAM];
  
  	int								m_nPrevSequence;
  	int								m_nRestoreSequence;
  
  	// Ropes that got spawned when the model was created.
  	CUtlLinkedList<C_RopeKeyframe*,unsigned short> m_Ropes;
  
  	// event processing info
  	float							m_flPrevEventCycle;
  	int								m_nEventSequence;
  
  	float							m_flEncodedController[MAXSTUDIOBONECTRLS];	
  	CInterpolatedVarArray< float, MAXSTUDIOBONECTRLS >		m_iv_flEncodedController;
  	float							m_flOldEncodedController[MAXSTUDIOBONECTRLS];
  
  	// Clientside animation
  	bool							m_bClientSideAnimation;
  	bool							m_bLastClientSideFrameReset;
  
  	int								m_nNewSequenceParity;
  	int								m_nResetEventsParity;
  
  	int								m_nPrevNewSequenceParity;
  	int								m_nPrevResetEventsParity;
  
  	bool							m_builtRagdoll;
  	Vector							m_vecPreRagdollMins;
  	Vector							m_vecPreRagdollMaxs;
  
  	// Current animation sequence
  	int								m_nSequence;
  	bool							m_bReceivedSequence;
  
  	// Current cycle location from server
  protected:
  	float							m_flCycle;
  	CInterpolatedVar< float >		m_iv_flCycle;
  	float							m_flOldCycle;
  	bool							m_bNoModelParticles;
  
  private:
  	float							m_flOldModelScale;
  	int								m_nOldSequence;
  	CBoneMergeCache					*m_pBoneMergeCache;	// This caches the strcmp lookups that it has to do
  														// when merg
  	
  	CUtlVector< matrix3x4_t >		m_CachedBoneData; // never access this directly. Use m_BoneAccessor.
  	memhandle_t						m_hitboxBoneCacheHandle;
  	float							m_flLastBoneSetupTime;
  	CJiggleBones					*m_pJiggleBones;
  
  	// Calculated attachment points
  	CUtlVector<CAttachmentData>		m_Attachments;
  
  	void							SetupBones_AttachmentHelper( CStudioHdr *pStudioHdr );
  
  	EHANDLE							m_hLightingOrigin;
  	EHANDLE							m_hLightingOriginRelative;
  
  	// These are compared against each other to determine if the entity should muzzle flash.
  	CNetworkVar( unsigned char, m_nMuzzleFlashParity );
  	unsigned char m_nOldMuzzleFlashParity;
  
  	bool							m_bInitModelEffects;
  
  	// Dynamic models
  	bool							m_bDynamicModelAllowed;
  	bool							m_bDynamicModelPending;
  	bool							m_bResetSequenceInfoOnLoad;
  	CRefCountedModelIndex			m_AutoRefModelIndex;
  public:
  	void							EnableDynamicModels() { m_bDynamicModelAllowed = true; }
  	bool							IsDynamicModelLoading() const { return m_bDynamicModelPending; }
  private:
  	virtual void					OnModelLoadComplete( const model_t* pModel );
  
  private:
  	void							LockStudioHdr();
  	void							UnlockStudioHdr();
  	mutable CStudioHdr				*m_pStudioHdr;
  	mutable MDLHandle_t				m_hStudioHdr;
  	CThreadFastMutex				m_StudioHdrInitLock;
  };
  
  enum 
  {
  	RAGDOLL_FRICTION_OFF = -2,
  	RAGDOLL_FRICTION_NONE,
  	RAGDOLL_FRICTION_IN,
  	RAGDOLL_FRICTION_HOLD,
  	RAGDOLL_FRICTION_OUT,
  };
  
  class C_ClientRagdoll : public C_BaseAnimating, public IPVSNotify
  {
  	
  public:
  	C_ClientRagdoll( bool bRestoring = true );
  	DECLARE_CLASS( C_ClientRagdoll, C_BaseAnimating );
  	DECLARE_DATADESC();
  
  	// inherited from IPVSNotify
  	virtual void OnPVSStatusChanged( bool bInPVS );
  
  	virtual void Release( void );
  	virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights );
  	virtual void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName );
  	void ClientThink( void );
  	void ReleaseRagdoll( void ) { m_bReleaseRagdoll = true;	}
  	bool ShouldSavePhysics( void ) { return true; }
  	virtual void	OnSave();
  	virtual void	OnRestore();
  	virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_SAVE_NON_NETWORKABLE; }
  	virtual IPVSNotify*				GetPVSNotifyInterface() { return this; }
  
  	void	HandleAnimatedFriction( void );
  	virtual void SUB_Remove( void );
  
  	void	FadeOut( void );
  	virtual float LastBoneChangedTime();
  
  	bool m_bFadeOut;
  	bool m_bImportant;
  	float m_flEffectTime;
  
  private:
  	int m_iCurrentFriction;
  	int m_iMinFriction;
  	int m_iMaxFriction;
  	float m_flFrictionModTime;
  	float m_flFrictionTime;
  
  	int  m_iFrictionAnimState;
  	bool m_bReleaseRagdoll;
  
  	bool m_bFadingOut;
  
  	float m_flScaleEnd[NUM_HITBOX_FIRES];
  	float m_flScaleTimeStart[NUM_HITBOX_FIRES];
  	float m_flScaleTimeEnd[NUM_HITBOX_FIRES];
  };
  
  //-----------------------------------------------------------------------------
  // Purpose: Serves the 90% case of calling SetSequence / ResetSequenceInfo.
  //-----------------------------------------------------------------------------
  inline void C_BaseAnimating::ResetSequence(int nSequence)
  {
  	SetSequence( nSequence );
  	ResetSequenceInfo();
  }
  
  inline float C_BaseAnimating::GetPlaybackRate()
  {
  	return m_flPlaybackRate;
  }
  
  inline void C_BaseAnimating::SetPlaybackRate( float rate )
  {
  	m_flPlaybackRate = rate;
  }
  
  inline const matrix3x4_t& C_BaseAnimating::GetBone( int iBone ) const
  {
  	return m_BoneAccessor.GetBone( iBone );
  }
  
  inline matrix3x4_t& C_BaseAnimating::GetBoneForWrite( int iBone )
  {
  	return m_BoneAccessor.GetBoneForWrite( iBone );
  }
  
  
  inline bool C_BaseAnimating::ShouldMuzzleFlash() const
  {
  	return m_nOldMuzzleFlashParity != m_nMuzzleFlashParity;
  }
  
  inline float C_BaseAnimating::GetCycle() const
  {
  	return m_flCycle;
  }
  
  //-----------------------------------------------------------------------------
  // Purpose: return a pointer to an updated studiomdl cache cache
  //-----------------------------------------------------------------------------
  
  inline CStudioHdr *C_BaseAnimating::GetModelPtr() const
  { 
  	if ( IsDynamicModelLoading() )
  		return NULL;
  
  #ifdef _DEBUG
  	// GetModelPtr() is often called before OnNewModel() so go ahead and set it up first chance.
  //	static IDataCacheSection *pModelCache = datacache->FindSection( "ModelData" );
  //	AssertOnce( pModelCache->IsFrameLocking() );
  #endif
  	if ( !m_pStudioHdr )
  	{
  		const_cast<C_BaseAnimating *>(this)->LockStudioHdr();
  	}
  	Assert( m_pStudioHdr ? m_pStudioHdr->GetRenderHdr() == mdlcache->GetStudioHdr(m_hStudioHdr) : m_hStudioHdr == MDLHANDLE_INVALID );
  	return m_pStudioHdr;
  }
  
  
  inline void C_BaseAnimating::InvalidateMdlCache()
  {
  	if ( m_pStudioHdr )
  	{
  		UnlockStudioHdr();
  		delete m_pStudioHdr;
  		m_pStudioHdr = NULL;
  	}
  }
  
  
  inline bool C_BaseAnimating::IsModelScaleFractional() const   /// very fast way to ask if the model scale is < 1.0f
  {
  	COMPILE_TIME_ASSERT( sizeof( m_flModelScale ) == sizeof( int ) );
  	return *((const int *) &m_flModelScale) < 0x3f800000;
  }
  
  inline bool C_BaseAnimating::IsModelScaled() const
  {
  	return ( m_flModelScale > 1.0f+FLT_EPSILON || m_flModelScale < 1.0f-FLT_EPSILON );
  }
  
  //-----------------------------------------------------------------------------
  // Sequence access
  //-----------------------------------------------------------------------------
  inline int C_BaseAnimating::GetSequence() 
  { 
  	return m_nSequence; 
  }
  
  inline bool C_BaseAnimating::IsSequenceFinished( void ) 
  { 
  	return m_bSequenceFinished; 
  }
  
  inline float C_BaseAnimating::SequenceDuration( void )
  { 
  	return SequenceDuration( GetSequence() ); 
  }
  
  
  //-----------------------------------------------------------------------------
  // Mouth
  //-----------------------------------------------------------------------------
  inline CMouthInfo& C_BaseAnimating::MouthInfo()			
  { 
  	return m_mouth; 
  }
  
  
  // FIXME: move these to somewhere that makes sense
  void GetColumn( matrix3x4_t& src, int column, Vector &dest );
  void SetColumn( Vector &src, int column, matrix3x4_t& dest );
  
  EXTERN_RECV_TABLE(DT_BaseAnimating);
  
  
  extern void DevMsgRT( PRINTF_FORMAT_STRING char const* pMsg, ... );
  
  #endif // C_BASEANIMATING_H
  