//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Hooks and classes for the support of humanoid NPCs with // groovy facial animation capabilities, aka, "Actors" // //=============================================================================// #ifndef AI_BASEACTOR_H #define AI_BASEACTOR_H #include "ai_basehumanoid.h" #include "ai_speech.h" #include "AI_Interest_Target.h" #include #if defined( _WIN32 ) #pragma once #endif //----------------------------------------------------------------------------- // CAI_BaseActor // // Purpose: The base class for all head/body/eye expressive NPCS. // //----------------------------------------------------------------------------- enum PoseParameter_t { POSE_END=INT_MAX }; enum FlexWeight_t { FLEX_END=INT_MAX }; class CInfoRemarkable; struct AILookTargetArgs_t { EHANDLE hTarget; Vector vTarget; float flDuration; float flInfluence; float flRamp; bool bExcludePlayers; CAI_InterestTarget *pQueue; }; class CAI_BaseActor : public CAI_ExpresserHost { DECLARE_CLASS( CAI_BaseActor, CAI_ExpresserHost ); //friend CPoseParameter; //friend CFlexWeight; #pragma region PoseParameter and FlexWeight get/set public: void Init( PoseParameter_t &index, const char *szName ) { index = (PoseParameter_t)LookupPoseParameter( szName ); }; void Set( PoseParameter_t index, float flValue ) { SetPoseParameter( (int)index, flValue ); } float Get( PoseParameter_t index ) { return GetPoseParameter( (int)index ); } float ClampWithBias( PoseParameter_t index, float value, float base ); // Note, you must add all names to this static function in order for Init to work static bool IsServerSideFlexController( char const *szName ); void Init( FlexWeight_t &index, const char *szName ) { // Make this fatal!!! if ( !IsServerSideFlexController( szName ) ) { Error( "You forgot to add flex controller %s to list in CAI_BaseActor::IsServerSideFlexController().", szName ); } index = (FlexWeight_t)FindFlexController( szName ); } void Set( FlexWeight_t index, float flValue ) { SetFlexWeight( (LocalFlexController_t)index, flValue ); } float Get( FlexWeight_t index ) { return GetFlexWeight( (LocalFlexController_t)index ); } #pragma endregion public: CAI_BaseActor() : m_fLatchedPositions( 0 ), m_latchedEyeOrigin( vec3_origin ), m_latchedEyeDirection( vec3_origin ), m_latchedHeadDirection( vec3_origin ), m_flBlinktime( 0 ), m_hLookTarget( NULL ), m_iszExpressionScene( NULL_STRING ), m_iszIdleExpression( NULL_STRING ), m_iszAlertExpression( NULL_STRING ), m_iszCombatExpression( NULL_STRING ), m_iszDeathExpression( NULL_STRING ), m_iszExpressionOverride( NULL_STRING ), m_bRemarkablePolling( false ) { memset( m_flextarget, 0, 64 * sizeof( m_flextarget[0] ) ); } ~CAI_BaseActor() { delete m_pExpresser; } virtual void StudioFrameAdvance(); virtual void Precache(); virtual void SetModel( const char *szModelName ); virtual bool StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget ); virtual bool ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ); virtual bool ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled ); virtual bool CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event ); Vector EyePosition( ); virtual Vector HeadDirection2D( void ); virtual Vector HeadDirection3D( void ); virtual Vector EyeDirection2D( void ); virtual Vector EyeDirection3D( void ); CBaseEntity *GetLooktarget() { return m_hLookTarget.Get(); } virtual void OnNewLookTarget() {}; // CBaseFlex virtual void SetViewtarget( const Vector &viewtarget ); // CAI_BaseNPC virtual float PickLookTarget( bool bExcludePlayers = false, float minTime = 1.5, float maxTime = 2.5 ); virtual float PickLookTarget( CAI_InterestTarget &queue, bool bExcludePlayers = false, float minTime = 1.5, float maxTime = 2.5 ); virtual bool PickTacticalLookTarget( AILookTargetArgs_t *pArgs ); virtual bool PickRandomLookTarget( AILookTargetArgs_t *pArgs ); virtual void MakeRandomLookTarget( AILookTargetArgs_t *pArgs, float minTime, float maxTime ); virtual bool HasActiveLookTargets( void ); virtual void OnSelectedLookTarget( AILookTargetArgs_t *pArgs ) { return; } virtual void ClearLookTarget( CBaseEntity *pTarget ); virtual void ExpireCurrentRandomLookTarget() { m_flNextRandomLookTime = gpGlobals->curtime - 0.1f; } virtual void AddLookTarget( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp = 0.0 ); virtual void AddLookTarget( const Vector &vecPosition, float flImportance, float flDuration, float flRamp = 0.0 ); virtual void MaintainLookTargets( float flInterval ); virtual bool ValidEyeTarget(const Vector &lookTargetPos); virtual bool ValidHeadTarget(const Vector &lookTargetPos); virtual float HeadTargetValidity(const Vector &lookTargetPos); virtual void StartTaskRangeAttack1( const Task_t *pTask ); virtual void SetHeadDirection( const Vector &vTargetPos, float flInterval ); void UpdateBodyControl( void ); void UpdateHeadControl( const Vector &vHeadTarget, float flHeadInfluence ); virtual float GetHeadDebounce( void ) { return 0.3; } // how much of previous head turn to use virtual bool ShouldBruteForceFailedNav() { return true; } virtual void GatherConditions( void ); void AccumulateIdealYaw( float flYaw, float flIntensity ); bool SetAccumulatedYawAndUpdate( void ); float m_flAccumYawDelta; float m_flAccumYawScale; //--------------------------------- virtual void OnStateChange( NPC_STATE OldState, NPC_STATE NewState ); //-- Code for responding to INFO_REMARKABLES -- #pragma region Info_Remarkable Code // INFO_REMARKABLEs are objects in the world for which AIs poll and // potentially say context-sensitive things. // The code is here because for the moment only AI_BaseActors do this. // However any other ExpresserHost theoretically could; if you want this // on, say, a player, we'd need to create some new common base class or // some such. // called from GatherConditions() for now because can't think of a better // place to poll it from. Returns true if speaking was tried. bool UpdateRemarkableSpeech(); inline void EnableRemarkables( bool bEnabled ) { m_bRemarkablePolling = bEnabled; } protected: /// true iff the character is allowed to poll for remarkables at all (eg, /// rate limiting). You can make it virtual if you need to. bool CanPollRemarkables(); /// Test to see if a particular remarkable can be commented upon. virtual bool TestRemarkingUpon( CInfoRemarkable * pRemarkable ); public: #pragma endregion //--------------------------------- virtual void PlayExpressionForState( NPC_STATE state ); virtual const char *SelectRandomExpressionForState( NPC_STATE state ); float SetExpression( const char * ); void ClearExpression(); const char * GetExpression(); enum { SCENE_AI_BLINK = 1, SCENE_AI_HOLSTER, SCENE_AI_UNHOLSTER, SCENE_AI_AIM, SCENE_AI_RANDOMLOOK, SCENE_AI_RANDOMFACEFLEX, SCENE_AI_RANDOMHEADFLEX, SCENE_AI_IGNORECOLLISION, SCENE_AI_DISABLEAI }; public: //--------------------------------- virtual void Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity ); void InvalidateBoneCache( void ); DECLARE_DATADESC(); private: enum { HUMANOID_LATCHED_EYE = 0x0001, HUMANOID_LATCHED_HEAD = 0x0002, HUMANOID_LATCHED_ALL = 0x0003, }; //--------------------------------- void UpdateLatchedValues( void ); // Input handlers. void InputSetExpressionOverride( inputdata_t &inputdata ); //--------------------------------- int m_fLatchedPositions; Vector m_latchedEyeOrigin; Vector m_latchedEyeDirection; // direction eyes are looking Vector m_latchedHeadDirection; // direction head is aiming void ClearHeadAdjustment( void ); Vector m_goalHeadDirection; float m_goalHeadInfluence; //--------------------------------- float m_goalSpineYaw; float m_goalBodyYaw; Vector m_goalHeadCorrection; //--------------------------------- float m_flBlinktime; EHANDLE m_hLookTarget; CAI_InterestTarget m_lookQueue; CAI_InterestTarget m_syntheticLookQueue; CAI_InterestTarget m_randomLookQueue; float m_flNextRandomLookTime; // FIXME: move to scene //--------------------------------- string_t m_iszExpressionScene; EHANDLE m_hExpressionSceneEnt; float m_flNextRandomExpressionTime; string_t m_iszExpressionOverride; protected: string_t m_iszIdleExpression; string_t m_iszAlertExpression; string_t m_iszCombatExpression; string_t m_iszDeathExpression; bool m_bRemarkablePolling; float m_fNextIdleVocalizeTime; ///< Not a CoundownTimer because it doesn't need to be networked float m_fNextRemarkPollTime; ///< we only poll for TLK_REMARK once per second or so #pragma region PoseParameters and FlexWeights private: //--------------------------------- //PoseParameter_t m_ParameterBodyTransY; // "body_trans_Y" //PoseParameter_t m_ParameterBodyTransX; // "body_trans_X" //PoseParameter_t m_ParameterBodyLift; // "body_lift" PoseParameter_t m_ParameterBodyYaw; // "body_yaw" //PoseParameter_t m_ParameterBodyPitch; // "body_pitch" //PoseParameter_t m_ParameterBodyRoll; // "body_roll" PoseParameter_t m_ParameterSpineYaw; // "spine_yaw" //PoseParameter_t m_ParameterSpinePitch; // "spine_pitch" //PoseParameter_t m_ParameterSpineRoll; // "spine_roll" PoseParameter_t m_ParameterNeckTrans; // "neck_trans" PoseParameter_t m_ParameterHeadYaw; // "head_yaw" PoseParameter_t m_ParameterHeadPitch; // "head_pitch" PoseParameter_t m_ParameterHeadRoll; // "head_roll" //FlexWeight_t m_FlexweightMoveRightLeft; // "move_rightleft" //FlexWeight_t m_FlexweightMoveForwardBack;// "move_forwardback" //FlexWeight_t m_FlexweightMoveUpDown; // "move_updown" FlexWeight_t m_FlexweightBodyRightLeft; // "body_rightleft" //FlexWeight_t m_FlexweightBodyUpDown; // "body_updown" //FlexWeight_t m_FlexweightBodyTilt; // "body_tilt" FlexWeight_t m_FlexweightChestRightLeft; // "chest_rightleft" //FlexWeight_t m_FlexweightChestUpDown; // "chest_updown" //FlexWeight_t m_FlexweightChestTilt; // "chest_tilt" FlexWeight_t m_FlexweightHeadForwardBack;// "head_forwardback" FlexWeight_t m_FlexweightHeadRightLeft; // "head_rightleft" FlexWeight_t m_FlexweightHeadUpDown; // "head_updown" FlexWeight_t m_FlexweightHeadTilt; // "head_tilt" PoseParameter_t m_ParameterGestureHeight; // "gesture_height" PoseParameter_t m_ParameterGestureWidth; // "gesture_width" FlexWeight_t m_FlexweightGestureUpDown; // "gesture_updown" FlexWeight_t m_FlexweightGestureRightLeft; // "gesture_rightleft" #pragma endregion Cached indices private: //--------------------------------- bool RandomFaceFlex( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ); bool RandomHeadFlex( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event ); float m_flextarget[64]; public: virtual bool UseSemaphore( void ); protected: bool m_bDontUseSemaphore; public: //--------------------------------- // // Speech support // virtual CAI_Expresser *GetExpresser(); protected: bool CreateComponents(); virtual CAI_Expresser *CreateExpresser(); private: //--------------------------------- CAI_Expresser *m_pExpresser; }; //----------------------------------------------------------------------------- #endif // AI_BASEACTOR_H