Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

3239 lines
112 KiB

//========= Copyright © 1996-2005, 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_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;
class CGlobalEvent;
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 flCameraDistance;
QAngle angCameraAngles;
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;
//=============================================================================
// Purpose: Some bridges a little more complicated to allow behavior to see
// what base class would do or control order in which it's done
//=============================================================================
abstract_class IAI_BehaviorBridge
{
public:
#define AI_GENERATE_BRIDGE_INTERFACE
#include "ai_behavior_template.h"
// Non-standard bridge methods
virtual void BehaviorBridge_GatherConditions() {}
virtual int BehaviorBridge_SelectSchedule() { return 0; }
virtual int BehaviorBridge_TranslateSchedule( int scheduleType ) { return 0; }
virtual float BehaviorBridge_GetJumpGravity() const { return 0; }
virtual bool BehaviorBridge_IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist ) const { return 0; }
virtual bool BehaviorBridge_MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ) { return 0; }
};
//=============================================================================
//
// class CAI_BaseNPC
//
//=============================================================================
class CAI_BaseNPC : public CBaseCombatCharacter,
public CAI_DefMovementSink,
public IAI_BehaviorBridge
{
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 );
virtual void OnSetSchedule( 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 void TranslateAddOnAttachment( char *pchAttachmentName, int iCount );
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
bool IsForceGatherConditionsSet() { return m_bForceConditionsGather; }
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();
virtual void RunTaskOverlay();
virtual bool ModifyAutoMovement( Vector &vecNewPos ) { return false; } // allow NPCs to adjust automovement
void EndTaskOverlay();
virtual void PostRunStopMoving();
virtual 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 );
inline CAI_Schedule *GetCurSchedule() const { 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 );
protected:
bool IsInChoreo() const;
// Choreo state is reset each time UpdateEfficiency() is called. Setting it in leaf code
// outside UpdateEfficiency() will result in your changes being lost.
bool m_bInChoreo;
protected:
// 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();
virtual 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;
static int gm_iNextThinkRebalanceTick;
static float gm_flTimeLastSpawn;
static int gm_nSpawnedThisFrame;
CGlobalEvent *m_pScheduleEvent;
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
//
//-----------------------------------------------------
void AddBehavior( CAI_BehaviorBase *pBehavior );
void RemoveAndDestroyBehavior( CAI_BehaviorBase *pBehavior );
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 bool ShouldBehaviorSelectSchedule( CAI_BehaviorBase *pBehavior ) { return true; }
bool BehaviorSelectSchedule();
bool IsRunningBehavior() const;
CAI_BehaviorBase *GetPrimaryBehavior();
CAI_BehaviorBase *DeferSchedulingToBehavior( CAI_BehaviorBase *pNewBehavior );
void SetPrimaryBehavior( CAI_BehaviorBase *pNewBehavior );
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 );
virtual void OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior, CAI_BehaviorBase *pNewBehavior ) {}
virtual CAI_BehaviorBase ** AccessBehaviors()
{
if (m_Behaviors.Count())
return m_Behaviors.Base();
return NULL;
}
virtual int NumBehaviors()
{
return m_Behaviors.Count();
}
// Automatically called during entity construction, derived class calls AddBehavior()
virtual bool CreateBehaviors() { return true; }
protected:
CAI_BehaviorBase * m_pPrimaryBehavior;
CUtlVector<CAI_BehaviorBase *> m_Behaviors;
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 );
virtual void OnConditionSet( int nCondition ) { };
virtual void OnConditionCleared( int nCondition ) { };
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; }
virtual 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 SetIdealSequence( int iSequence, bool bReset = false ) { if ( bReset ) ResetIdealActivity( ACT_SPECIFIC_SEQUENCE ); else SetIdealActivity( ACT_SPECIFIC_SEQUENCE ); m_nIdealSequence = iSequence; }
void ResetIdealActivity( Activity newIdealActivity );
void SetSequenceByName( 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 );
virtual void OnEnemyChanged( CBaseEntity *pOldEnemy, CBaseEntity *pNewEnemy ) { }
virtual 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() { AssertMsg(false, "Called GetExpresser() on something that has no expresser!\n"); 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( float flTolerance = 0.0f );
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
virtual bool IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos, float maxUp, float maxDown, float maxDist ) const;
bool ShouldMoveWait();
#ifdef INFESTED_DLL
virtual float StepHeight() const { return 24.0f; } // NOTE: Have to set this here rather than in the Infested derived AI classes, as this value is used by the AI node network generation
#else
virtual float StepHeight() const { return 18.0f; }
#endif
float GetStepDownMultiplier() const;
virtual float GetMaxJumpSpeed() const { return 350.0f; }
virtual float GetJumpGravity() const { return GetDefaultJumpGravity(); }
virtual float GetDefaultJumpGravity() const { return 1.0f; }
virtual float GetMinJumpHeight() const { return 0; }
//---------------------------------
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() {};
//---------------------------------
virtual 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.
float m_flFaceEnemyTolerance; // min. angle difference required when running TASK_FACE_ENEMY
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, bool bUseSlowHighAccuracyContacts = true );
// 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 void ChangeFaction( int nNewFaction );
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; }
bool FindCoverFromEnemy( bool bNodesOnly = false, float flMinDistance = 0, float flMaxDistance = FLT_MAX );
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 );
void DecalTrace( trace_t *pTrace, char const *decalName );
void ImpactTrace( trace_t *pTrace, int iDamageType, 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 );
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 );
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, float flDamage = 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( 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 );
void InputCreateAddon( 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()); }
unsigned int GetHullTraceMask() const { return NAI_Hull::TraceMask(GetHullType()); }
void SetupVPhysicsHull();
virtual void StartTouch( CBaseEntity *pOther );
void CheckPhysicsContacts();
virtual bool ShouldCheckPhysicsContacts( void ) { return ( GetMoveType() == MOVETYPE_STEP && VPhysicsGetObject() ); }
private:
void TryRestoreHull( void );
bool m_fIsUsingSmallHull;
protected:
bool m_bCheckContacts;
private:
// Task implementation helpers
void StartTurn( float flDeltaYaw );
bool FindCoverFromBestSound( Vector *pCoverPos );
void StartScriptMoveToTargetTask( int task );
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 );
virtual void RunDieTask();
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);
virtual void Freeze( float flFreezeAmount = -1.0f, CBaseEntity *pFreezer = NULL, Ray_t *pFreezeRay = NULL );
virtual bool ShouldBecomeStatue();
virtual bool IsMovementFrozen( void ) { return m_flMovementFrozen > m_flFrozenMoveBlock; }
virtual bool IsAttackFrozen( void ) { return m_flAttackFrozen > 0.0f; }
virtual void Unfreeze();
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
float m_flFrozenMoveBlock; // entity can't move after it's frozen past this amount
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(); }
protected:
unsigned int m_nAITraceMask;
public:
inline unsigned int GetAITraceMask( void ) const { return m_nAITraceMask; }
inline unsigned int GetAITraceMask_BrushOnly( void ) const { return (m_nAITraceMask & ~CONTENTS_MONSTER); }
virtual bool OnlySeeAliveEntities( void ) { return true; }
};
//-------------------------------------
inline bool CAI_BaseNPC::IsRunningBehavior() const
{
return ( m_pPrimaryBehavior != NULL );
}
//-------------------------------------
inline CAI_BehaviorBase *CAI_BaseNPC::GetPrimaryBehavior()
{
return m_pPrimaryBehavior;
}
//-----------------------------------------------------------------------------
// 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,
float flDamage, 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 = flDamage;
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<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<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( (char *)g_psz##id );
//-----------------
#define DEFINE_SCHEDULE( id, text ) \
scheduleIds.PushBack( #id, id ); \
char * g_psz##id = \
"\n Schedule" \
"\n " #id \
text \
"\n"; \
schedulesToLoad.AddToTail( (char *)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(), GetSchedulingSymbols() ); \
} \
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(), GetSchedulingSymbols() ); \
} \
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::GetHullTraceMask() const
{
return NAI_Hull::TraceMask(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