This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

3329 lines
122 KiB
C++

//========= 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<MAX_CONDITIONS> 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<CAI_BaseNPC *> 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 <class BEHAVIOR_TYPE>
bool GetBehavior(BEHAVIOR_TYPE **ppBehavior) {
CAI_BehaviorBase **ppBehaviors = AccessBehaviors();
*ppBehavior = NULL;
for (int i = 0; i < NumBehaviors(); i++) {
*ppBehavior = dynamic_cast<BEHAVIOR_TYPE *>(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<CAI_BaseNPC> m_hForcedInteractionPartner;
Vector m_vecForcedWorldPosition;
float m_flForcedInteractionTimeout; // Abort the interaction if it hasn't
// started by this time.
CHandle<CAI_BaseNPC> m_hInteractionPartner;
EHANDLE m_hLastInteractionTestTarget;
bool m_bCannotDieDuringInteraction;
int m_iInteractionState;
int m_iInteractionPlaying;
CUtlVector<ScriptedNPCInteraction_t> 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<CAI_BaseNPC *>(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<CBasePropDoor>
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<UnreachableEnt_t>
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<CAI_ScriptedSequence> 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<CAI_Hint> 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<CBaseFilter> 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<AIScheduleChoice_t> 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: <Unknown>\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<CAI_BaseNPC> 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<const char *> schedulesToLoad; \
CUtlVector<AIScheduleLoadFunc_t> 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<const char *> schedulesToLoad; \
CUtlVector<AIScheduleLoadFunc_t> 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<AI_NamespaceAddInfo_t> {
public:
void PushBack(const char *pszName, int localId) {
AddToTail(AI_NamespaceAddInfo_t(pszName, localId));
}
void Sort() { CUtlVector<AI_NamespaceAddInfo_t>::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 <typename T>
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 NPC_CLASS>
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<CFunctor *> m_Functors;
bool m_bGameFrameRunning;
};
extern CPostFrameNavigationHook *PostFrameNavigationSystem(void);
#endif // AI_BASENPC_H