//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #ifndef AI_BEHAVIOR_ACTBUSY_H #define AI_BEHAVIOR_ACTBUSY_H #ifdef _WIN32 #pragma once #endif #include "ai_behavior.h" #include "ai_goalentity.h" //----------------------------------------------------------------------------- enum { ACTBUSY_TYPE_DEFAULT = 0, ACTBUSY_TYPE_COMBAT, }; enum busyinterrupt_t { BA_INT_NONE, // Nothing breaks us out of this BA_INT_DANGER, // Only danger signals interrupts this busy anim. The player will be ignored. BA_INT_PLAYER, // The Player's presence interrupts this busy anim BA_INT_AMBUSH, // We're waiting to ambush enemies. Don't break on danger sounds in front of us. BA_INT_COMBAT, // Only break out if we're shot at. BA_INT_ZOMBIESLUMP, // Zombies who are slumped on the ground. BA_INT_SIEGE_DEFENSE, }; enum busyanimparts_t { BA_BUSY, BA_ENTRY, BA_EXIT, BA_MAX_ANIMS, }; struct busyanim_t { string_t iszName; Activity iActivities[BA_MAX_ANIMS]; string_t iszSequences[BA_MAX_ANIMS]; string_t iszSounds[BA_MAX_ANIMS]; float flMinTime; // Min time spent in this busy animation float flMaxTime; // Max time spent in this busy animation. 0 means continue until interrupted. busyinterrupt_t iBusyInterruptType; bool bUseAutomovement; }; struct busysafezone_t { Vector vecMins; Vector vecMaxs; }; #define NO_MAX_TIME -1 class CAI_ActBusyGoal; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CAI_ActBusyBehavior : public CAI_SimpleBehavior { DECLARE_CLASS( CAI_ActBusyBehavior, CAI_SimpleBehavior ); public: DECLARE_DATADESC(); CAI_ActBusyBehavior(); enum { // Schedules SCHED_ACTBUSY_START_BUSYING = BaseClass::NEXT_SCHEDULE, SCHED_ACTBUSY_BUSY, SCHED_ACTBUSY_STOP_BUSYING, SCHED_ACTBUSY_LEAVE, SCHED_ACTBUSY_TELEPORT_TO_BUSY, NEXT_SCHEDULE, // Tasks TASK_ACTBUSY_PLAY_BUSY_ANIM = BaseClass::NEXT_TASK, TASK_ACTBUSY_PLAY_ENTRY, TASK_ACTBUSY_PLAY_EXIT, TASK_ACTBUSY_TELEPORT_TO_BUSY, TASK_ACTBUSY_WALK_PATH_TO_BUSY, TASK_ACTBUSY_GET_PATH_TO_ACTBUSY, TASK_ACTBUSY_VERIFY_EXIT, NEXT_TASK, // Conditions COND_ACTBUSY_LOST_SEE_ENTITY = BaseClass::NEXT_CONDITION, COND_ACTBUSY_AWARE_OF_ENEMY_IN_SAFE_ZONE, COND_ACTBUSY_ENEMY_TOO_CLOSE, NEXT_CONDITION, }; static const char *GetClassName() { return "behavior_act_busy"; } virtual const char *GetClassNameV() { return GetClassName(); } virtual const char *GetName() { return "ActBusy"; } void Enable( CAI_ActBusyGoal *pGoal, float flRange, bool bVisibleOnly ); void OnRestore(); void SetBusySearchRange( float flRange ); void Disable( void ); void ForceActBusy( CAI_ActBusyGoal *pGoal, CAI_Hint *pHintNode = NULL, float flMaxTime = NO_MAX_TIME, bool bVisibleOnly = false, bool bTeleportToBusy = false, bool bUseNearestBusy = false, CBaseEntity *pSeeEntity = NULL, Activity activity = ACT_INVALID ); void ForceActBusyLeave( bool bVisibleOnly = false ); void StopBusying( void ); bool IsStopBusying(); CAI_Hint *FindActBusyHintNode( void ); CAI_Hint *FindCombatActBusyHintNode( void ); CAI_Hint *FindCombatActBusyTeleportHintNode( void ); bool CanSelectSchedule( void ); bool IsCurScheduleOverridable( void ); bool ShouldIgnoreSound( CSound *pSound ); void OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ); int OnTakeDamage_Alive( const CTakeDamageInfo &info ); void GatherConditions( void ); void BuildScheduleTestBits( void ); void EndScheduleSelection( void ); Activity NPC_TranslateActivity( Activity nActivity ); void HandleAnimEvent( animevent_t *pEvent ); void CheckAndCleanupOnExit( void ); bool FValidateHintType( CAI_Hint *pHint ); bool ActBusyNodeStillActive( void ); bool IsMovingToBusy( void ) { return m_bMovingToBusy; } bool IsEnabled( void ) { return m_bEnabled; } float GetReasonableFacingDist( void ) { return 0; } // Actbusy ignores reasonable facing bool IsInterruptable( void ); bool ShouldPlayerAvoid( void ); void SetUseRenderBounds( bool bUseBounds ) { m_bUseRenderBoundsForCollision = bUseBounds; } void ComputeAndSetRenderBounds(); bool CanFlinch( void ); bool CanRunAScriptedNPCInteraction( bool bForced ); void OnScheduleChange(); bool QueryHearSound( CSound *pSound ); void OnSeeEntity( CBaseEntity *pEntity ); bool NeedsToPlayExitAnim() { return m_bNeedsToPlayExitAnim; } // Returns true if the current NPC is acting busy, or moving to an actbusy bool IsActive( void ); // Returns true if the current NPC is actually acting busy (i.e. inside an act busy anim) bool IsInsideActBusy( void ) { return m_bBusy; } // Combat act busy stuff bool IsCombatActBusy(); void CollectSafeZoneVolumes( CAI_ActBusyGoal *pActBusyGoal ); bool IsInSafeZone( CBaseEntity *pEntity ); int CountEnemiesInSafeZone(); private: virtual int SelectSchedule( void ); int SelectScheduleForLeaving( void ); int SelectScheduleWhileNotBusy( int iBase ); int SelectScheduleWhileBusy( void ); virtual void StartTask( const Task_t *pTask ); virtual void RunTask( const Task_t *pTask ); void NotifyBusyEnding( void ); bool HasAnimForActBusy( int iActBusy, busyanimparts_t AnimPart ); bool PlayAnimForActBusy( busyanimparts_t AnimPart ); void PlaySoundForActBusy( busyanimparts_t AnimPart ); private: bool m_bEnabled; bool m_bForceActBusy; Activity m_ForcedActivity; bool m_bTeleportToBusy; bool m_bUseNearestBusy; bool m_bLeaving; bool m_bVisibleOnly; bool m_bUseRenderBoundsForCollision; float m_flForcedMaxTime; bool m_bBusy; bool m_bMovingToBusy; bool m_bNeedsToPlayExitAnim; float m_flNextBusySearchTime; float m_flEndBusyAt; float m_flBusySearchRange; bool m_bInQueue; int m_iCurrentBusyAnim; CHandle m_hActBusyGoal; bool m_bNeedToSetBounds; EHANDLE m_hSeeEntity; float m_fTimeLastSawSeeEntity; bool m_bExitedBusyToDueLostSeeEntity; bool m_bExitedBusyToDueSeeEnemy; int m_iNumConsecutivePathFailures; // Count how many times we failed to find a path to a node, so we can consider teleporting. bool m_bAutoFireWeapon; float m_flDeferUntil; int m_iNumEnemiesInSafeZone; CUtlVectorm_SafeZones; DEFINE_CUSTOM_SCHEDULE_PROVIDER; }; //----------------------------------------------------------------------------- // Purpose: A level tool to control the actbusy behavior. //----------------------------------------------------------------------------- class CAI_ActBusyGoal : public CAI_GoalEntity { DECLARE_CLASS( CAI_ActBusyGoal, CAI_GoalEntity ); public: CAI_ActBusyGoal() { // Support legacy maps, where this value used to be set from a constant (with a value of 1). // Now designers can specify whatever they want in Hammer. Take care of old maps by setting // this in the constructor. (sjb) m_flSeeEntityTimeout = 1; } virtual void NPCMovingToBusy( CAI_BaseNPC *pNPC ); virtual void NPCAbortedMoveTo( CAI_BaseNPC *pNPC ); virtual void NPCStartedBusy( CAI_BaseNPC *pNPC ); virtual void NPCStartedLeavingBusy( CAI_BaseNPC *pNPC ); virtual void NPCFinishedBusy( CAI_BaseNPC *pNPC ); virtual void NPCLeft( CAI_BaseNPC *pNPC ); virtual void NPCLostSeeEntity( CAI_BaseNPC *pNPC ); virtual void NPCSeeEnemy( CAI_BaseNPC *pNPC ); int GetType() { return m_iType; } bool IsCombatActBusyTeleportAllowed() { return m_bAllowCombatActBusyTeleport; } protected: CAI_ActBusyBehavior *GetBusyBehaviorForNPC( const char *pszActorName, CBaseEntity *pActivator, CBaseEntity *pCaller, const char *sInputName ); CAI_ActBusyBehavior *GetBusyBehaviorForNPC( CBaseEntity *pEntity, const char *sInputName ); void EnableGoal( CAI_BaseNPC *pAI ); // Inputs virtual void InputActivate( inputdata_t &inputdata ); virtual void InputDeactivate( inputdata_t &inputdata ); void InputSetBusySearchRange( inputdata_t &inputdata ); void InputForceNPCToActBusy( inputdata_t &inputdata ); void InputForceThisNPCToActBusy( inputdata_t &inputdata ); void InputForceThisNPCToLeave( inputdata_t &inputdata ); DECLARE_DATADESC(); protected: float m_flBusySearchRange; bool m_bVisibleOnly; int m_iType; bool m_bAllowCombatActBusyTeleport; public: // Let the actbusy behavior query these so we don't have to duplicate the data. string_t m_iszSeeEntityName; float m_flSeeEntityTimeout; string_t m_iszSafeZoneVolume; int m_iSightMethod; protected: COutputEHANDLE m_OnNPCStartedBusy; COutputEHANDLE m_OnNPCFinishedBusy; COutputEHANDLE m_OnNPCLeft; COutputEHANDLE m_OnNPCLostSeeEntity; COutputEHANDLE m_OnNPCSeeEnemy; }; // Maximum number of nodes allowed in an actbusy queue #define MAX_QUEUE_NODES 20 //----------------------------------------------------------------------------- // Purpose: A level tool to control the actbusy behavior to create NPC queues //----------------------------------------------------------------------------- class CAI_ActBusyQueueGoal : public CAI_ActBusyGoal { DECLARE_CLASS( CAI_ActBusyQueueGoal, CAI_ActBusyGoal ); public: virtual void Spawn( void ); virtual void DrawDebugGeometryOverlays( void ); virtual void NPCMovingToBusy( CAI_BaseNPC *pNPC ); virtual void NPCStartedBusy( CAI_BaseNPC *pNPC ); virtual void NPCAbortedMoveTo( CAI_BaseNPC *pNPC ); virtual void NPCFinishedBusy( CAI_BaseNPC *pNPC ); virtual void NPCStartedLeavingBusy( CAI_BaseNPC *pNPC ); virtual void InputActivate( inputdata_t &inputdata ); void InputPlayerStartedBlocking( inputdata_t &inputdata ); void InputPlayerStoppedBlocking( inputdata_t &inputdata ); void InputMoveQueueUp( inputdata_t &inputdata ); void PushNPCBackInQueue( CAI_BaseNPC *pNPC, int iStartingNode ); void RemoveNPCFromQueue( CAI_BaseNPC *pNPC ); void RecalculateQueueCount( void ); void QueueThink( void ); void MoveQueueUp( void ); void MoveQueueUpThink( void ); bool NodeIsOccupied( int i ); CAI_BaseNPC *GetNPCOnNode( int iNode ); CAI_ActBusyBehavior *GetQueueBehaviorForNPC( CAI_BaseNPC *pNPC ); DECLARE_DATADESC(); private: int m_iCurrentQueueCount; CHandle m_hNodes[ MAX_QUEUE_NODES ]; bool m_bPlayerBlockedNodes[ MAX_QUEUE_NODES ]; EHANDLE m_hExitNode; EHANDLE m_hExitingNPC; bool m_bForceReachFront; // Read from mapdata string_t m_iszNodes[ MAX_QUEUE_NODES ]; string_t m_iszExitNode; // Outputs COutputInt m_OnQueueMoved; COutputEHANDLE m_OnNPCLeftQueue; COutputEHANDLE m_OnNPCStartedLeavingQueue; }; #endif // AI_BEHAVIOR_ACTBUSY_H