//========= Copyright 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" #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_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 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