//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Base NPC character with AI // //=============================================================================// #ifndef AI_BASENPC_H #define AI_BASENPC_H #ifdef _WIN32 #pragma once #endif #include "activitylist.h" #include "ai_basenpc.h" #include "ai_component.h" #include "ai_condition.h" #include "ai_debug.h" #include "ai_default.h" #include "ai_hull.h" #include "ai_moveshoot.h" #include "ai_movetypes.h" #include "ai_namespaces.h" #include "ai_navgoaltype.h" //GoalType_t enum #include "ai_navigator.h" #include "ai_navtype.h" #include "ai_npcstate.h" #include "ai_schedule.h" #include "ai_task.h" #include "ai_utils.h" #include "basecombatcharacter.h" #include "bitstring.h" #include "entityoutput.h" #include "eventlist.h" #include "simtimer.h" #include "soundent.h" #include "tier1/functors.h" #include "utlvector.h" #define PLAYER_SQUADNAME "player_squad" class CAI_Schedule; class CAI_Network; class CAI_Route; class CAI_Hint; class CAI_Node; class CAI_Navigator; class CAI_Pathfinder; class CAI_Senses; class CAI_Enemies; class CAI_Squad; class CAI_Expresser; class CAI_BehaviorBase; class CAI_GoalEntity; class CAI_Motor; class CAI_MoveProbe; class CAI_LocalNavigator; class CAI_TacticalServices; class CVarBitVec; class CAI_ScriptedSequence; class CSceneEntity; class CBaseGrenade; class CBaseDoor; class CBasePropDoor; struct AI_Waypoint_t; class AI_Response; class CBaseFilter; typedef CBitVec CAI_ScheduleBits; // Used to control optimizations mostly dealing with pathfinding for NPCs extern ConVar ai_strong_optimizations; extern bool AIStrongOpt(void); // AI_MONITOR_FOR_OSCILLATION defaults to OFF. If you build with this ON, you // can flag NPC's and monitor them to detect oscillations in their schedule // (circular logic and conditions bugs) DO NOT SHIP WITH THIS ON! #undef AI_MONITOR_FOR_OSCILLATION //============================================================================= // // Constants & enumerations // //============================================================================= #define TURRET_CLOSE_RANGE 200 #define TURRET_MEDIUM_RANGE 500 #define COMMAND_GOAL_TOLERANCE 48 // 48 inches. #define TIME_CARE_ABOUT_DAMAGE 3.0 #define ITEM_PICKUP_TOLERANCE 48.0f // Max's of the box used to search for a weapon to pick up. 45x45x~8 ft. #define WEAPON_SEARCH_DELTA Vector(540, 540, 100) enum Interruptability_t { GENERAL_INTERRUPTABILITY, DAMAGEORDEATH_INTERRUPTABILITY, DEATH_INTERRUPTABILITY }; //------------------------------------- // Memory //------------------------------------- #define MEMORY_CLEAR 0 #define bits_MEMORY_PROVOKED (1 << 0) // right now only used for houndeyes. #define bits_MEMORY_INCOVER (1 << 1) // npc knows it is in a covered position. #define bits_MEMORY_SUSPICIOUS \ (1 << 2) // Ally is suspicious of the player, and will move to provoked // more easily #define bits_MEMORY_TASK_EXPENSIVE \ (1 << 3) // NPC has completed a task which is considered costly, so don't // do another task this frame //#define bits_MEMORY_ ( 1 << 4 ) #define bits_MEMORY_PATH_FAILED (1 << 5) // Failed to find a path #define bits_MEMORY_FLINCHED (1 << 6) // Has already flinched //#define bits_MEMORY_ ( 1 << 7 ) #define bits_MEMORY_TOURGUIDE (1 << 8) // I have been acting as a tourguide. //#define bits_MEMORY_ ( 1 << 9 )// #define bits_MEMORY_LOCKED_HINT (1 << 10) // //#define bits_MEMORY_ ( 1 << 12 ) #define bits_MEMORY_TURNING (1 << 13) // Turning, don't interrupt me. #define bits_MEMORY_TURNHACK (1 << 14) #define bits_MEMORY_HAD_ENEMY (1 << 15) // Had an enemy #define bits_MEMORY_HAD_PLAYER (1 << 16) // Had player #define bits_MEMORY_HAD_LOS (1 << 17) // Had LOS to enemy #define bits_MEMORY_MOVED_FROM_SPAWN (1 << 18) // Has moved since spawning. #define bits_MEMORY_CUSTOM4 (1 << 28) // NPC-specific memory #define bits_MEMORY_CUSTOM3 (1 << 29) // NPC-specific memory #define bits_MEMORY_CUSTOM2 (1 << 30) // NPC-specific memory #define bits_MEMORY_CUSTOM1 (1 << 31) // NPC-specific memory //------------------------------------- // Spawn flags //------------------------------------- #define SF_NPC_WAIT_TILL_SEEN \ (1 << 0) // spawnflag that makes npcs wait until player can see them before // attacking. #define SF_NPC_GAG (1 << 1) // no idle noises from this npc #define SF_NPC_FALL_TO_GROUND (1 << 2) // used my NPC_Maker #define SF_NPC_DROP_HEALTHKIT (1 << 3) // Drop a healthkit upon death #define SF_NPC_START_EFFICIENT (1 << 4) // Set into efficiency mode from spawn // ( 1 << 5 //) ( 1 << 6 ) #define SF_NPC_WAIT_FOR_SCRIPT \ (1 << 7) // spawnflag that makes npcs wait to check for attacking until the // script is done or they've been attacked #define SF_NPC_LONG_RANGE \ (1 << 8) // makes npcs look far and relaxes weapon range limit #define SF_NPC_FADE_CORPSE (1 << 9) // Fade out corpse after death #define SF_NPC_ALWAYSTHINK (1 << 10) // Simulate even when player isn't in PVS. #define SF_NPC_TEMPLATE \ (1 << 11) // This NPC will be used as a template by an npc_maker -- do not // spawn. #define SF_NPC_ALTCOLLISION (1 << 12) #define SF_NPC_NO_WEAPON_DROP \ (1 << 13) // This NPC will not actually drop a weapon that can be picked up #define SF_NPC_NO_PLAYER_PUSHAWAY (1 << 14) // ( 1 << 15 //) // !! Flags above ( 1 << 15 ) are reserved for NPC sub-classes //------------------------------------- // // Return codes from CanPlaySequence. // //------------------------------------- enum CanPlaySequence_t { CANNOT_PLAY = 0, // Can't play for any number of reasons. CAN_PLAY_NOW, // Can play the script immediately. CAN_PLAY_ENQUEUED, // Can play the script after I finish playing my current // script. }; //------------------------------------- // Weapon holstering //------------------------------------- enum DesiredWeaponState_t { DESIREDWEAPONSTATE_IGNORE = 0, DESIREDWEAPONSTATE_HOLSTERED, DESIREDWEAPONSTATE_HOLSTERED_DESTROYED, // Put the weapon away, then // destroy it. DESIREDWEAPONSTATE_UNHOLSTERED, DESIREDWEAPONSTATE_CHANGING, DESIREDWEAPONSTATE_CHANGING_DESTROY, // Destroy the weapon when this change // is complete. }; //------------------------------------- // // Efficiency modes // //------------------------------------- enum AI_Efficiency_t { // Run at full tilt AIE_NORMAL, // Run decision process less often AIE_EFFICIENT, // Run decision process even less often, ignore other NPCs AIE_VERY_EFFICIENT, // Run decision process even less often, ignore other NPCs AIE_SUPER_EFFICIENT, // Don't run at all AIE_DORMANT, }; enum AI_MoveEfficiency_t { AIME_NORMAL, AIME_EFFICIENT, }; //------------------------------------- // // Sleep state // //------------------------------------- enum AI_SleepState_t { AISS_AWAKE, AISS_WAITING_FOR_THREAT, AISS_WAITING_FOR_PVS, AISS_WAITING_FOR_INPUT, AISS_AUTO_PVS, AISS_AUTO_PVS_AFTER_PVS, // Same as AUTO_PVS, except doesn't activate // until/unless the NPC is IN the player's PVS. }; #define AI_SLEEP_FLAGS_NONE 0x00000000 #define AI_SLEEP_FLAG_AUTO_PVS 0x00000001 #define AI_SLEEP_FLAG_AUTO_PVS_AFTER_PVS 0x00000002 //------------------------------------- // // Debug bits // //------------------------------------- enum DebugBaseNPCBits_e { bits_debugDisableAI = 0x00000001, // disable AI bits_debugStepAI = 0x00000002, // step AI }; //------------------------------------- // // Base Sentence index for behaviors // //------------------------------------- enum SentenceIndex_t { SENTENCE_BASE_BEHAVIOR_INDEX = 1000, }; #ifdef AI_MONITOR_FOR_OSCILLATION struct AIScheduleChoice_t { float m_flTimeSelected; CAI_Schedule *m_pScheduleSelected; }; #endif // AI_MONITOR_FOR_OSCILLATION #define MARK_TASK_EXPENSIVE() \ if (GetOuter()) { \ GetOuter()->Remember(bits_MEMORY_TASK_EXPENSIVE); \ } //============================================================================= // // Types used by CAI_BaseNPC // //============================================================================= struct AIScheduleState_t { int iCurTask; TaskStatus_e fTaskStatus; float timeStarted; float timeCurTaskStarted; AI_TaskFailureCode_t taskFailureCode; int iTaskInterrupt; bool bTaskRanAutomovement; bool bTaskUpdatedYaw; bool bScheduleWasInterrupted; DECLARE_SIMPLE_DATADESC(); }; // ----------------------------------------- // An entity that this NPC can't reach // ----------------------------------------- struct UnreachableEnt_t { EHANDLE hUnreachableEnt; // Entity that's unreachable float fExpireTime; // Time to forget this information Vector vLocationWhenUnreachable; DECLARE_SIMPLE_DATADESC(); }; //============================================================================= // SCRIPTED NPC INTERACTIONS //============================================================================= // ----------------------------------------- // Scripted NPC interaction flags // ----------------------------------------- #define SCNPC_FLAG_TEST_OTHER_ANGLES (1 << 1) #define SCNPC_FLAG_TEST_OTHER_VELOCITY (1 << 2) #define SCNPC_FLAG_LOOP_IN_ACTION (1 << 3) #define SCNPC_FLAG_NEEDS_WEAPON_ME (1 << 4) #define SCNPC_FLAG_NEEDS_WEAPON_THEM (1 << 5) #define SCNPC_FLAG_DONT_TELEPORT_AT_END_ME (1 << 6) #define SCNPC_FLAG_DONT_TELEPORT_AT_END_THEM (1 << 7) // ----------------------------------------- // Scripted NPC interaction trigger methods // ----------------------------------------- enum { SNPCINT_CODE = 0, SNPCINT_AUTOMATIC_IN_COMBAT = 1, }; // ----------------------------------------- // Scripted NPC interaction loop breaking trigger methods // ----------------------------------------- #define SNPCINT_LOOPBREAK_ON_DAMAGE (1 << 1) #define SNPCINT_LOOPBREAK_ON_FLASHLIGHT_ILLUM (1 << 2) // ----------------------------------------- // Scripted NPC interaction anim phases // ----------------------------------------- enum { SNPCINT_ENTRY = 0, SNPCINT_SEQUENCE, SNPCINT_EXIT, SNPCINT_NUM_PHASES }; struct ScriptedNPCInteraction_Phases_t { string_t iszSequence; int iActivity; DECLARE_SIMPLE_DATADESC(); }; // Allowable delta from the desired dynamic scripted sequence point #define DSS_MAX_DIST 6 #define DSS_MAX_ANGLE_DIFF 4 // Interaction Logic States enum { NPCINT_NOT_RUNNING = 0, NPCINT_RUNNING_ACTIVE, // I'm in an interaction that I initiated NPCINT_RUNNING_PARTNER, // I'm in an interaction that was initiated by the // other NPC NPCINT_MOVING_TO_MARK, // I'm moving to a position to do an interaction }; #define NPCINT_NONE -1 #define MAXTACLAT_IGNORE -1 // ----------------------------------------- // A scripted interaction between NPCs // ----------------------------------------- struct ScriptedNPCInteraction_t { ScriptedNPCInteraction_t() { iszInteractionName = NULL_STRING; iFlags = 0; iTriggerMethod = SNPCINT_CODE; iLoopBreakTriggerMethod = 0; vecRelativeOrigin = vec3_origin; bValidOnCurrentEnemy = false; flDelay = 5.0; flDistSqr = (DSS_MAX_DIST * DSS_MAX_DIST); flNextAttemptTime = 0; iszMyWeapon = NULL_STRING; iszTheirWeapon = NULL_STRING; for (int i = 0; i < SNPCINT_NUM_PHASES; i++) { sPhases[i].iszSequence = NULL_STRING; sPhases[i].iActivity = ACT_INVALID; } } // Fill out these when passing to AddScriptedNPCInteraction string_t iszInteractionName; int iFlags; int iTriggerMethod; int iLoopBreakTriggerMethod; Vector vecRelativeOrigin; // (forward, right, up) QAngle angRelativeAngles; Vector vecRelativeVelocity; // Desired relative velocity of the other NPC float flDelay; // Delay before interaction can be used again float flDistSqr; // Max distance sqr from the relative origin the NPC is // allowed to be to trigger string_t iszMyWeapon; // Classname of the weapon I'm holding, if any string_t iszTheirWeapon; // Classname of the weapon my interaction partner // is holding, if any ScriptedNPCInteraction_Phases_t sPhases[SNPCINT_NUM_PHASES]; // These will be filled out for you in AddScriptedNPCInteraction VMatrix matDesiredLocalToWorld; // Desired relative position / angles of // the other NPC bool bValidOnCurrentEnemy; float flNextAttemptTime; DECLARE_SIMPLE_DATADESC(); }; //============================================================================= // // Utility functions // //============================================================================= Vector VecCheckToss(CBaseEntity *pEdict, Vector vecSpot1, Vector vecSpot2, float flHeightMaxRatio, float flGravityAdj, bool bRandomize, Vector *vecMins = NULL, Vector *vecMaxs = NULL); Vector VecCheckToss(CBaseEntity *pEntity, ITraceFilter *pFilter, Vector vecSpot1, Vector vecSpot2, float flHeightMaxRatio, float flGravityAdj, bool bRandomize, Vector *vecMins = NULL, Vector *vecMaxs = NULL); Vector VecCheckThrow(CBaseEntity *pEdict, const Vector &vecSpot1, Vector vecSpot2, float flSpeed, float flGravityAdj = 1.0f, Vector *vecMins = NULL, Vector *vecMaxs = NULL); extern Vector g_vecAttackDir; bool FBoxVisible(CBaseEntity *pLooker, CBaseEntity *pTarget); bool FBoxVisible(CBaseEntity *pLooker, CBaseEntity *pTarget, Vector &vecTargetOrigin, float flSize = 0.0); // FIXME: move to utils? float DeltaV(float v0, float v1, float d); float ChangeDistance(float flInterval, float flGoalDistance, float flGoalVelocity, float flCurVelocity, float flIdealVelocity, float flAccelRate, float &flNewDistance, float &flNewVelocity); //============================================================================= // // class CAI_Manager // // Central location for components of the AI to operate across all AIs without // iterating over the global list of entities. // //============================================================================= class CAI_Manager { public: CAI_Manager(); CAI_BaseNPC **AccessAIs(); int NumAIs(); void AddAI(CAI_BaseNPC *pAI); void RemoveAI(CAI_BaseNPC *pAI); bool FindAI(CAI_BaseNPC *pAI) { return (m_AIs.Find(pAI) != m_AIs.InvalidIndex()); } private: enum { MAX_AIS = 256 }; typedef CUtlVector CAIArray; CAIArray m_AIs; }; //------------------------------------- extern CAI_Manager g_AI_Manager; //============================================================================= // // class CAI_BaseNPC // //============================================================================= class CAI_BaseNPC : public CBaseCombatCharacter, public CAI_DefMovementSink { DECLARE_CLASS(CAI_BaseNPC, CBaseCombatCharacter); public: //----------------------------------------------------- // // Initialization, cleanup, serialization, identity // CAI_BaseNPC(); ~CAI_BaseNPC(); //--------------------------------- DECLARE_DATADESC(); DECLARE_SERVERCLASS(); virtual int Save(ISave &save); virtual int Restore(IRestore &restore); virtual void OnRestore(); void SaveConditions(ISave &save, const CAI_ScheduleBits &conditions); void RestoreConditions(IRestore &restore, CAI_ScheduleBits *pConditions); bool ShouldSavePhysics() { return false; } virtual unsigned int PhysicsSolidMaskForEntity(void) const; virtual bool KeyValue(const char *szKeyName, const char *szValue); //--------------------------------- virtual void PostConstructor(const char *szClassname); virtual void Activate(void); virtual void Precache(void); // derived calls at start of Spawn() virtual bool CreateVPhysics(); virtual void NPCInit(void); // derived calls after Spawn() void NPCInitThink(void); virtual void PostNPCInit(){}; // called after NPC_InitThink virtual void StartNPC(void); virtual bool IsTemplate(void); virtual void CleanupOnDeath(CBaseEntity *pCulprit = NULL, bool bFireDeathOutput = true); virtual void UpdateOnRemove(void); virtual int UpdateTransmitState(); //--------------------------------- // Component creation factories // // The master call, override if you introduce new component types. Call base // first virtual bool CreateComponents(); // Components defined by the base AI class virtual CAI_Senses *CreateSenses(); virtual CAI_MoveProbe *CreateMoveProbe(); virtual CAI_Motor *CreateMotor(); virtual CAI_LocalNavigator *CreateLocalNavigator(); virtual CAI_Navigator *CreateNavigator(); virtual CAI_Pathfinder *CreatePathfinder(); virtual CAI_TacticalServices *CreateTacticalServices(); //--------------------------------- virtual bool IsNPC(void) const { return true; } //--------------------------------- void TestPlayerPushing(CBaseEntity *pPlayer); void CascadePlayerPush(const Vector &push, const Vector &pushOrigin); void NotifyPushMove(); public: //----------------------------------------------------- // // AI processing - thinking, schedule selection and task running // //----------------------------------------------------- void CallNPCThink(void); // Thinking, including core thinking, movement, animation virtual void NPCThink(void); // Core thinking (schedules & tasks) virtual void RunAI(void); // core ai function! // Called to gather up all relevant conditons virtual void GatherConditions(void); // Called immediately prior to schedule processing virtual void PrescheduleThink(void); // Called immediately after schedule processing virtual void PostscheduleThink(void) { return; }; // Notification that the current schedule, if any, is ending and a new one // is being selected virtual void OnScheduleChange(void); // Notification that a new schedule is about to run its first task virtual void OnStartSchedule(int scheduleType){}; // This function implements a decision tree for the NPC. It is responsible // for choosing the next behavior (schedule) based on the current conditions // and state. virtual int SelectSchedule(void); virtual int SelectFailSchedule(int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode); // After the schedule has been selected, it will be processed by this // function so child NPC classes can remap base schedules into // child-specific behaviors virtual int TranslateSchedule(int scheduleType); virtual void StartTask(const Task_t *pTask); virtual void RunTask(const Task_t *pTask); void ClearTransientConditions(); virtual void HandleAnimEvent(animevent_t *pEvent); virtual bool IsInterruptable(); virtual void OnStartScene(void) { } // Called when an NPC begins a cine scene (useful for clean-up) virtual bool ShouldPlayerAvoid(void); virtual void SetPlayerAvoidState(void); virtual void PlayerPenetratingVPhysics(void); virtual bool ShouldAlwaysThink(); void ForceGatherConditions() { m_bForceConditionsGather = true; SetEfficiency(AIE_NORMAL); } // Force an NPC out of PVS to call GatherConditions on next think virtual float LineOfSightDist(const Vector &vecDir = vec3_invalid, float zEye = FLT_MAX); virtual void MakeTracer(const Vector &vecTracerSrc, const trace_t &tr, int iTracerType); virtual const char *GetTracerType(void); virtual void DoImpactEffect(trace_t &tr, int nDamageType); enum { NEXT_SCHEDULE = LAST_SHARED_SCHEDULE, NEXT_TASK = LAST_SHARED_TASK, NEXT_CONDITION = LAST_SHARED_CONDITION, }; protected: // Used by derived classes to chain a task to a task that might not be the // one they are currently handling: void ChainStartTask(int task, float taskData = 0) { Task_t tempTask = {task, taskData}; StartTask((const Task_t *)&tempTask); } void ChainRunTask(int task, float taskData = 0) { Task_t tempTask = {task, taskData}; RunTask((const Task_t *)&tempTask); } void StartTaskOverlay(); void RunTaskOverlay(); void EndTaskOverlay(); virtual void PostRunStopMoving(); bool CheckPVSCondition(); private: bool CanThinkRebalance(); void RebalanceThinks(); bool PreNPCThink(); void PostNPCThink(); bool PreThink(void); void PerformSensing(); void CheckOnGround(void); void MaintainSchedule(void); void RunAnimation(void); void PostRun(void); void PerformMovement(); void PostMovement(); virtual int StartTask(Task_t *pTask) { DevMsg("Called wrong StartTask()\n"); StartTask((const Task_t *)pTask); return 0; } // to ensure correct signature in derived classes virtual int RunTask(Task_t *pTask) { DevMsg("Called wrong RunTask()\n"); RunTask((const Task_t *)pTask); return 0; } // to ensure correct signature in derived classes public: //----------------------------------------------------- // // Schedules & tasks // //----------------------------------------------------- void SetSchedule(CAI_Schedule *pNewSchedule); bool SetSchedule(int localScheduleID); void SetDefaultFailSchedule(int failSchedule) { m_failSchedule = failSchedule; } void ClearSchedule(const char *szReason); CAI_Schedule *GetCurSchedule() { return m_pSchedule; } bool IsCurSchedule(int schedId, bool fIdeal = true); virtual CAI_Schedule *GetSchedule(int localScheduleID); virtual int GetLocalScheduleId(int globalScheduleID) { return AI_IdIsLocal(globalScheduleID) ? globalScheduleID : GetClassScheduleIdSpace()->ScheduleGlobalToLocal( globalScheduleID); } virtual int GetGlobalScheduleId(int localScheduleID) { return AI_IdIsGlobal(localScheduleID) ? localScheduleID : GetClassScheduleIdSpace()->ScheduleLocalToGlobal( localScheduleID); } float GetTimeScheduleStarted() const { return m_ScheduleState.timeStarted; } //--------------------------------- const Task_t *GetTask(void); int TaskIsRunning(void); virtual void TaskFail(AI_TaskFailureCode_t); void TaskFail(const char *pszGeneralFailText) { TaskFail(MakeFailCode(pszGeneralFailText)); } void TaskComplete(bool fIgnoreSetFailedCondition = false); void TaskInterrupt() { m_ScheduleState.iTaskInterrupt++; } void ClearTaskInterrupt() { m_ScheduleState.iTaskInterrupt = 0; } int GetTaskInterrupt() const { return m_ScheduleState.iTaskInterrupt; } void TaskMovementComplete(void); inline int TaskIsComplete(void) { return (GetTaskStatus() == TASKSTATUS_COMPLETE); } virtual const char *TaskName(int taskID); float GetTimeTaskStarted() const { return m_ScheduleState.timeCurTaskStarted; } virtual int GetLocalTaskId(int globalTaskId) { return GetClassScheduleIdSpace()->TaskGlobalToLocal(globalTaskId); } virtual const char *GetSchedulingErrorName() { return "CAI_BaseNPC"; } protected: static bool LoadSchedules(void); virtual bool LoadedSchedules(void); virtual void BuildScheduleTestBits(void); //--------------------------------- // This is the main call to select/translate a schedule virtual CAI_Schedule *GetNewSchedule(void); virtual CAI_Schedule *GetFailSchedule(void); //--------------------------------- virtual bool CanFlinch(void); virtual void CheckFlinches(void); virtual void PlayFlinchGesture(void); int SelectFlinchSchedule(void); virtual bool IsAllowedToDodge(void); bool IsInChoreo() const; private: // This function maps the type through TranslateSchedule() and then // retrieves the pointer to the actual CAI_Schedule from the database of // schedules available to this class. CAI_Schedule *GetScheduleOfType(int scheduleType); bool FHaveSchedule(void); bool FScheduleDone(void); CAI_Schedule *ScheduleInList(const char *pName, CAI_Schedule **pList, int listCount); int GetScheduleCurTaskIndex() const { return m_ScheduleState.iCurTask; } inline int IncScheduleCurTaskIndex(); inline void ResetScheduleCurTaskIndex(); void NextScheduledTask(void); bool IsScheduleValid(void); bool ShouldSelectIdealState(void); // Selecting the ideal state NPC_STATE SelectIdleIdealState(); NPC_STATE SelectAlertIdealState(); NPC_STATE SelectScriptIdealState(); // Various schedule selections based on NPC_STATE int SelectIdleSchedule(); int SelectAlertSchedule(); int SelectCombatSchedule(); virtual int SelectDeadSchedule(); int SelectScriptSchedule(); int SelectInteractionSchedule(); void OnStartTask(void) { SetTaskStatus(TASKSTATUS_RUN_MOVE_AND_TASK); } void SetTaskStatus(TaskStatus_e status) { m_ScheduleState.fTaskStatus = status; } TaskStatus_e GetTaskStatus() const { return m_ScheduleState.fTaskStatus; } void DiscardScheduleState(); //--------------------------------- CAI_Schedule *m_pSchedule; int m_IdealSchedule; AIScheduleState_t m_ScheduleState; int m_failSchedule; // Schedule type to choose if current schedule fails bool m_bDoPostRestoreRefindPath; bool m_bUsingStandardThinkTime; float m_flLastRealThinkTime; int m_iFrameBlocked; bool m_bInChoreo; static int gm_iNextThinkRebalanceTick; static float gm_flTimeLastSpawn; static int gm_nSpawnedThisFrame; protected: // pose parameters int m_poseAim_Pitch; int m_poseAim_Yaw; int m_poseMove_Yaw; virtual void PopulatePoseParameters(void); public: inline bool HasPoseMoveYaw() { return (m_poseMove_Yaw >= 0); } // Return the stored pose parameter for "move_yaw" inline int LookupPoseMoveYaw() { return m_poseMove_Yaw; } //----------------------------------------------------- // // Hooks for CAI_Behaviors, *if* derived class supports them // //----------------------------------------------------- template bool GetBehavior(BEHAVIOR_TYPE **ppBehavior) { CAI_BehaviorBase **ppBehaviors = AccessBehaviors(); *ppBehavior = NULL; for (int i = 0; i < NumBehaviors(); i++) { *ppBehavior = dynamic_cast(ppBehaviors[i]); if (*ppBehavior) return true; } return false; } virtual CAI_BehaviorBase *GetRunningBehavior() { return NULL; } virtual bool ShouldAcceptGoal(CAI_BehaviorBase *pBehavior, CAI_GoalEntity *pGoal) { return true; } virtual void OnClearGoal(CAI_BehaviorBase *pBehavior, CAI_GoalEntity *pGoal) {} // Notification that the status behavior ability to select schedules has // changed. Return "true" to signal a schedule interrupt is desired virtual bool OnBehaviorChangeStatus(CAI_BehaviorBase *pBehavior, bool fCanFinishSchedule) { return false; } private: virtual CAI_BehaviorBase **AccessBehaviors() { return NULL; } virtual int NumBehaviors() { return 0; } public: //----------------------------------------------------- // // Conditions // //----------------------------------------------------- virtual const char *ConditionName(int conditionID); virtual void RemoveIgnoredConditions(void); void SetCondition(int iCondition /*, bool state = true*/); bool HasCondition(int iCondition); bool HasCondition(int iCondition, bool bUseIgnoreConditions); bool HasInterruptCondition(int iCondition); bool HasConditionsToInterruptSchedule(int nLocalScheduleID); void ClearCondition(int iCondition); void ClearConditions(int *pConditions, int nConditions); void SetIgnoreConditions(int *pConditions, int nConditions); void ClearIgnoreConditions(int *pConditions, int nConditions); bool ConditionInterruptsCurSchedule(int iCondition); bool ConditionInterruptsSchedule(int schedule, int iCondition); void SetCustomInterruptCondition(int nCondition); bool IsCustomInterruptConditionSet(int nCondition); void ClearCustomInterruptCondition(int nCondition); void ClearCustomInterruptConditions(void); bool ConditionsGathered() const { return m_bConditionsGathered; } const CAI_ScheduleBits &AccessConditionBits() const { return m_Conditions; } CAI_ScheduleBits &AccessConditionBits() { return m_Conditions; } bool DidChooseEnemy() const { return !m_bSkippedChooseEnemy; } private: CAI_ScheduleBits m_Conditions; CAI_ScheduleBits m_CustomInterruptConditions; // Bit string assembled by the schedule // running, then modified by leaf classes // to suit their needs CAI_ScheduleBits m_ConditionsPreIgnore; CAI_ScheduleBits m_InverseIgnoreConditions; bool m_bForceConditionsGather; bool m_bConditionsGathered; bool m_bSkippedChooseEnemy; public: //----------------------------------------------------- // // NPC State // //----------------------------------------------------- inline void SetIdealState(NPC_STATE eIdealState); inline NPC_STATE GetIdealState(); virtual NPC_STATE SelectIdealState(void); void SetState(NPC_STATE State); virtual bool ShouldGoToIdleState(void) { return (false); } virtual void OnStateChange(NPC_STATE OldState, NPC_STATE NewState){/*Base class doesn't care*/}; NPC_STATE GetState(void) { return m_NPCState; } AI_Efficiency_t GetEfficiency() const { return m_Efficiency; } void SetEfficiency(AI_Efficiency_t efficiency) { m_Efficiency = efficiency; } AI_MoveEfficiency_t GetMoveEfficiency() const { return m_MoveEfficiency; } void SetMoveEfficiency(AI_MoveEfficiency_t efficiency) { m_MoveEfficiency = efficiency; } virtual void UpdateEfficiency(bool bInPVS); void ForceDecisionThink() { m_flNextDecisionTime = 0; SetEfficiency(AIE_NORMAL); } bool IsFlaggedEfficient() const { return HasSpawnFlags(SF_NPC_START_EFFICIENT); } AI_SleepState_t GetSleepState() const { return m_SleepState; } void SetSleepState(AI_SleepState_t sleepState) { m_SleepState = sleepState; } void AddSleepFlags(int flags) { m_SleepFlags |= flags; } void RemoveSleepFlags(int flags) { m_SleepFlags &= ~flags; } bool HasSleepFlags(int flags) { return (m_SleepFlags & flags) == flags; } void UpdateSleepState(bool bInPVS); virtual void Wake(bool bFireOutput = true); void Sleep(); bool WokeThisTick() const; //--------------------------------- NPC_STATE m_NPCState; // npc's current state float m_flLastStateChangeTime; private: NPC_STATE m_IdealNPCState; // npc should change to this state AI_Efficiency_t m_Efficiency; AI_MoveEfficiency_t m_MoveEfficiency; float m_flNextDecisionTime; AI_SleepState_t m_SleepState; int m_SleepFlags; float m_flWakeRadius; bool m_bWakeSquad; int m_nWakeTick; public: //----------------------------------------------------- // // Activities // //----------------------------------------------------- Activity TranslateActivity(Activity idealActivity, Activity *pIdealWeaponActivity = NULL); Activity NPC_TranslateActivity(Activity eNewActivity); Activity GetActivity(void) { return m_Activity; } virtual void SetActivity(Activity NewActivity); Activity GetIdealActivity(void) { return m_IdealActivity; } void SetIdealActivity(Activity NewActivity); void ResetIdealActivity(Activity newIdealActivity); void SetSequenceByName(const char *szSequence); void SetSequenceById(int iSequence); Activity GetScriptCustomMoveActivity(void); int GetScriptCustomMoveSequence(void); Activity GetStoppedActivity(void); inline bool HaveSequenceForActivity(Activity activity); inline bool IsActivityStarted(void); virtual bool IsActivityFinished(void); virtual bool IsActivityMovementPhased(Activity activity); virtual void OnChangeActivity(Activity eNewActivity); void MaintainActivity(void); void ResetActivity(void) { m_Activity = ACT_RESET; } void SetActivityAndSequence(Activity NewActivity, int iSequence, Activity translatedActivity, Activity weaponActivity); private: void AdvanceToIdealActivity(void); void ResolveActivityToSequence(Activity NewActivity, int &iSequence, Activity &translatedActivity, Activity &weaponActivity); Activity m_Activity; // Current animation state Activity m_translatedActivity; // Current actual translated animation Activity m_IdealActivity; // Desired animation state int m_nIdealSequence; // Desired animation sequence Activity m_IdealTranslatedActivity; // Desired actual translated animation state Activity m_IdealWeaponActivity; // Desired weapon animation state CNetworkVar(int, m_iDeathPose); CNetworkVar(int, m_iDeathFrame); public: //----------------------------------------------------- // // Senses // //----------------------------------------------------- CAI_Senses *GetSenses() { return m_pSenses; } const CAI_Senses *GetSenses() const { return m_pSenses; } void SetDistLook(float flDistLook); virtual bool QueryHearSound(CSound *pSound); virtual bool QuerySeeEntity(CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC = false); virtual void OnLooked(int iDistance); virtual void OnListened(); virtual void OnSeeEntity(CBaseEntity *pEntity) {} // If true, AI will try to see this entity regardless of distance. virtual bool ShouldNotDistanceCull() { return false; } virtual int GetSoundInterests(void); virtual int GetSoundPriority(CSound *pSound); CSound *GetLoudestSoundOfType(int iType); virtual CSound *GetBestSound(int validTypes = ALL_SOUNDS); virtual CSound *GetBestScent(void); virtual float HearingSensitivity(void) { return 1.0; } virtual bool ShouldIgnoreSound(CSound *) { return false; } bool SoundIsVisible(CSound *pSound); protected: virtual void ClearSenseConditions(void); private: void LockBestSound(); void UnlockBestSound(); CAI_Senses *m_pSenses; CSound *m_pLockedBestSound; public: //----------------------------------------------------- // // Enemy and target // //----------------------------------------------------- Vector GetSmoothedVelocity(void); CBaseEntity *GetEnemy() { return m_hEnemy.Get(); } CBaseEntity *GetEnemy() const { return m_hEnemy.Get(); } float GetTimeEnemyAcquired() { return m_flTimeEnemyAcquired; } void SetEnemy(CBaseEntity *pEnemy, bool bSetCondNewEnemy = true); const Vector &GetEnemyLKP() const; float GetEnemyLastTimeSeen() const; void MarkEnemyAsEluded(); void ClearEnemyMemory(); bool EnemyHasEludedMe() const; virtual CBaseEntity *BestEnemy(); // returns best enemy in memory list virtual bool IsValidEnemy(CBaseEntity *pEnemy); virtual bool CanBeAnEnemyOf(CBaseEntity *pEnemy); void ForceChooseNewEnemy() { m_EnemiesSerialNumber = -1; } bool ChooseEnemy(); virtual bool ShouldChooseNewEnemy(); virtual void GatherEnemyConditions(CBaseEntity *pEnemy); virtual float EnemyDistTolerance() { return 0; } // Enemy distances within this tolerance of each other are considered // equivalent. float EnemyDistance(CBaseEntity *pEnemy); CBaseCombatCharacter *GetEnemyCombatCharacterPointer(); void SetEnemyOccluder(CBaseEntity *pBlocker); CBaseEntity *GetEnemyOccluder(void); virtual void StartTargetHandling(CBaseEntity *pTargetEnt); //--------------------------------- CBaseEntity *GetTarget() { return m_hTargetEnt.Get(); } void SetTarget(CBaseEntity *pTarget); void CheckTarget(CBaseEntity *pTarget); float GetAcceptableTimeSeenEnemy(void) { return m_flAcceptableTimeSeenEnemy; } virtual CAI_BaseNPC *CreateCustomTarget(const Vector &vecOrigin, float duration = -1); void SetDeathPose(const int &iDeathPose) { m_iDeathPose = iDeathPose; } void SetDeathPoseFrame(const int &iDeathPoseFrame) { m_iDeathFrame = iDeathPoseFrame; } void SelectDeathPose(const CTakeDamageInfo &info); virtual bool ShouldPickADeathPose(void) { return true; } virtual bool AllowedToIgnite(void) { return false; } protected: virtual float GetGoalRepathTolerance(CBaseEntity *pGoalEnt, GoalType_t type, const Vector &curGoal, const Vector &curTargetPos); private: void *CheckEnemy(CBaseEntity *pEnemy) { return NULL; } // OBSOLETE, replaced by GatherEnemyConditions(), left here to make // derived code not compile // Updates the goal position in case of GOALTYPE_ENEMY void UpdateEnemyPos(); // Updates the goal position in case of GOALTYPE_TARGETENT void UpdateTargetPos(); //--------------------------------- EHANDLE m_hEnemy; // the entity that the npc is fighting. float m_flTimeEnemyAcquired; // The time at which the entity the NPC is // fighting became the NPC's enemy. EHANDLE m_hTargetEnt; // the entity that the npc is trying to reach CRandStopwatch m_GiveUpOnDeadEnemyTimer; CSimpleSimTimer m_FailChooseEnemyTimer; int m_EnemiesSerialNumber; float m_flAcceptableTimeSeenEnemy; CSimpleSimTimer m_UpdateEnemyPosTimer; static CSimpleSimTimer m_AnyUpdateEnemyPosTimer; public: //----------------------------------------------------- // // Commander mode stuff. // //----------------------------------------------------- virtual bool IsCommandable() { return false; } virtual bool IsPlayerAlly(CBasePlayer *pPlayer = NULL); virtual bool IsMedic() { return false; } virtual bool IsCommandMoving() { return false; } virtual bool ShouldAutoSummon() { return false; } virtual void SetCommandGoal(const Vector &vecGoal); virtual void ClearCommandGoal(); virtual void OnTargetOrder() {} virtual void OnMoveOrder() {} virtual bool IsValidCommandTarget(CBaseEntity *pTarget) { return false; } const Vector &GetCommandGoal() const { return m_vecCommandGoal; } virtual void OnMoveToCommandGoalFailed() {} string_t GetPlayerSquadName() const { Assert(gm_iszPlayerSquad != NULL_STRING); return gm_iszPlayerSquad; } bool IsInPlayerSquad() const; virtual CAI_BaseNPC *GetSquadCommandRepresentative() { return NULL; } virtual bool TargetOrder(CBaseEntity *pTarget, CAI_BaseNPC **Allies, int numAllies) { OnTargetOrder(); return true; } virtual void MoveOrder(const Vector &vecDest, CAI_BaseNPC **Allies, int numAllies) { SetCommandGoal(vecDest); SetCondition(COND_RECEIVED_ORDERS); OnMoveOrder(); } // Return true if you're willing to be idly talked to by other friends. virtual bool CanBeUsedAsAFriend(void); private: Vector m_vecCommandGoal; static string_t gm_iszPlayerSquad; public: CAI_MoveMonitor m_CommandMoveMonitor; //----------------------------------------------------- // Dynamic scripted NPC interactions //----------------------------------------------------- public: float GetInteractionYaw(void) const { return m_flInteractionYaw; } protected: void ParseScriptedNPCInteractions(void); void AddScriptedNPCInteraction(ScriptedNPCInteraction_t *pInteraction); const char *GetScriptedNPCInteractionSequence( ScriptedNPCInteraction_t *pInteraction, int iPhase); void StartRunningInteraction(CAI_BaseNPC *pOtherNPC, bool bActive); void StartScriptedNPCInteraction(CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector vecOtherOrigin, QAngle angOtherAngles); void CheckForScriptedNPCInteractions(void); void CalculateValidEnemyInteractions(void); void CheckForcedNPCInteractions(void); bool InteractionCouldStart(CAI_BaseNPC *pOtherNPC, ScriptedNPCInteraction_t *pInteraction, Vector &vecOrigin, QAngle &angAngles); virtual bool CanRunAScriptedNPCInteraction(bool bForced = false); bool IsRunningDynamicInteraction(void) { return (m_iInteractionState != NPCINT_NOT_RUNNING && (m_hCine != NULL)); } bool IsActiveDynamicInteraction(void) { return (m_iInteractionState == NPCINT_RUNNING_ACTIVE && (m_hCine != NULL)); } ScriptedNPCInteraction_t *GetRunningDynamicInteraction(void) { return &(m_ScriptedInteractions[m_iInteractionPlaying]); } void SetInteractionCantDie(bool bCantDie) { m_bCannotDieDuringInteraction = bCantDie; } bool HasInteractionCantDie(void); void InputForceInteractionWithNPC(inputdata_t &inputdata); void StartForcedInteraction(CAI_BaseNPC *pNPC, int iInteraction); void CleanupForcedInteraction(void); void CalculateForcedInteractionPosition(void); CAI_BaseNPC *GetInteractionPartner(void); private: // Forced interactions CHandle m_hForcedInteractionPartner; Vector m_vecForcedWorldPosition; float m_flForcedInteractionTimeout; // Abort the interaction if it hasn't // started by this time. CHandle m_hInteractionPartner; EHANDLE m_hLastInteractionTestTarget; bool m_bCannotDieDuringInteraction; int m_iInteractionState; int m_iInteractionPlaying; CUtlVector m_ScriptedInteractions; float m_flInteractionYaw; public: //----------------------------------------------------- // // Sounds // //----------------------------------------------------- virtual CanPlaySequence_t CanPlaySequence(bool fDisregardState, int interruptLevel); virtual bool CanPlaySentence(bool fDisregardState) { return IsAlive(); } virtual int PlaySentence(const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, CBaseEntity *pListener = NULL); virtual int PlayScriptedSentence(const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener); virtual bool FOkToMakeSound(int soundPriority = 0); virtual void JustMadeSound(int soundPriority = 0, float flSoundLength = 0.0f); virtual void DeathSound(const CTakeDamageInfo &info) { return; }; virtual void AlertSound(void) { return; }; virtual void IdleSound(void) { return; }; virtual void PainSound(const CTakeDamageInfo &info) { return; }; virtual void FearSound(void) { return; }; virtual void LostEnemySound(void) { return; }; virtual void FoundEnemySound(void) { return; }; virtual void BarnacleDeathSound(void) { CTakeDamageInfo info; PainSound(info); } virtual void SpeakSentence(int sentenceType) { return; }; virtual bool ShouldPlayIdleSound(void); virtual void MakeAIFootstepSound(float volume, float duration = 0.5f); //--------------------------------- virtual CAI_Expresser *GetExpresser() { return NULL; } const CAI_Expresser *GetExpresser() const { return const_cast(this)->GetExpresser(); } //--------------------------------- // NPC Event Response System virtual bool CanRespondToEvent(const char *ResponseConcept) { return false; } virtual bool RespondedTo(const char *ResponseConcept, bool bForce, bool bCancelScene) { return false; } virtual void PlayerHasIlluminatedNPC(CBasePlayer *pPlayer, float flDot); virtual void ModifyOrAppendCriteria(AI_CriteriaSet &set); protected: float SoundWaitTime() const { return m_flSoundWaitTime; } public: //----------------------------------------------------- // // Capabilities report (from CBaseCombatCharacter) // //----------------------------------------------------- virtual int CapabilitiesGet(void) const; // local capabilities access int CapabilitiesAdd(int capabilities); int CapabilitiesRemove(int capabilities); void CapabilitiesClear(void); private: int m_afCapability; // tells us what a npc can/can't do. public: //----------------------------------------------------- // // Pathfinding, navigation & movement // //----------------------------------------------------- CAI_Navigator *GetNavigator() { return m_pNavigator; } const CAI_Navigator *GetNavigator() const { return m_pNavigator; } CAI_LocalNavigator *GetLocalNavigator() { return m_pLocalNavigator; } const CAI_LocalNavigator *GetLocalNavigator() const { return m_pLocalNavigator; } CAI_Pathfinder *GetPathfinder() { return m_pPathfinder; } const CAI_Pathfinder *GetPathfinder() const { return m_pPathfinder; } CAI_MoveProbe *GetMoveProbe() { return m_pMoveProbe; } const CAI_MoveProbe *GetMoveProbe() const { return m_pMoveProbe; } CAI_Motor *GetMotor() { return m_pMotor; } const CAI_Motor *GetMotor() const { return m_pMotor; } //--------------------------------- static bool FindSpotForNPCInRadius(Vector *pResult, const Vector &vStartPos, CAI_BaseNPC *pNPC, float radius, bool bOutOfPlayerViewcone = false); //--------------------------------- virtual bool IsNavigationUrgent(); virtual bool ShouldFailNav(bool bMovementFailed); virtual bool ShouldBruteForceFailedNav() { return false; } // The current navigation (movement) mode (e.g. fly, swim, locomote, etc) Navigation_t GetNavType() const; void SetNavType(Navigation_t navType); CBaseEntity *GetNavTargetEntity(void); bool IsMoving(void); virtual float GetTimeToNavGoal(); // NPCs can override this to tweak with how costly particular movements are virtual bool MovementCost(int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost); // Turns a directional vector into a yaw value that points down that vector. float VecToYaw(const Vector &vecDir); // Turning virtual float CalcIdealYaw(const Vector &vecTarget); virtual float MaxYawSpeed(void); // Get max yaw speed bool FacingIdeal(void); void SetUpdatedYaw() { m_ScheduleState.bTaskUpdatedYaw = true; } // Add multiple facing goals while moving/standing still. virtual void AddFacingTarget(CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp = 0.0); virtual void AddFacingTarget(const Vector &vecPosition, float flImportance, float flDuration, float flRamp = 0.0); virtual void AddFacingTarget(CBaseEntity *pTarget, const Vector &vecPosition, float flImportance, float flDuration, float flRamp = 0.0); virtual float GetFacingDirection(Vector &vecDir); // ------------ // Methods used by motor to query properties/preferences/move-related state // ------------ virtual bool CanStandOn(CBaseEntity *pSurface) const; virtual bool IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos) const; // Override for specific creature types bool IsJumpLegal(const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist) const; bool ShouldMoveWait(); virtual float StepHeight() const { return 18.0f; } float GetStepDownMultiplier() const; virtual float GetMaxJumpSpeed() const { return 350.0f; } virtual float GetJumpGravity() const { return 1.0f; } //--------------------------------- virtual bool OverrideMove( float flInterval); // Override to take total control of movement // (return true if done so) virtual bool OverrideMoveFacing(const AILocalMoveGoal_t &move, float flInterval); //--------------------------------- virtual bool IsUnusableNode( int iNodeID, CAI_Hint *pHint); // Override for special NPC behavior virtual bool ValidateNavGoal(); virtual bool IsCurTaskContinuousMove(); virtual bool IsValidMoveAwayDest(const Vector &vecDest) { return true; } //--------------------------------- // // Notifications from navigator // virtual void OnMovementFailed(){}; virtual void OnMovementComplete(){}; //--------------------------------- bool FindNearestValidGoalPos(const Vector &vTestPoint, Vector *pResult); void RememberUnreachable( CBaseEntity *pEntity, float duration = -1); // Remember that entity is unreachable virtual bool IsUnreachable( CBaseEntity *pEntity); // Is entity is unreachable? //--------------------------------- // Inherited from IAI_MotorMovementServices virtual float CalcYawSpeed(void); virtual bool OnCalcBaseMove(AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult); virtual bool OnObstructionPreSteer(AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult); // Translations of the above into some useful game terms virtual bool OnObstructingDoor(AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult); virtual bool OnUpcomingPropDoor(AILocalMoveGoal_t *pMoveGoal, CBasePropDoor *pDoor, float distClear, AIMoveResult_t *pResult); void OpenPropDoorBegin(CBasePropDoor *pDoor); void OpenPropDoorNow(CBasePropDoor *pDoor); //--------------------------------- void DelayMoveStart(float delay) { m_flMoveWaitFinished = gpGlobals->curtime + delay; } float m_flMoveWaitFinished; // // Stuff for opening doors. // void OnDoorFullyOpen(CBasePropDoor *pDoor); void OnDoorBlocked(CBasePropDoor *pDoor); CHandle m_hOpeningDoor; // The CBasePropDoor that we are in the midst of // opening for navigation. protected: // BRJ 4/11 // Semi-obsolete-looking Lars code I moved out of the engine and into here int FlyMove(const Vector &vecPosition, unsigned int mask); int WalkMove(const Vector &vecPosition, unsigned int mask); // Unreachable Entities CUtlVector m_UnreachableEnts; // Array of unreachable entities private: CAI_Navigator *m_pNavigator; CAI_LocalNavigator *m_pLocalNavigator; CAI_Pathfinder *m_pPathfinder; CAI_MoveProbe *m_pMoveProbe; CAI_Motor *m_pMotor; EHANDLE m_hGoalEnt; // path corner we are heading towards float m_flTimeLastMovement; CSimpleSimTimer m_CheckOnGroundTimer; public: //----------------------------------------------------- // // Eye position, view offset, head direction, eye direction // //----------------------------------------------------- void SetDefaultEyeOffset(void); const Vector &GetDefaultEyeOffset(void) { return m_vDefaultEyeOffset; } virtual Vector GetNodeViewOffset() { return GetViewOffset(); } virtual Vector EyeOffset(Activity nActivity); virtual Vector EyePosition(void); //--------------------------------- virtual Vector HeadDirection2D(void); virtual Vector HeadDirection3D(void); virtual Vector EyeDirection2D(void); virtual Vector EyeDirection3D(void); virtual CBaseEntity *EyeLookTarget( void); // Overridden by subclass to force look at an entity 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 SetHeadDirection(const Vector &vTargetPos, float flInterval); virtual void MaintainLookTargets(float flInterval); virtual bool ValidEyeTarget(const Vector &lookTargetPos); virtual Vector FacingPosition(void) { return EyePosition(); }; // position that other npc's use when facing you virtual void MaintainTurnActivity(void); virtual bool FInAimCone(const Vector &vecSpot); virtual void AimGun(); virtual void SetAim(const Vector &aimDir); virtual void RelaxAim(void); virtual CBaseEntity *GetAlternateMoveShootTarget() { return NULL; } protected: Vector m_vDefaultEyeOffset; float m_flNextEyeLookTime; // Next time a pick a new place to look float m_flEyeIntegRate; // How fast does eye move to target private: Vector m_vEyeLookTarget; // Where I want to be looking Vector m_vCurEyeTarget; // Direction I'm looking at EHANDLE m_hEyeLookTarget; // What I want to be looking at float m_flHeadYaw; // Current head yaw float m_flHeadPitch; // Current head pitch protected: float m_flOriginalYaw; // This is the direction facing when the level // designer placed the NPC in the level. public: //----------------------------------------------------- // Mapmaker Scripting // // Set when the NPC is being scripted by a mapmaker, and // shouldn't be responding to external stimuli that would // break him out of his "script". NOT a scripted sequence. //----------------------------------------------------- inline bool IsInAScript(void) { return m_bInAScript; } inline void SetInAScript(bool bScript) { m_bInAScript = bScript; } void InputStartScripting(inputdata_t &inputdata) { m_bInAScript = true; } void InputStopScripting(inputdata_t &inputdata) { m_bInAScript = false; } void InputGagEnable(inputdata_t &inputdata) { AddSpawnFlags(SF_NPC_GAG); } void InputGagDisable(inputdata_t &inputdata) { RemoveSpawnFlags(SF_NPC_GAG); } bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter *sourceEnt); virtual void InputOutsideTransition(inputdata_t &inputdata); virtual void InputInsideTransition(inputdata_t &inputdata); void CleanupScriptsOnTeleport(bool bEnrouteAsWell); virtual void SetScriptedScheduleIgnoreConditions( Interruptability_t interrupt); private: bool m_bInAScript; public: //----------------------------------------------------- // // Scripting // //----------------------------------------------------- // Scripted sequence Info enum SCRIPTSTATE { SCRIPT_PLAYING = 0, // Playing the action animation. SCRIPT_WAIT, // Waiting on everyone in the script to be ready. Plays // the pre idle animation if there is one. SCRIPT_POST_IDLE, // Playing the post idle animation after playing the // action animation. SCRIPT_CLEANUP, // Cancelling the script / cleaning up. SCRIPT_WALK_TO_MARK, // Walking to the scripted sequence position. SCRIPT_RUN_TO_MARK, // Running to the scripted sequence position. SCRIPT_CUSTOM_MOVE_TO_MARK, // Moving to the scripted sequence position // while playing a custom movement // animation. }; bool ExitScriptedSequence(); bool CineCleanup(); virtual void Teleport(const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity); // forces movement and sets a new schedule virtual bool ScheduledMoveToGoalEntity(int scheduleType, CBaseEntity *pGoalEntity, Activity movementActivity); virtual bool ScheduledFollowPath(int scheduleType, CBaseEntity *pPathStart, Activity movementActivity); static void ForceSelectedGo(CBaseEntity *pPlayer, const Vector &targetPos, const Vector &traceDir, bool bRun); static void ForceSelectedGoRandom(void); bool AutoMovement(CBaseEntity *pTarget = NULL, AIMoveTrace_t *pTraceResult = NULL); bool AutoMovement(float flInterval, CBaseEntity *pTarget = NULL, AIMoveTrace_t *pTraceResult = NULL); bool TaskRanAutomovement(void) { return m_ScheduleState.bTaskRanAutomovement; } SCRIPTSTATE m_scriptState; // internal cinematic state CHandle m_hCine; Activity m_ScriptArrivalActivity; string_t m_strScriptArrivalSequence; //----------------------------------------------------- // // Scenes // //----------------------------------------------------- void AddSceneLock(float flDuration = 0.2f) { m_flSceneTime = MAX(gpGlobals->curtime + flDuration, m_flSceneTime); }; void ClearSceneLock(float flDuration = 0.2f) { m_flSceneTime = gpGlobals->curtime + flDuration; }; bool IsInLockedScene(void) { return m_flSceneTime > gpGlobals->curtime; }; float m_flSceneTime; string_t m_iszSceneCustomMoveSeq; public: //----------------------------------------------------- // // Memory // //----------------------------------------------------- inline void Remember(int iMemory) { m_afMemory |= iMemory; } inline void Forget(int iMemory) { m_afMemory &= ~iMemory; } inline bool HasMemory(int iMemory) { if (m_afMemory & iMemory) return TRUE; return FALSE; } inline bool HasAllMemories(int iMemory) { if ((m_afMemory & iMemory) == iMemory) return TRUE; return FALSE; } virtual CAI_Enemies *GetEnemies(void); virtual void RemoveMemory(void); virtual bool UpdateEnemyMemory(CBaseEntity *pEnemy, const Vector &position, CBaseEntity *pInformer = NULL); virtual float GetReactionDelay(CBaseEntity *pEnemy); void SetLastAttackTime(float time) { m_flLastAttackTime = time; } float GetLastAttackTime() const { return m_flLastAttackTime; } float GetLastDamageTime() const { return m_flLastDamageTime; } float GetLastPlayerDamageTime() const { return m_flLastPlayerDamageTime; } float GetLastEnemyTime() const { return m_flLastEnemyTime; } // Set up the shot regulator based on the equipped weapon virtual void OnChangeActiveWeapon(CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon); // Weapon holstering virtual bool CanHolsterWeapon(void); virtual int HolsterWeapon(void); virtual int UnholsterWeapon(void); void InputHolsterWeapon(inputdata_t &inputdata); void InputHolsterAndDestroyWeapon(inputdata_t &inputdata); void InputUnholsterWeapon(inputdata_t &inputdata); bool IsWeaponHolstered(void); bool IsWeaponStateChanging(void); void SetDesiredWeaponState(DesiredWeaponState_t iState) { m_iDesiredWeaponState = iState; } // NOTE: The Shot Regulator is used to manage the RangeAttack1 weapon. inline CAI_ShotRegulator *GetShotRegulator() { return &m_ShotRegulator; } virtual void OnRangeAttack1(); protected: // Shot regulator code virtual void OnUpdateShotRegulator(); protected: CAI_Enemies *m_pEnemies; // Holds information about enemies / danger // positions / shared between sqaud members int m_afMemory; EHANDLE m_hEnemyOccluder; // The entity my enemy is hiding behind. float m_flSumDamage; // How much consecutive damage I've received float m_flLastDamageTime; // Last time I received damage float m_flLastPlayerDamageTime; // Last time I received damage from the // player float m_flLastSawPlayerTime; // Last time I saw the player float m_flLastAttackTime; // Last time that I attacked my current enemy float m_flLastEnemyTime; float m_flNextWeaponSearchTime; // next time to search for a better weapon string_t m_iszPendingWeapon; // THe NPC should create and equip this weapon. bool m_bIgnoreUnseenEnemies; private: CAI_ShotRegulator m_ShotRegulator; // When should I shoot next? DesiredWeaponState_t m_iDesiredWeaponState; public: //----------------------------------------------------- // // Squads & tactics // //----------------------------------------------------- virtual bool InitSquad(void); virtual const char *SquadSlotName(int slotID) { return gm_SquadSlotNamespace.IdToSymbol(slotID); } bool OccupyStrategySlot(int squadSlotID); bool OccupyStrategySlotRange(int slotIDStart, int slotIDEnd); bool HasStrategySlot(int squadSlotID); bool HasStrategySlotRange(int slotIDStart, int slotIDEnd); int GetMyStrategySlot() { return m_iMySquadSlot; } void VacateStrategySlot(void); bool IsStrategySlotRangeOccupied( int slotIDStart, int slotIDEnd); // Returns true if all in the range are occupied CAI_Squad *GetSquad() { return m_pSquad; } virtual void SetSquad(CAI_Squad *pSquad); void AddToSquad(string_t name); void RemoveFromSquad(); void CheckSquad(); void SetSquadName(string_t name) { m_SquadName = name; } bool IsInSquad() const { return m_pSquad != NULL; } virtual bool IsSilentSquadMember() const { return false; } int NumWeaponsInSquad(const char *pszWeaponClassname); string_t GetHintGroup(void) { return m_strHintGroup; } void ClearHintGroup(void) { SetHintGroup(NULL_STRING); } void SetHintGroup(string_t name, bool bHintGroupNavLimiting = false); bool IsLimitingHintGroups(void) { return m_bHintGroupNavLimiting; } //--------------------------------- CAI_TacticalServices *GetTacticalServices() { return m_pTacticalServices; } const CAI_TacticalServices *GetTacticalServices() const { return m_pTacticalServices; } //--------------------------------- // Cover virtual bool FindCoverPos(CBaseEntity *pEntity, Vector *pResult); virtual bool FindCoverPosInRadius(CBaseEntity *pEntity, const Vector &goalPos, float coverRadius, Vector *pResult); virtual bool FindCoverPos(CSound *pSound, Vector *pResult); virtual bool IsValidCover(const Vector &vecCoverLocation, CAI_Hint const *pHint); virtual bool IsValidShootPosition(const Vector &vecCoverLocation, CAI_Node *pNode, CAI_Hint const *pHint); virtual bool TestShootPosition(const Vector &vecShootPos, const Vector &targetPos) { return WeaponLOSCondition(vecShootPos, targetPos, false); } virtual bool IsCoverPosition(const Vector &vecThreat, const Vector &vecPosition); virtual float CoverRadius(void) { return 1024; } // Default cover radius virtual float GetMaxTacticalLateralMovement(void) { return MAXTACLAT_IGNORE; } protected: virtual void OnChangeHintGroup(string_t oldGroup, string_t newGroup) {} CAI_Squad *m_pSquad; // The squad that I'm on string_t m_SquadName; int m_iMySquadSlot; // this is the behaviour slot that the npc currently // holds in the squad. private: string_t m_strHintGroup; bool m_bHintGroupNavLimiting; CAI_TacticalServices *m_pTacticalServices; public: //----------------------------------------------------- // // Base schedule & task support; Miscellaneous // //----------------------------------------------------- void InitRelationshipTable(void); void AddRelationship(const char *pszRelationship, CBaseEntity *pActivator); virtual void AddEntityRelationship(CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority); virtual void AddClassRelationship(Class_T nClass, Disposition_t nDisposition, int nPriority); void NPCUse(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); CBaseGrenade *IncomingGrenade(void); virtual bool ShouldFadeOnDeath(void); void NPCInitDead(void); // Call after animation/pose is set up void CorpseFallThink(void); float ThrowLimit(const Vector &vecStart, const Vector &vecEnd, float fGravity, float fArcSize, const Vector &mins, const Vector &maxs, CBaseEntity *pTarget, Vector *jumpVel, CBaseEntity **pBlocker); // these functions will survey conditions and set appropriate conditions // bits for attack types. virtual int RangeAttack1Conditions(float flDot, float flDist); virtual int RangeAttack2Conditions(float flDot, float flDist); virtual int MeleeAttack1Conditions(float flDot, float flDist); virtual int MeleeAttack2Conditions(float flDot, float flDist); virtual float InnateRange1MinRange(void) { return 0.0f; } virtual float InnateRange1MaxRange(void) { return FLT_MAX; } virtual bool OnBeginMoveAndShoot(void) { return true; } virtual void OnEndMoveAndShoot(void) {} virtual bool UseAttackSquadSlots() { return false; } //--------------------------------- virtual CBaseEntity *FindNamedEntity(const char *pszName, IEntityFindFilter *pFilter = NULL); //--------------------------------- // States //--------------------------------- virtual void ClearAttackConditions(void); void GatherAttackConditions(CBaseEntity *pTarget, float flDist); virtual bool ShouldLookForBetterWeapon(); bool Weapon_IsBetterAvailable(void); virtual Vector Weapon_ShootPosition(void); virtual void GiveWeapon(string_t iszWeaponName); virtual void OnGivenWeapon(CBaseCombatWeapon *pNewWeapon) {} bool IsMovingToPickupWeapon(); virtual bool WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions); virtual bool CurrentWeaponLOSCondition(const Vector &targetPos, bool bSetConditions) { return WeaponLOSCondition(GetAbsOrigin(), targetPos, bSetConditions); } virtual bool IsWaitingToRappel(void) { return false; } virtual void BeginRappel() {} // override to change the chase location of an enemy // This is where your origin should go when you are chasing pEnemy when his // origin is at chasePosition by default, leave this alone to make your // origin coincide with his. virtual void TranslateNavGoal(CBaseEntity *pEnemy, Vector &chasePosition); virtual float GetDefaultNavGoalTolerance() { return (GetHullWidth() * 2.0); } virtual bool FCanCheckAttacks(void); virtual void CheckAmmo(void) {} virtual bool FValidateHintType(CAI_Hint *pHint); virtual Activity GetHintActivity(short sHintType, Activity HintsActivity); virtual float GetHintDelay(short sHintType); virtual Activity GetCoverActivity(CAI_Hint *pHint); virtual Activity GetReloadActivity(CAI_Hint *pHint); virtual void SetTurnActivity(void); bool UpdateTurnGesture(void); // Returns the time when the door will be open float OpenDoorAndWait(CBaseEntity *pDoor); bool BBoxFlat(void); //--------------------------------- virtual void Ignite(float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false); virtual bool PassesDamageFilter(const CTakeDamageInfo &info); //--------------------------------- void MakeDamageBloodDecal(int cCount, float flNoise, trace_t *ptr, Vector vecDir); virtual float GetHitgroupDamageMultiplier(int iHitGroup, const CTakeDamageInfo &info); void TraceAttack(const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator); void DecalTrace(trace_t *pTrace, char const *decalName); void ImpactTrace(trace_t *pTrace, int iDamageType, const char *pCustomImpactName); virtual bool PlayerInSpread(const Vector &sourcePos, const Vector &targetPos, float flSpread, float maxDistOffCenter, bool ignoreHatedPlayers = true); CBaseEntity *PlayerInRange(const Vector &vecLocation, float flDist); bool PointInSpread(CBaseCombatCharacter *pCheckEntity, const Vector &sourcePos, const Vector &targetPos, const Vector &testPoint, float flSpread, float maxDistOffCenter); bool IsSquadmateInSpread(const Vector &sourcePos, const Vector &targetPos, float flSpread, float maxDistOffCenter); //--------------------------------- // combat functions //--------------------------------- virtual bool InnateWeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions); virtual Activity GetFlinchActivity(bool bHeavyDamage, bool bGesture); virtual bool Event_Gibbed(const CTakeDamageInfo &info); virtual void Event_Killed(const CTakeDamageInfo &info); virtual Vector GetShootEnemyDir(const Vector &shootOrigin, bool bNoisy = true); #ifdef HL2_DLL virtual Vector GetActualShootPosition(const Vector &shootOrigin); virtual Vector GetActualShootTrajectory(const Vector &shootOrigin); virtual Vector GetAttackSpread(CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL); virtual float GetSpreadBias(CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget); #endif // HL2_DLL virtual void CollectShotStats(const Vector &vecShootOrigin, const Vector &vecShootDir); virtual Vector BodyTarget(const Vector &posSrc, bool bNoisy = true); virtual Vector GetAutoAimCenter() { return BodyTarget(vec3_origin, false); } virtual void FireBullets(const FireBulletsInfo_t &info); // OLD VERSION! Use the struct version void FireBullets(int cShots, const Vector &vecSrc, const Vector &vecDirShooting, const Vector &vecSpread, float flDistance, int iAmmoType, int iTracerFreq = 4, int firingEntID = -1, int attachmentID = -1, int iDamage = 0, CBaseEntity *pAttacker = NULL, bool bFirstShotAccurate = false); virtual bool ShouldMoveAndShoot(void); //--------------------------------- // Damage //--------------------------------- virtual int OnTakeDamage_Alive(const CTakeDamageInfo &info); virtual int OnTakeDamage_Dying(const CTakeDamageInfo &info); virtual int OnTakeDamage_Dead(const CTakeDamageInfo &info); virtual void NotifyFriendsOfDamage(CBaseEntity *pAttackerEntity); virtual void OnFriendDamaged(CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker); virtual bool IsLightDamage(const CTakeDamageInfo &info); virtual bool IsHeavyDamage(const CTakeDamageInfo &info); void DoRadiusDamage(const CTakeDamageInfo &info, int iClassIgnore, CBaseEntity *pEntityIgnore); void DoRadiusDamage(const CTakeDamageInfo &info, const Vector &vecSrc, int iClassIgnore, CBaseEntity *pEntityIgnore); //--------------------------------- virtual void PickupWeapon(CBaseCombatWeapon *pWeapon); virtual void PickupItem(CBaseEntity *pItem){}; CBaseEntity *DropItem(const char *pszItemName, Vector vecPos, QAngle vecAng); // drop an item. //--------------------------------- // Inputs //--------------------------------- void InputSetRelationship(inputdata_t &inputdata); void InputSetEnemyFilter(inputdata_t &inputdata); void InputSetHealth(inputdata_t &inputdata); void InputBeginRappel(inputdata_t &inputdata); void InputSetSquad(inputdata_t &inputdata); void InputWake(inputdata_t &inputdata); void InputForgetEntity(inputdata_t &inputdata); void InputIgnoreDangerSounds(inputdata_t &inputdata); void InputUpdateEnemyMemory(inputdata_t &inputdata); //--------------------------------- virtual void NotifyDeadFriend(CBaseEntity *pFriend) { return; } //--------------------------------- // Utility methods static Vector CalcThrowVelocity(const Vector &startPos, const Vector &endPos, float fGravity, float fArcSize); //--------------------------------- float SetWait(float minWait, float maxWait = 0.0); void ClearWait(); float GetWaitFinishTime() { return m_flWaitFinished; } bool IsWaitFinished(); bool IsWaitSet(); CBaseEntity *GetGoalEnt() { return m_hGoalEnt; } void SetGoalEnt(CBaseEntity *pGoalEnt) { m_hGoalEnt.Set(pGoalEnt); } CAI_Hint *GetHintNode() { return m_pHintNode; } const CAI_Hint *GetHintNode() const { return m_pHintNode; } void SetHintNode(CAI_Hint *pHintNode); void ClearHintNode(float reuseDelay = 0.0); float m_flWaitFinished; // if we're told to wait, this is the time that the // wait will be over. float m_flNextFlinchTime; // Time at which we'll flinch fully again (as // opposed to just doing gesture flinches) float m_flNextDodgeTime; // Time at which I can dodge again. Used so that // the behavior doesn't happen over and over. CAI_MoveAndShootOverlay m_MoveAndShootOverlay; Vector m_vecLastPosition; // npc sometimes wants to return to where it // started after an operation. Vector m_vSavePosition; // position stored by code that called this schedules Vector m_vInterruptSavePosition; // position stored by a task that was // interrupted private: CHandle m_pHintNode; // this is the hint that the npc is moving // towards or performing active idle on. public: int m_cAmmoLoaded; // how much ammo is in the weapon (used to trigger // reload anim sequences) float m_flDistTooFar; // if enemy farther away than this, // bits_COND_ENEMY_TOOFAR set in GatherEnemyConditions string_t m_spawnEquipment; bool m_fNoDamageDecal; EHANDLE m_hStoredPathTarget; // For TASK_SET_GOAL Vector m_vecStoredPathGoal; // GoalType_t m_nStoredPathType; // int m_fStoredPathFlags; // CHandle m_hEnemyFilter; string_t m_iszEnemyFilterName; bool m_bDidDeathCleanup; IMPLEMENT_NETWORK_VAR_FOR_DERIVED(m_lifeState); //--------------------------------- // Outputs //--------------------------------- COutputEvent m_OnDamaged; COutputEvent m_OnDeath; COutputEvent m_OnHalfHealth; COutputEHANDLE m_OnFoundEnemy; COutputEvent m_OnLostEnemyLOS; COutputEvent m_OnLostEnemy; COutputEHANDLE m_OnFoundPlayer; COutputEvent m_OnLostPlayerLOS; COutputEvent m_OnLostPlayer; COutputEvent m_OnHearWorld; COutputEvent m_OnHearPlayer; COutputEvent m_OnHearCombat; COutputEvent m_OnDamagedByPlayer; COutputEvent m_OnDamagedByPlayerSquad; COutputEvent m_OnDenyCommanderUse; COutputEvent m_OnRappelTouchdown; COutputEvent m_OnSleep; COutputEvent m_OnWake; COutputEvent m_OnForcedInteractionStarted; COutputEvent m_OnForcedInteractionAborted; COutputEvent m_OnForcedInteractionFinished; public: // use this to shrink the bbox temporarily void SetHullSizeNormal(bool force = false); bool SetHullSizeSmall(bool force = false); bool IsUsingSmallHull() const { return m_fIsUsingSmallHull; } const Vector &GetHullMins() const { return NAI_Hull::Mins(GetHullType()); } const Vector &GetHullMaxs() const { return NAI_Hull::Maxs(GetHullType()); } float GetHullWidth() const { return NAI_Hull::Width(GetHullType()); } float GetHullHeight() const { return NAI_Hull::Height(GetHullType()); } void SetupVPhysicsHull(); virtual void StartTouch(CBaseEntity *pOther); void CheckPhysicsContacts(); private: void TryRestoreHull(void); bool m_fIsUsingSmallHull; bool m_bCheckContacts; private: // Task implementation helpers void StartTurn(float flDeltaYaw); bool FindCoverFromEnemy(bool bNodesOnly = false, float flMinDistance = 0, float flMaxDistance = FLT_MAX); bool FindCoverFromBestSound(Vector *pCoverPos); void StartScriptMoveToTargetTask(int task); void RunDieTask(); void RunAttackTask(int task); protected: virtual float CalcReasonableFacing(bool bIgnoreOriginalFacing = false); virtual bool IsValidReasonableFacing(const Vector &vecSightDir, float sightDist) { return true; } virtual float GetReasonableFacingDist(void); public: inline int UsableNPCObjectCaps(int baseCaps) { if (IsAlive()) baseCaps |= FCAP_IMPULSE_USE; return baseCaps; } virtual int ObjectCaps() { return (BaseClass::ObjectCaps() | FCAP_NOTIFY_ON_TRANSITION); } //----------------------------------------------------- // // Core mapped data structures // // String Registries for default AI Shared by all CBaseNPCs // These are used only during initialization and in debug //----------------------------------------------------- static void InitSchedulingTables(); static CAI_GlobalScheduleNamespace *GetSchedulingSymbols() { return &gm_SchedulingSymbols; } static CAI_ClassScheduleIdSpace &AccessClassScheduleIdSpaceDirect() { return gm_ClassScheduleIdSpace; } virtual CAI_ClassScheduleIdSpace *GetClassScheduleIdSpace() { return &gm_ClassScheduleIdSpace; } static int GetScheduleID(const char *schedName); static int GetActivityID(const char *actName); static int GetConditionID(const char *condName); static int GetTaskID(const char *taskName); static int GetSquadSlotID(const char *slotName); virtual const char *GetSquadSlotDebugName(int iSquadSlot); static const char *GetActivityName(int actID); static void AddActivityToSR(const char *actName, int conID); static void AddEventToSR(const char *eventName, int conID); static const char *GetEventName(int actID); static int GetEventID(const char *actName); public: //----------------------------------------------------- // Crouch handling //----------------------------------------------------- bool CrouchIsDesired(void) const; virtual bool IsCrouching(void); inline void ForceCrouch(void); inline void ClearForceCrouch(void); protected: virtual bool Crouch(void); virtual bool Stand(void); virtual void DesireCrouch(void); inline void DesireStand(void); bool CouldShootIfCrouching(CBaseEntity *pTarget); virtual bool IsCrouchedActivity(Activity activity); protected: // Override these in your derived NPC class virtual Vector GetCrouchEyeOffset(void) { return Vector(0, 0, 40); } virtual Vector GetCrouchGunOffset(void) { return Vector(0, 0, 36); } private: bool m_bCrouchDesired; bool m_bForceCrouch; bool m_bIsCrouching; //----------------------------------------------------- //----------------------------------------------------- // ai_post_frame_navigation //----------------------------------------------------- private: bool m_bDeferredNavigation; // This NPCs has a navigation query that's // being deferred until later in the frame public: void SetNavigationDeferred(bool bState) { m_bDeferredNavigation = bState; } bool IsNavigationDeferred(void) { return m_bDeferredNavigation; } //----------------------------------------------------- protected: static CAI_GlobalNamespace gm_SquadSlotNamespace; static CAI_LocalIdSpace gm_SquadSlotIdSpace; private: // Checks to see that the nav hull is valid for the NPC bool IsNavHullValid() const; friend class CAI_SystemHook; friend class CAI_SchedulesManager; static bool LoadDefaultSchedules(void); static void InitDefaultScheduleSR(void); static void InitDefaultTaskSR(void); static void InitDefaultConditionSR(void); static void InitDefaultActivitySR(void); static void InitDefaultSquadSlotSR(void); static CStringRegistry *m_pActivitySR; static int m_iNumActivities; static CStringRegistry *m_pEventSR; static int m_iNumEvents; static CAI_GlobalScheduleNamespace gm_SchedulingSymbols; static CAI_ClassScheduleIdSpace gm_ClassScheduleIdSpace; public: //---------------------------------------------------- // Debugging tools // // ----------------------------- // Debuging Fields and Methods // ----------------------------- const char *m_failText; // Text of why it failed const char *m_interruptText; // Text of why schedule interrupted CAI_Schedule *m_failedSchedule; // The schedule that failed last CAI_Schedule *m_interuptSchedule; // The schedule that was interrupted last int m_nDebugCurIndex; // Index used for stepping through AI virtual void ReportAIState(void); virtual void ReportOverThinkLimit(float time); void DumpTaskTimings(); void DrawDebugGeometryOverlays(void); virtual int DrawDebugTextOverlays(void); void ToggleFreeze(void); static void ClearAllSchedules(void); static int m_nDebugBits; static CAI_BaseNPC *m_pDebugNPC; static int m_nDebugPauseIndex; // Current step static inline void SetDebugNPC(CAI_BaseNPC *pNPC) { m_pDebugNPC = pNPC; } static inline bool IsDebugNPC(CAI_BaseNPC *pNPC) { return (pNPC == m_pDebugNPC); } float m_LastShootAccuracy; int m_TotalShots; int m_TotalHits; #ifdef _DEBUG bool m_bSelected; #endif float m_flSoundWaitTime; // Time when I'm allowed to make another sound int m_nSoundPriority; float m_flIgnoreDangerSoundsUntil; #ifdef AI_MONITOR_FOR_OSCILLATION CUtlVector m_ScheduleHistory; #endif // AI_MONITOR_FOR_OSCILLATION private: // Break into pieces! void Break(CBaseEntity *pBreaker); void InputBreak(inputdata_t &inputdata); friend void CC_NPC_Go(); friend void CC_NPC_GoRandom(); friend void CC_NPC_Freeze(const CCommand &args); public: CNetworkVar(bool, m_bPerformAvoidance); CNetworkVar(bool, m_bIsMoving); CNetworkVar(bool, m_bFadeCorpse); CNetworkVar(bool, m_bImportanRagdoll); CNetworkVar(bool, m_bSpeedModActive); CNetworkVar(int, m_iSpeedModRadius); CNetworkVar(int, m_iSpeedModSpeed); CNetworkVar( float, m_flTimePingEffect); // Display the pinged effect until this time void InputActivateSpeedModifier(inputdata_t &inputdata) { m_bSpeedModActive = true; } void InputDisableSpeedModifier(inputdata_t &inputdata) { m_bSpeedModActive = false; } void InputSetSpeedModifierRadius(inputdata_t &inputdata); void InputSetSpeedModifierSpeed(inputdata_t &inputdata); virtual bool ShouldProbeCollideAgainstEntity(CBaseEntity *pEntity); bool m_bPlayerAvoidState; void GetPlayerAvoidBounds(Vector *pMins, Vector *pMaxs); void StartPingEffect(void) { m_flTimePingEffect = gpGlobals->curtime + 2.0f; DispatchUpdateTransmitState(); } }; //----------------------------------------------------------------------------- // Purpose: Returns whether our ideal activity has started. If not, we are in // a transition sequence. //----------------------------------------------------------------------------- inline bool CAI_BaseNPC::IsActivityStarted(void) { return (GetSequence() == m_nIdealSequence); } //----------------------------------------------------------------------------- // Bullet firing (legacy)... //----------------------------------------------------------------------------- inline void CAI_BaseNPC::FireBullets(int cShots, const Vector &vecSrc, const Vector &vecDirShooting, const Vector &vecSpread, float flDistance, int iAmmoType, int iTracerFreq, int firingEntID, int attachmentID, int iDamage, CBaseEntity *pAttacker, bool bFirstShotAccurate) { FireBulletsInfo_t info; info.m_iShots = cShots; info.m_vecSrc = vecSrc; info.m_vecDirShooting = vecDirShooting; info.m_vecSpread = vecSpread; info.m_flDistance = flDistance; info.m_iAmmoType = iAmmoType; info.m_iTracerFreq = iTracerFreq; info.m_flDamage = iDamage; info.m_pAttacker = pAttacker; info.m_nFlags = bFirstShotAccurate ? FIRE_BULLETS_FIRST_SHOT_ACCURATE : 0; FireBullets(info); } //----------------------------------------------------------------------------- // Purpose: Sets the ideal state of this NPC. //----------------------------------------------------------------------------- inline void CAI_BaseNPC::SetIdealState(NPC_STATE eIdealState) { if (eIdealState != m_IdealNPCState) { /*switch (eIdealState) { case NPC_STATE_NONE: Msg("%s.SetIdealState: NPC_STATE_NONE\n", GetDebugName()); break; case NPC_STATE_IDLE: Msg("%s.SetIdealState: NPC_STATE_IDLE\n", GetDebugName()); break; case NPC_STATE_ALERT: Msg("%s.SetIdealState: NPC_STATE_ALERT\n", GetDebugName()); break; case NPC_STATE_COMBAT: Msg("%s.SetIdealState: NPC_STATE_COMBAT\n", GetDebugName()); break; case NPC_STATE_SCRIPT: Msg("%s.SetIdealState: NPC_STATE_SCRIPT\n", GetDebugName()); break; case NPC_STATE_PLAYDEAD: Msg("%s.SetIdealState: NPC_STATE_PLAYDEAD\n", GetDebugName()); break; case NPC_STATE_PRONE: Msg("%s.SetIdealState: NPC_STATE_PRONE\n", GetDebugName()); break; case NPC_STATE_DEAD: Msg("%s.SetIdealState: NPC_STATE_DEAD\n", GetDebugName()); break; default: Msg("%s.SetIdealState: \n", GetDebugName()); break; }*/ m_IdealNPCState = eIdealState; } } //----------------------------------------------------------------------------- // Purpose: Returns the current ideal state the NPC will try to achieve. //----------------------------------------------------------------------------- inline NPC_STATE CAI_BaseNPC::GetIdealState() { return m_IdealNPCState; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline int CAI_BaseNPC::IncScheduleCurTaskIndex() { m_ScheduleState.iTaskInterrupt = 0; m_ScheduleState.bTaskRanAutomovement = false; m_ScheduleState.bTaskUpdatedYaw = false; return ++m_ScheduleState.iCurTask; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline void CAI_BaseNPC::ResetScheduleCurTaskIndex() { m_ScheduleState.iCurTask = 0; m_ScheduleState.iTaskInterrupt = 0; m_ScheduleState.bTaskRanAutomovement = false; m_ScheduleState.bTaskUpdatedYaw = false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- inline bool CAI_BaseNPC::CrouchIsDesired(void) const { return ((CapabilitiesGet() & bits_CAP_DUCK) && (m_bCrouchDesired | m_bForceCrouch)); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- inline void CAI_BaseNPC::DesireStand(void) { m_bCrouchDesired = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- inline void CAI_BaseNPC::ForceCrouch(void) { m_bForceCrouch = true; Crouch(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- inline void CAI_BaseNPC::ClearForceCrouch(void) { m_bForceCrouch = false; if (IsCrouching()) { Stand(); } } inline bool CAI_BaseNPC::HaveSequenceForActivity(Activity activity) { #if STUDIO_SEQUENCE_ACTIVITY_LOOKUPS_ARE_SLOW return ((GetModelPtr()) ? (SelectWeightedSequence(activity) != ACTIVITY_NOT_AVAILABLE) : false); #else return ((GetModelPtr()) ? GetModelPtr()->HaveSequenceForActivity(activity) : false); #endif } typedef CHandle AIHANDLE; // ============================================================================ // Macros for introducing new schedules in sub-classes // // Strings registries and schedules use unique ID's for each item, but // sub-class enumerations are non-unique, so we translate between the // enumerations and unique ID's // ============================================================================ #define AI_BEGIN_CUSTOM_SCHEDULE_PROVIDER(derivedClass) \ IMPLEMENT_CUSTOM_SCHEDULE_PROVIDER(derivedClass) \ void derivedClass::InitCustomSchedules(void) { \ typedef derivedClass CNpc; \ const char *pszClassName = #derivedClass; \ \ CUtlVector schedulesToLoad; \ CUtlVector reqiredOthers; \ CAI_NamespaceInfos scheduleIds; \ CAI_NamespaceInfos taskIds; \ CAI_NamespaceInfos conditionIds; //----------------- #define AI_BEGIN_CUSTOM_NPC(className, derivedClass) \ IMPLEMENT_CUSTOM_AI(className, derivedClass) \ void derivedClass::InitCustomSchedules(void) { \ typedef derivedClass CNpc; \ const char *pszClassName = #derivedClass; \ \ CUtlVector schedulesToLoad; \ CUtlVector reqiredOthers; \ CAI_NamespaceInfos scheduleIds; \ CAI_NamespaceInfos taskIds; \ CAI_NamespaceInfos conditionIds; \ CAI_NamespaceInfos squadSlotIds; //----------------- #define EXTERN_SCHEDULE(id) \ scheduleIds.PushBack(#id, id); \ extern const char *g_psz##id; \ schedulesToLoad.AddToTail(g_psz##id); //----------------- #define DEFINE_SCHEDULE(id, text) \ scheduleIds.PushBack(#id, id); \ const char *g_psz##id = \ "\n Schedule" \ "\n " #id text "\n"; \ schedulesToLoad.AddToTail(g_psz##id); //----------------- #define DECLARE_CONDITION(id) conditionIds.PushBack(#id, id); //----------------- #define DECLARE_TASK(id) taskIds.PushBack(#id, id); //----------------- #define DECLARE_ACTIVITY(id) ADD_CUSTOM_ACTIVITY(CNpc, id); //----------------- #define DECLARE_SQUADSLOT(id) squadSlotIds.PushBack(#id, id); //----------------- #define DECLARE_INTERACTION(interaction) ADD_CUSTOM_INTERACTION(interaction); //----------------- #define DECLARE_ANIMEVENT(id) ADD_CUSTOM_ANIMEVENT(CNpc, id); //----------------- #define DECLARE_USES_SCHEDULE_PROVIDER(classname) \ reqiredOthers.AddToTail(ScheduleLoadHelper(classname)); //----------------- // IDs are stored and then added in order due to constraints in the namespace // implementation #define AI_END_CUSTOM_SCHEDULE_PROVIDER() \ \ int i; \ \ CNpc::AccessClassScheduleIdSpaceDirect().Init( \ pszClassName, BaseClass::GetSchedulingSymbols(), \ &BaseClass::AccessClassScheduleIdSpaceDirect()); \ \ scheduleIds.Sort(); \ taskIds.Sort(); \ conditionIds.Sort(); \ \ for (i = 0; i < scheduleIds.Count(); i++) { \ ADD_CUSTOM_SCHEDULE_NAMED(CNpc, scheduleIds[i].pszName, \ scheduleIds[i].localId); \ } \ \ for (i = 0; i < taskIds.Count(); i++) { \ ADD_CUSTOM_TASK_NAMED(CNpc, taskIds[i].pszName, taskIds[i].localId); \ } \ \ for (i = 0; i < conditionIds.Count(); i++) { \ if (ValidateConditionLimits(conditionIds[i].pszName)) { \ ADD_CUSTOM_CONDITION_NAMED(CNpc, conditionIds[i].pszName, \ conditionIds[i].localId); \ } \ } \ \ for (i = 0; i < reqiredOthers.Count(); i++) { \ (*reqiredOthers[i])(); \ } \ \ for (i = 0; i < schedulesToLoad.Count(); i++) { \ if (CNpc::gm_SchedLoadStatus.fValid) { \ CNpc::gm_SchedLoadStatus.fValid = \ g_AI_SchedulesManager.LoadSchedulesFromBuffer( \ pszClassName, schedulesToLoad[i], \ &AccessClassScheduleIdSpaceDirect()); \ } else \ break; \ } \ } inline bool ValidateConditionLimits(const char *pszNewCondition) { int nGlobalConditions = CAI_BaseNPC::GetSchedulingSymbols()->NumConditions(); if (nGlobalConditions >= MAX_CONDITIONS) { AssertMsg2( 0, "Exceeded max number of conditions (%d), ignoring condition %s\n", MAX_CONDITIONS, pszNewCondition); DevWarning( "Exceeded max number of conditions (%d), ignoring condition %s\n", MAX_CONDITIONS, pszNewCondition); return false; } return true; } //------------------------------------- // IDs are stored and then added in order due to constraints in the namespace // implementation #define AI_END_CUSTOM_NPC() \ \ int i; \ \ CNpc::AccessClassScheduleIdSpaceDirect().Init( \ pszClassName, BaseClass::GetSchedulingSymbols(), \ &BaseClass::AccessClassScheduleIdSpaceDirect()); \ CNpc::gm_SquadSlotIdSpace.Init(&BaseClass::gm_SquadSlotNamespace, \ &BaseClass::gm_SquadSlotIdSpace); \ \ scheduleIds.Sort(); \ taskIds.Sort(); \ conditionIds.Sort(); \ squadSlotIds.Sort(); \ \ for (i = 0; i < scheduleIds.Count(); i++) { \ ADD_CUSTOM_SCHEDULE_NAMED(CNpc, scheduleIds[i].pszName, \ scheduleIds[i].localId); \ } \ \ for (i = 0; i < taskIds.Count(); i++) { \ ADD_CUSTOM_TASK_NAMED(CNpc, taskIds[i].pszName, taskIds[i].localId); \ } \ \ for (i = 0; i < conditionIds.Count(); i++) { \ if (ValidateConditionLimits(conditionIds[i].pszName)) { \ ADD_CUSTOM_CONDITION_NAMED(CNpc, conditionIds[i].pszName, \ conditionIds[i].localId); \ } \ } \ \ for (i = 0; i < squadSlotIds.Count(); i++) { \ ADD_CUSTOM_SQUADSLOT_NAMED(CNpc, squadSlotIds[i].pszName, \ squadSlotIds[i].localId); \ } \ \ for (i = 0; i < reqiredOthers.Count(); i++) { \ (*reqiredOthers[i])(); \ } \ \ for (i = 0; i < schedulesToLoad.Count(); i++) { \ if (CNpc::gm_SchedLoadStatus.fValid) { \ CNpc::gm_SchedLoadStatus.fValid = \ g_AI_SchedulesManager.LoadSchedulesFromBuffer( \ pszClassName, schedulesToLoad[i], \ &AccessClassScheduleIdSpaceDirect()); \ } else \ break; \ } \ } //------------------------------------- struct AI_NamespaceAddInfo_t { AI_NamespaceAddInfo_t(const char *pszName, int localId) : pszName(pszName), localId(localId) {} const char *pszName; int localId; }; class CAI_NamespaceInfos : public CUtlVector { public: void PushBack(const char *pszName, int localId) { AddToTail(AI_NamespaceAddInfo_t(pszName, localId)); } void Sort() { CUtlVector::Sort(Compare); } private: static int __cdecl Compare(const AI_NamespaceAddInfo_t *pLeft, const AI_NamespaceAddInfo_t *pRight) { return pLeft->localId - pRight->localId; } }; //------------------------------------- // Declares the static variables that hold the string registry offset for the // new subclass as well as the initialization in schedule load functions struct AI_SchedLoadStatus_t { bool fValid; int signature; }; // Load schedules pulled out to support stepping through with debugger inline bool AI_DoLoadSchedules(bool (*pfnBaseLoad)(), void (*pfnInitCustomSchedules)(), AI_SchedLoadStatus_t *pLoadStatus) { (*pfnBaseLoad)(); if (pLoadStatus->signature != g_AI_SchedulesManager.GetScheduleLoadSignature()) { (*pfnInitCustomSchedules)(); pLoadStatus->fValid = true; pLoadStatus->signature = g_AI_SchedulesManager.GetScheduleLoadSignature(); } return pLoadStatus->fValid; } //------------------------------------- typedef bool (*AIScheduleLoadFunc_t)(); // @Note (toml 02-16-03): The following class exists to allow us to establish an // anonymous friendship in DEFINE_CUSTOM_SCHEDULE_PROVIDER. The particulars of // this implementation is almost entirely defined by bugs in MSVC 6.0 class ScheduleLoadHelperImpl { public: template static AIScheduleLoadFunc_t AccessScheduleLoadFunc(T *) { return (&T::LoadSchedules); } }; #define ScheduleLoadHelper(type) \ (ScheduleLoadHelperImpl::AccessScheduleLoadFunc((type *)0)) //------------------------------------- #define DEFINE_CUSTOM_SCHEDULE_PROVIDER \ static AI_SchedLoadStatus_t gm_SchedLoadStatus; \ static CAI_ClassScheduleIdSpace gm_ClassScheduleIdSpace; \ static const char *gm_pszErrorClassName; \ \ static CAI_ClassScheduleIdSpace &AccessClassScheduleIdSpaceDirect() { \ return gm_ClassScheduleIdSpace; \ } \ virtual CAI_ClassScheduleIdSpace *GetClassScheduleIdSpace() { \ return &gm_ClassScheduleIdSpace; \ } \ virtual const char *GetSchedulingErrorName() { \ return gm_pszErrorClassName; \ } \ \ static void InitCustomSchedules(void); \ \ static bool LoadSchedules(void); \ virtual bool LoadedSchedules(void); \ \ friend class ScheduleLoadHelperImpl; \ \ class CScheduleLoader { \ public: \ CScheduleLoader(); \ } m_ScheduleLoader; \ \ friend class CScheduleLoader; //------------------------------------- #define DEFINE_CUSTOM_AI \ DEFINE_CUSTOM_SCHEDULE_PROVIDER \ \ static CAI_LocalIdSpace gm_SquadSlotIdSpace; \ \ const char *SquadSlotName(int squadSlotID); //------------------------------------- #define IMPLEMENT_CUSTOM_SCHEDULE_PROVIDER(derivedClass) \ AI_SchedLoadStatus_t derivedClass::gm_SchedLoadStatus = {true, -1}; \ CAI_ClassScheduleIdSpace derivedClass::gm_ClassScheduleIdSpace; \ const char *derivedClass::gm_pszErrorClassName = #derivedClass; \ \ derivedClass::CScheduleLoader::CScheduleLoader() { \ derivedClass::LoadSchedules(); \ } \ \ /* --------------------------------------------- */ \ /* Load schedules for this type of NPC */ \ /* --------------------------------------------- */ \ bool derivedClass::LoadSchedules(void) { \ return AI_DoLoadSchedules(derivedClass::BaseClass::LoadSchedules, \ derivedClass::InitCustomSchedules, \ &derivedClass::gm_SchedLoadStatus); \ } \ \ bool derivedClass::LoadedSchedules(void) { \ return derivedClass::gm_SchedLoadStatus.fValid; \ } //------------------------------------- // Initialize offsets and implement methods for loading and getting squad info // for the subclass #define IMPLEMENT_CUSTOM_AI(className, derivedClass) \ IMPLEMENT_CUSTOM_SCHEDULE_PROVIDER(derivedClass) \ \ CAI_LocalIdSpace derivedClass::gm_SquadSlotIdSpace; \ \ /* -------------------------------------------------- */ \ /* Given squadSlot enumeration return squadSlot name */ \ /* -------------------------------------------------- */ \ const char *derivedClass::SquadSlotName(int slotEN) { \ return gm_SquadSlotNamespace.IdToSymbol( \ derivedClass::gm_SquadSlotIdSpace.LocalToGlobal(slotEN)); \ } //------------------------------------- #define ADD_CUSTOM_SCHEDULE_NAMED(derivedClass, schedName, schedEN) \ if (!derivedClass::AccessClassScheduleIdSpaceDirect().AddSchedule( \ schedName, schedEN, derivedClass::gm_pszErrorClassName)) \ return; #define ADD_CUSTOM_SCHEDULE(derivedClass, schedEN) \ ADD_CUSTOM_SCHEDULE_NAMED(derivedClass, #schedEN, schedEN) #define ADD_CUSTOM_TASK_NAMED(derivedClass, taskName, taskEN) \ if (!derivedClass::AccessClassScheduleIdSpaceDirect().AddTask( \ taskName, taskEN, derivedClass::gm_pszErrorClassName)) \ return; #define ADD_CUSTOM_TASK(derivedClass, taskEN) \ ADD_CUSTOM_TASK_NAMED(derivedClass, #taskEN, taskEN) #define ADD_CUSTOM_CONDITION_NAMED(derivedClass, condName, condEN) \ if (!derivedClass::AccessClassScheduleIdSpaceDirect().AddCondition( \ condName, condEN, derivedClass::gm_pszErrorClassName)) \ return; #define ADD_CUSTOM_CONDITION(derivedClass, condEN) \ ADD_CUSTOM_CONDITION_NAMED(derivedClass, #condEN, condEN) //------------------------------------- #define INIT_CUSTOM_AI(derivedClass) \ derivedClass::AccessClassScheduleIdSpaceDirect().Init( \ #derivedClass, BaseClass::GetSchedulingSymbols(), \ &BaseClass::AccessClassScheduleIdSpaceDirect()); \ derivedClass::gm_SquadSlotIdSpace.Init( \ &CAI_BaseNPC::gm_SquadSlotNamespace, &BaseClass::gm_SquadSlotIdSpace); #define ADD_CUSTOM_INTERACTION(interaction) \ { interaction = CBaseCombatCharacter::GetInteractionID(); } #define ADD_CUSTOM_SQUADSLOT_NAMED(derivedClass, squadSlotName, squadSlotEN) \ if (!derivedClass::gm_SquadSlotIdSpace.AddSymbol( \ squadSlotName, squadSlotEN, "squadslot", \ derivedClass::gm_pszErrorClassName)) \ return; #define ADD_CUSTOM_SQUADSLOT(derivedClass, squadSlotEN) \ ADD_CUSTOM_SQUADSLOT_NAMED(derivedClass, #squadSlotEN, squadSlotEN) #define ADD_CUSTOM_ACTIVITY_NAMED(derivedClass, activityName, activityEnum) \ REGISTER_PRIVATE_ACTIVITY(activityEnum); \ CAI_BaseNPC::AddActivityToSR(activityName, activityEnum); #define ADD_CUSTOM_ACTIVITY(derivedClass, activityEnum) \ ADD_CUSTOM_ACTIVITY_NAMED(derivedClass, #activityEnum, activityEnum) #define ADD_CUSTOM_ANIMEVENT_NAMED(derivedClass, eventName, eventEnum) \ REGISTER_PRIVATE_ANIMEVENT(eventEnum); \ CAI_BaseNPC::AddEventToSR(eventName, eventEnum); #define ADD_CUSTOM_ANIMEVENT(derivedClass, eventEnum) \ ADD_CUSTOM_ANIMEVENT_NAMED(derivedClass, #eventEnum, eventEnum) //============================================================================= // class CAI_Component //============================================================================= inline const Vector &CAI_Component::GetLocalOrigin() const { return GetOuter()->GetLocalOrigin(); } //----------------------------------------------------------------------------- inline void CAI_Component::SetLocalOrigin(const Vector &origin) { GetOuter()->SetLocalOrigin(origin); } //----------------------------------------------------------------------------- inline const Vector &CAI_Component::GetAbsOrigin() const { return GetOuter()->GetAbsOrigin(); } //----------------------------------------------------------------------------- inline const QAngle &CAI_Component::GetAbsAngles() const { return GetOuter()->GetAbsAngles(); } //----------------------------------------------------------------------------- inline void CAI_Component::SetSolid(SolidType_t val) { GetOuter()->SetSolid(val); } //----------------------------------------------------------------------------- inline SolidType_t CAI_Component::GetSolid() const { return GetOuter()->GetSolid(); } //----------------------------------------------------------------------------- inline const Vector &CAI_Component::WorldAlignMins() const { return GetOuter()->WorldAlignMins(); } //----------------------------------------------------------------------------- inline const Vector &CAI_Component::WorldAlignMaxs() const { return GetOuter()->WorldAlignMaxs(); } //----------------------------------------------------------------------------- inline Hull_t CAI_Component::GetHullType() const { return GetOuter()->GetHullType(); } //----------------------------------------------------------------------------- inline Vector CAI_Component::WorldSpaceCenter() const { return GetOuter()->WorldSpaceCenter(); } //----------------------------------------------------------------------------- inline float CAI_Component::GetGravity() const { return GetOuter()->GetGravity(); } //----------------------------------------------------------------------------- inline void CAI_Component::SetGravity(float flGravity) { GetOuter()->SetGravity(flGravity); } //----------------------------------------------------------------------------- inline float CAI_Component::GetHullWidth() const { return NAI_Hull::Width(GetOuter()->GetHullType()); } //----------------------------------------------------------------------------- inline float CAI_Component::GetHullHeight() const { return NAI_Hull::Height(GetOuter()->GetHullType()); } //----------------------------------------------------------------------------- inline const Vector &CAI_Component::GetHullMins() const { return NAI_Hull::Mins(GetOuter()->GetHullType()); } //----------------------------------------------------------------------------- inline const Vector &CAI_Component::GetHullMaxs() const { return NAI_Hull::Maxs(GetOuter()->GetHullType()); } //----------------------------------------------------------------------------- inline int CAI_Component::GetCollisionGroup() const { return GetOuter()->GetCollisionGroup(); } //----------------------------------------------------------------------------- inline CBaseEntity *CAI_Component::GetEnemy() { return GetOuter()->GetEnemy(); } //----------------------------------------------------------------------------- inline const Vector &CAI_Component::GetEnemyLKP() const { return GetOuter()->GetEnemyLKP(); } //----------------------------------------------------------------------------- inline void CAI_Component::TranslateNavGoal(CBaseEntity *pEnemy, Vector &chasePosition) { GetOuter()->TranslateNavGoal(pEnemy, chasePosition); } //----------------------------------------------------------------------------- inline CBaseEntity *CAI_Component::GetTarget() { return GetOuter()->GetTarget(); } //----------------------------------------------------------------------------- inline void CAI_Component::SetTarget(CBaseEntity *pTarget) { GetOuter()->SetTarget(pTarget); } //----------------------------------------------------------------------------- inline const Task_t *CAI_Component::GetCurTask() { return GetOuter()->GetTask(); } //----------------------------------------------------------------------------- inline void CAI_Component::TaskFail(AI_TaskFailureCode_t code) { GetOuter()->TaskFail(code); } //----------------------------------------------------------------------------- inline void CAI_Component::TaskFail(const char *pszGeneralFailText) { GetOuter()->TaskFail(pszGeneralFailText); } //----------------------------------------------------------------------------- inline void CAI_Component::TaskComplete(bool fIgnoreSetFailedCondition) { GetOuter()->TaskComplete(fIgnoreSetFailedCondition); } //----------------------------------------------------------------------------- inline int CAI_Component::TaskIsRunning() { return GetOuter()->TaskIsRunning(); } //----------------------------------------------------------------------------- inline int CAI_Component::TaskIsComplete() { return GetOuter()->TaskIsComplete(); } //----------------------------------------------------------------------------- inline Activity CAI_Component::GetActivity() { return GetOuter()->GetActivity(); } //----------------------------------------------------------------------------- inline void CAI_Component::SetActivity(Activity NewActivity) { GetOuter()->SetActivity(NewActivity); } //----------------------------------------------------------------------------- inline float CAI_Component::GetIdealSpeed() const { return GetOuter()->GetIdealSpeed(); } //----------------------------------------------------------------------------- inline float CAI_Component::GetIdealAccel() const { return GetOuter()->GetIdealAccel(); } //----------------------------------------------------------------------------- inline int CAI_Component::GetSequence() { return GetOuter()->GetSequence(); } //----------------------------------------------------------------------------- inline int CAI_Component::GetEntFlags() const { return GetOuter()->GetFlags(); } //----------------------------------------------------------------------------- inline void CAI_Component::AddEntFlag(int flags) { GetOuter()->AddFlag(flags); } //----------------------------------------------------------------------------- inline void CAI_Component::RemoveEntFlag(int flagsToRemove) { GetOuter()->RemoveFlag(flagsToRemove); } //----------------------------------------------------------------------------- // Purpose: Change the ground entity for the outer // Input : *ground - // Output : inline void //----------------------------------------------------------------------------- inline void CAI_Component::SetGroundEntity(CBaseEntity *ground) { GetOuter()->SetGroundEntity(ground); } //----------------------------------------------------------------------------- inline void CAI_Component::ToggleEntFlag(int flagToToggle) { GetOuter()->ToggleFlag(flagToToggle); } //----------------------------------------------------------------------------- inline CBaseEntity *CAI_Component::GetGoalEnt() { return GetOuter()->GetGoalEnt(); } //----------------------------------------------------------------------------- inline void CAI_Component::SetGoalEnt(CBaseEntity *pGoalEnt) { GetOuter()->SetGoalEnt(pGoalEnt); } //----------------------------------------------------------------------------- inline void CAI_Component::Remember(int iMemory) { GetOuter()->Remember(iMemory); } //----------------------------------------------------------------------------- inline void CAI_Component::Forget(int iMemory) { GetOuter()->Forget(iMemory); } //----------------------------------------------------------------------------- inline bool CAI_Component::HasMemory(int iMemory) { return GetOuter()->HasMemory(iMemory); } //----------------------------------------------------------------------------- inline CAI_Enemies *CAI_Component::GetEnemies() { return GetOuter()->GetEnemies(); } //----------------------------------------------------------------------------- inline const char *CAI_Component::GetEntClassname() { return GetOuter()->GetClassname(); } //----------------------------------------------------------------------------- inline int CAI_Component::CapabilitiesGet() { return GetOuter()->CapabilitiesGet(); } //----------------------------------------------------------------------------- inline void CAI_Component::SetLocalAngles(const QAngle &angles) { GetOuter()->SetLocalAngles(angles); } //----------------------------------------------------------------------------- inline const QAngle &CAI_Component::GetLocalAngles(void) const { return GetOuter()->GetLocalAngles(); } //----------------------------------------------------------------------------- inline edict_t *CAI_Component::GetEdict() { return GetOuter()->NetworkProp()->edict(); } //----------------------------------------------------------------------------- inline float CAI_Component::GetLastThink(const char *szContext) { return GetOuter()->GetLastThink(szContext); } // ============================================================================ abstract_class INPCInteractive { public: virtual bool CanInteractWith(CAI_BaseNPC * pUser) = 0; virtual bool HasBeenInteractedWith() = 0; virtual void NotifyInteraction(CAI_BaseNPC * pUser) = 0; // Alyx specific interactions virtual void AlyxStartedInteraction(void) = 0; virtual void AlyxFinishedInteraction(void) = 0; }; // Base Class for any NPC that wants to be interactable by other NPCS (i.e. Alyx // Hackable) NOTE: YOU MUST DEFINE THE OUTPUTS IN YOUR CLASS'S DATADESC! // THE DO SO, INSERT THE FOLLOWING MACRO INTO YOUR CLASS'S //DATADESC. // #define DEFINE_BASENPCINTERACTABLE_DATADESC() \ DEFINE_OUTPUT(m_OnAlyxStartedInteraction, "OnAlyxStartedInteraction"), \ DEFINE_OUTPUT(m_OnAlyxFinishedInteraction, \ "OnAlyxFinishedInteraction"), \ DEFINE_INPUTFUNC(FIELD_VOID, "InteractivePowerDown", InputPowerdown) template class CNPCBaseInteractive : public NPC_CLASS, public INPCInteractive { DECLARE_CLASS(CNPCBaseInteractive, NPC_CLASS); public: virtual bool CanInteractWith(CAI_BaseNPC *pUser) { return false; }; virtual bool HasBeenInteractedWith() { return false; }; virtual void NotifyInteraction(CAI_BaseNPC *pUser) { return; }; virtual void InputPowerdown(inputdata_t &inputdata) {} // Alyx specific interactions virtual void AlyxStartedInteraction(void) { m_OnAlyxStartedInteraction.FireOutput(this, this); } virtual void AlyxFinishedInteraction(void) { m_OnAlyxFinishedInteraction.FireOutput(this, this); } public: // Outputs // Alyx specific interactions COutputEvent m_OnAlyxStartedInteraction; COutputEvent m_OnAlyxFinishedInteraction; }; // // Deferred Navigation calls go here // extern ConVar ai_post_frame_navigation; class CPostFrameNavigationHook : public CBaseGameSystemPerFrame { public: virtual const char *Name(void) { return "CPostFrameNavigationHook"; } virtual bool Init(void); virtual void FrameUpdatePostEntityThink(void); virtual void FrameUpdatePreEntityThink(void); bool IsGameFrameRunning(void) { return m_bGameFrameRunning; } void SetGrameFrameRunning(bool bState) { m_bGameFrameRunning = bState; } void EnqueueEntityNavigationQuery(CAI_BaseNPC *pNPC, CFunctor *functor); private: CUtlVector m_Functors; bool m_bGameFrameRunning; }; extern CPostFrameNavigationHook *PostFrameNavigationSystem(void); #endif // AI_BASENPC_H