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.
441 lines
14 KiB
441 lines
14 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Base class for humanoid NPCs intended to fight along side player in close |
|
// environments |
|
// |
|
//=============================================================================// |
|
|
|
#ifndef NPC_PLAYERCOMPANION_H |
|
#define NPC_PLAYERCOMPANION_H |
|
|
|
#include "ai_playerally.h" |
|
|
|
#include "ai_behavior_follow.h" |
|
#include "ai_behavior_standoff.h" |
|
#include "ai_behavior_assault.h" |
|
#include "ai_behavior_lead.h" |
|
#include "ai_behavior_actbusy.h" |
|
#include "ai_behavior_fear.h" |
|
#include "ai_behavior_fightfromcover.h" |
|
|
|
#ifdef HL2_EPISODIC |
|
#include "ai_behavior_operator.h" |
|
#include "ai_behavior_passenger_companion.h" |
|
#endif |
|
|
|
#if defined( _WIN32 ) |
|
#pragma once |
|
#endif |
|
|
|
enum AIReadiness_t |
|
{ |
|
AIRL_PANIC = -2, |
|
AIRL_STEALTH = -1, |
|
AIRL_RELAXED = 0, |
|
AIRL_STIMULATED, |
|
AIRL_AGITATED, |
|
}; |
|
|
|
enum AIReadinessUse_t |
|
{ |
|
AIRU_NEVER, |
|
AIRU_ALWAYS, |
|
AIRU_ONLY_PLAYER_SQUADMATES, |
|
}; |
|
|
|
|
|
class CCompanionActivityRemap : public CActivityRemap |
|
{ |
|
public: |
|
CCompanionActivityRemap( void ) : |
|
m_fUsageBits( 0 ), |
|
m_readiness( AIRL_RELAXED ), |
|
m_bAiming( false ), |
|
m_bWeaponRequired( false ), |
|
m_bInVehicle( false ) {} |
|
|
|
// This bitfield maps which bits of data are being utilized by this data structure, since not all criteria |
|
// in the parsed file are essential. You must add corresponding bits to the definitions below and maintain |
|
// their state in the parsing of the file, as well as check the bitfield before accessing the data. This |
|
// could be encapsulated into this class, but we'll probably move away from this model and closer to something |
|
// more akin to the response rules -- jdw |
|
|
|
int m_fUsageBits; |
|
|
|
AIReadiness_t m_readiness; |
|
bool m_bAiming; |
|
bool m_bWeaponRequired; |
|
bool m_bInVehicle; // For future expansion, this needs to speak more to the exact seat, role, and vehicle |
|
}; |
|
|
|
// Usage bits for remap "extra" parsing - if these bits are set, the associated data has changed |
|
#define bits_REMAP_READINESS (1<<0) |
|
#define bits_REMAP_AIMING (1<<1) |
|
#define bits_REMAP_WEAPON_REQUIRED (1<<2) |
|
#define bits_REMAP_IN_VEHICLE (1<<3) |
|
|
|
// Readiness modes that only change due to mapmaker scripts |
|
#define READINESS_MIN_VALUE -2 |
|
#define READINESS_MODE_PANIC -2 |
|
#define READINESS_MODE_STEALTH -1 |
|
|
|
// Readiness modes that change normally |
|
#define READINESS_VALUE_RELAXED 0.1f |
|
#define READINESS_VALUE_STIMULATED 0.95f |
|
#define READINESS_VALUE_AGITATED 1.0f |
|
|
|
class CPhysicsProp; |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// CLASS: CNPC_PlayerCompanion |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
class CNPC_PlayerCompanion : public CAI_PlayerAlly |
|
{ |
|
DECLARE_CLASS( CNPC_PlayerCompanion, CAI_PlayerAlly ); |
|
|
|
public: |
|
//--------------------------------- |
|
bool CreateBehaviors(); |
|
void Precache(); |
|
void Spawn(); |
|
virtual void SelectModel() {}; |
|
|
|
virtual int Restore( IRestore &restore ); |
|
virtual void DoCustomSpeechAI( void ); |
|
|
|
//--------------------------------- |
|
int ObjectCaps(); |
|
bool ShouldAlwaysThink(); |
|
|
|
Disposition_t IRelationType( CBaseEntity *pTarget ); |
|
|
|
bool IsSilentSquadMember() const; |
|
|
|
//--------------------------------- |
|
// Behavior |
|
//--------------------------------- |
|
void GatherConditions(); |
|
virtual void PredictPlayerPush(); |
|
void BuildScheduleTestBits(); |
|
|
|
CSound *GetBestSound( int validTypes = ALL_SOUNDS ); |
|
bool QueryHearSound( CSound *pSound ); |
|
bool QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC = false ); |
|
bool ShouldIgnoreSound( CSound * ); |
|
|
|
int SelectSchedule(); |
|
|
|
virtual int SelectScheduleDanger(); |
|
virtual int SelectSchedulePriorityAction(); |
|
virtual int SelectScheduleNonCombat() { return SCHED_NONE; } |
|
virtual int SelectScheduleCombat(); |
|
int SelectSchedulePlayerPush(); |
|
|
|
virtual bool CanReload( void ); |
|
|
|
virtual bool ShouldDeferToFollowBehavior(); |
|
bool ShouldDeferToPassengerBehavior( void ); |
|
|
|
bool IsValidReasonableFacing( const Vector &vecSightDir, float sightDist ); |
|
|
|
int TranslateSchedule( int scheduleType ); |
|
|
|
void StartTask( const Task_t *pTask ); |
|
void RunTask( const Task_t *pTask ); |
|
|
|
Activity TranslateActivityReadiness( Activity activity ); |
|
Activity NPC_TranslateActivity( Activity eNewActivity ); |
|
void HandleAnimEvent( animevent_t *pEvent ); |
|
bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt); |
|
|
|
int GetSoundInterests(); |
|
|
|
void Touch( CBaseEntity *pOther ); |
|
|
|
virtual bool IgnorePlayerPushing( void ); |
|
|
|
void ModifyOrAppendCriteria( AI_CriteriaSet& set ); |
|
void Activate( void ); |
|
|
|
void PrepareReadinessRemap( void ); |
|
|
|
virtual bool IsNavigationUrgent( void ); |
|
|
|
//--------------------------------- |
|
// Readiness |
|
//--------------------------------- |
|
|
|
protected: |
|
virtual bool IsReadinessCapable(); |
|
bool IsReadinessLocked() { return gpGlobals->curtime < m_flReadinessLockedUntil; } |
|
void AddReadiness( float flAdd, bool bOverrideLock = false ); |
|
void SubtractReadiness( float flAdd, bool bOverrideLock = false ); |
|
void SetReadinessValue( float flSet ); |
|
void SetReadinessSensitivity( float flSensitivity ) { m_flReadinessSensitivity = flSensitivity; } |
|
virtual void UpdateReadiness(); |
|
virtual float GetReadinessDecay(); |
|
bool IsInScriptedReadinessState( void ) { return (m_flReadiness < 0 ); } |
|
|
|
CUtlVector< CCompanionActivityRemap > m_activityMappings; |
|
|
|
public: |
|
float GetReadinessValue() { return m_flReadiness; } |
|
int GetReadinessLevel(); |
|
void SetReadinessLevel( int iLevel, bool bOverrideLock, bool bSlam ); |
|
void LockReadiness( float duration = -1.0f ); // Defaults to indefinitely locked |
|
void UnlockReadiness( void ); |
|
|
|
virtual void ReadinessLevelChanged( int iPriorLevel ) { } |
|
|
|
void InputGiveWeapon( inputdata_t &inputdata ); |
|
|
|
#ifdef HL2_EPISODIC |
|
//--------------------------------- |
|
// Vehicle passenger |
|
//--------------------------------- |
|
void InputEnterVehicle( inputdata_t &inputdata ); |
|
void InputEnterVehicleImmediately( inputdata_t &inputdata ); |
|
void InputCancelEnterVehicle( inputdata_t &inputdata ); |
|
void InputExitVehicle( inputdata_t &inputdata ); |
|
bool CanEnterVehicle( void ); |
|
bool CanExitVehicle( void ); |
|
void EnterVehicle( CBaseEntity *pEntityVehicle, bool bImmediately ); |
|
virtual bool ExitVehicle( void ); |
|
|
|
virtual void UpdateEfficiency( bool bInPVS ); |
|
virtual bool IsInAVehicle( void ) const; |
|
virtual IServerVehicle *GetVehicle( void ); |
|
virtual CBaseEntity *GetVehicleEntity( void ); |
|
|
|
virtual bool CanRunAScriptedNPCInteraction( bool bForced = false ); |
|
virtual bool IsAllowedToDodge( void ); |
|
|
|
#endif // HL2_EPISODIC |
|
|
|
public: |
|
|
|
virtual void OnPlayerKilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); |
|
|
|
//--------------------------------- |
|
//--------------------------------- |
|
bool PickTacticalLookTarget( AILookTargetArgs_t *pArgs ); |
|
|
|
//--------------------------------- |
|
// Aiming |
|
//--------------------------------- |
|
CBaseEntity *GetAimTarget() { return m_hAimTarget; } |
|
void SetAimTarget( CBaseEntity *pTarget ); |
|
void StopAiming( char *pszReason = NULL ); |
|
bool FindNewAimTarget(); |
|
void OnNewLookTarget(); |
|
bool ShouldBeAiming(); |
|
virtual bool IsAllowedToAim(); |
|
bool HasAimLOS( CBaseEntity *pAimTarget ); |
|
void AimGun(); |
|
CBaseEntity *GetAlternateMoveShootTarget(); |
|
|
|
//--------------------------------- |
|
// Combat |
|
//--------------------------------- |
|
virtual void LocateEnemySound() {}; |
|
|
|
bool IsValidEnemy( CBaseEntity *pEnemy ); |
|
|
|
bool IsSafeFromFloorTurret( const Vector &vecLocation, CBaseEntity *pTurret ); |
|
|
|
bool ShouldMoveAndShoot( void ); |
|
void OnUpdateShotRegulator(); |
|
|
|
void DecalTrace( trace_t *pTrace, char const *decalName ); |
|
bool FCanCheckAttacks(); |
|
Vector GetActualShootPosition( const Vector &shootOrigin ); |
|
WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ); |
|
bool ShouldLookForBetterWeapon(); |
|
bool Weapon_CanUse( CBaseCombatWeapon *pWeapon ); |
|
void Weapon_Equip( CBaseCombatWeapon *pWeapon ); |
|
void PickupWeapon( CBaseCombatWeapon *pWeapon ); |
|
|
|
bool FindCoverPos( CBaseEntity *pEntity, Vector *pResult); |
|
bool FindCoverPosInRadius( CBaseEntity *pEntity, const Vector &goalPos, float coverRadius, Vector *pResult ); |
|
bool FindCoverPos( CSound *pSound, Vector *pResult ); |
|
bool FindMortarCoverPos( CSound *pSound, Vector *pResult ); |
|
bool IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition ); |
|
|
|
bool IsEnemyTurret() { return ( GetEnemy() && IsTurret(GetEnemy()) ); } |
|
|
|
static bool IsMortar( CBaseEntity *pEntity ); |
|
static bool IsSniper( CBaseEntity *pEntity ); |
|
static bool IsTurret( CBaseEntity *pEntity ); |
|
static bool IsGunship( CBaseEntity *pEntity ); |
|
|
|
//--------------------------------- |
|
// Damage handling |
|
//--------------------------------- |
|
int OnTakeDamage_Alive( const CTakeDamageInfo &info ); |
|
void OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker ); |
|
|
|
//--------------------------------- |
|
// Hints |
|
//--------------------------------- |
|
bool FValidateHintType ( CAI_Hint *pHint ); |
|
|
|
//--------------------------------- |
|
// Navigation |
|
//--------------------------------- |
|
bool IsValidMoveAwayDest( const Vector &vecDest ); |
|
bool ValidateNavGoal(); |
|
bool OverrideMove( float flInterval ); // Override to take total control of movement (return true if done so) |
|
bool MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost ); |
|
float GetIdealSpeed() const; |
|
float GetIdealAccel() const; |
|
bool OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); |
|
|
|
//--------------------------------- |
|
// Inputs |
|
//--------------------------------- |
|
void InputOutsideTransition( inputdata_t &inputdata ); |
|
void InputSetReadinessPanic( inputdata_t &inputdata ); |
|
void InputSetReadinessStealth( inputdata_t &inputdata ); |
|
void InputSetReadinessLow( inputdata_t &inputdata ); |
|
void InputSetReadinessMedium( inputdata_t &inputdata ); |
|
void InputSetReadinessHigh( inputdata_t &inputdata ); |
|
void InputLockReadiness( inputdata_t &inputdata ); |
|
#if HL2_EPISODIC |
|
void InputClearAllOuputs( inputdata_t &inputdata ); ///< annihilate every output on this npc |
|
#endif |
|
|
|
bool AllowReadinessValueChange( void ); |
|
|
|
protected: |
|
//----------------------------------------------------- |
|
// Conditions, Schedules, Tasks |
|
//----------------------------------------------------- |
|
enum |
|
{ |
|
COND_PC_HURTBYFIRE = BaseClass::NEXT_CONDITION, |
|
COND_PC_SAFE_FROM_MORTAR, |
|
COND_PC_BECOMING_PASSENGER, |
|
NEXT_CONDITION, |
|
|
|
SCHED_PC_COWER = BaseClass::NEXT_SCHEDULE, |
|
SCHED_PC_MOVE_TOWARDS_COVER_FROM_BEST_SOUND, |
|
SCHED_PC_TAKE_COVER_FROM_BEST_SOUND, |
|
SCHED_PC_FLEE_FROM_BEST_SOUND, |
|
SCHED_PC_FAIL_TAKE_COVER_TURRET, |
|
SCHED_PC_FAKEOUT_MORTAR, |
|
SCHED_PC_GET_OFF_COMPANION, |
|
NEXT_SCHEDULE, |
|
|
|
TASK_PC_WAITOUT_MORTAR = BaseClass::NEXT_TASK, |
|
TASK_PC_GET_PATH_OFF_COMPANION, |
|
NEXT_TASK, |
|
}; |
|
|
|
private: |
|
void SetupCoverSearch( CBaseEntity *pEntity ); |
|
void CleanupCoverSearch(); |
|
|
|
//----------------------------------------------------- |
|
|
|
bool m_bMovingAwayFromPlayer; |
|
bool m_bWeightPathsInCover; |
|
|
|
enum eCoverType |
|
{ |
|
CT_NORMAL, |
|
CT_TURRET, |
|
CT_MORTAR |
|
}; |
|
|
|
static eCoverType gm_fCoverSearchType; |
|
static bool gm_bFindingCoverFromAllEnemies; |
|
|
|
CSimpleSimTimer m_FakeOutMortarTimer; |
|
|
|
// Derived classes should not use the expresser directly |
|
virtual CAI_Expresser *GetExpresser() { return BaseClass::GetExpresser(); } |
|
|
|
protected: |
|
//----------------------------------------------------- |
|
|
|
virtual CAI_FollowBehavior &GetFollowBehavior( void ) { return m_FollowBehavior; } |
|
|
|
CAI_FightFromCoverBehavior m_FightFromCoverBehavior; |
|
CAI_AssaultBehavior m_AssaultBehavior; |
|
CAI_FollowBehavior m_FollowBehavior; |
|
CAI_StandoffBehavior m_StandoffBehavior; |
|
CAI_LeadBehavior m_LeadBehavior; |
|
CAI_ActBusyBehavior m_ActBusyBehavior; |
|
#ifdef HL2_EPISODIC |
|
CAI_OperatorBehavior m_OperatorBehavior; |
|
CAI_PassengerBehaviorCompanion m_PassengerBehavior; |
|
CAI_FearBehavior m_FearBehavior; |
|
#endif |
|
//----------------------------------------------------- |
|
|
|
bool ShouldAlwaysTransition( void ); |
|
|
|
// Readiness is a value that's fed by various events in the NPC's AI. It is used |
|
// to make decisions about what type of posture the NPC should be in (relaxed, agitated). |
|
// It is not used to make decisions about what to do in the AI. |
|
float m_flReadiness; |
|
float m_flReadinessSensitivity; |
|
bool m_bReadinessCapable; |
|
float m_flReadinessLockedUntil; |
|
float m_fLastBarrelExploded; |
|
float m_fLastPlayerKill; |
|
int m_iNumConsecutiveBarrelsExploded; // Companions keep track of the # of consecutive barrels exploded by the player and speaks a response as it increases |
|
int m_iNumConsecutivePlayerKills; // Alyx keeps track of the # of consecutive kills by the player and speaks a response as it increases |
|
|
|
//----------------------------------------------------- |
|
|
|
float m_flBoostSpeed; |
|
|
|
//----------------------------------------------------- |
|
|
|
CSimpleSimTimer m_AnnounceAttackTimer; |
|
|
|
//----------------------------------------------------- |
|
|
|
EHANDLE m_hAimTarget; |
|
|
|
#ifdef HL2_EPISODIC |
|
CHandle<CPhysicsProp> m_hFlare; |
|
#endif // HL2_EPISODIC |
|
|
|
//----------------------------------------------------- |
|
|
|
static string_t gm_iszMortarClassname; |
|
static string_t gm_iszFloorTurretClassname; |
|
static string_t gm_iszGroundTurretClassname; |
|
static string_t gm_iszShotgunClassname; |
|
static string_t gm_iszRollerMineClassname; |
|
|
|
//----------------------------------------------------- |
|
|
|
void InputEnableAlwaysTransition( inputdata_t &inputdata ); |
|
void InputDisableAlwaysTransition( inputdata_t &inputdata ); |
|
bool m_bAlwaysTransition; |
|
bool m_bDontPickupWeapons; |
|
|
|
void InputEnableWeaponPickup( inputdata_t &inputdata ); |
|
void InputDisableWeaponPickup( inputdata_t &inputdata ); |
|
|
|
COutputEvent m_OnWeaponPickup; |
|
|
|
CStopwatch m_SpeechWatch_PlayerLooking; |
|
|
|
DECLARE_DATADESC(); |
|
DEFINE_CUSTOM_AI; |
|
}; |
|
|
|
// Used for quick override move searches against certain types of entities |
|
void OverrideMoveCache_ForceRepopulateList( void ); |
|
CBaseEntity *OverrideMoveCache_FindTargetsInRadius( CBaseEntity *pFirstEntity, const Vector &vecOrigin, float flRadius ); |
|
void OverrideMoveCache_LevelInitPreEntity( void ); |
|
void OverrideMoveCache_LevelShutdownPostEntity( void ); |
|
|
|
#endif // NPC_PLAYERCOMPANION_H
|
|
|