You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3125 lines
107 KiB
3125 lines
107 KiB
//========= 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 "simtimer.h" |
|
#include "basecombatcharacter.h" |
|
#include "ai_debug.h" |
|
#include "ai_default.h" |
|
#include "ai_schedule.h" |
|
#include "ai_condition.h" |
|
#include "ai_component.h" |
|
#include "ai_task.h" |
|
#include "ai_movetypes.h" |
|
#include "ai_navtype.h" |
|
#include "ai_namespaces.h" |
|
#include "ai_npcstate.h" |
|
#include "ai_hull.h" |
|
#include "ai_utils.h" |
|
#include "ai_moveshoot.h" |
|
#include "entityoutput.h" |
|
#include "utlvector.h" |
|
#include "activitylist.h" |
|
#include "bitstring.h" |
|
#include "ai_basenpc.h" |
|
#include "ai_navgoaltype.h" //GoalType_t enum |
|
#include "eventlist.h" |
|
#include "soundent.h" |
|
#include "ai_navigator.h" |
|
#include "tier1/functors.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 ShouldGib( const CTakeDamageInfo &info ) { return false; } // Always ragdoll, unless specified by the leaf class |
|
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
|
|
|