//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #ifndef NPC_METROPOLICE_H #define NPC_METROPOLICE_H #ifdef _WIN32 #pragma once #endif #include "rope.h" #include "rope_shared.h" #include "ai_baseactor.h" #include "ai_basenpc.h" #include "ai_goal_police.h" #include "ai_behavior.h" #include "ai_behavior_standoff.h" #include "ai_behavior_assault.h" #include "ai_behavior_functank.h" #include "ai_behavior_actbusy.h" #include "ai_behavior_rappel.h" #include "ai_behavior_police.h" #include "ai_behavior_follow.h" #include "ai_sentence.h" #include "props.h" class CNPC_MetroPolice; class CNPC_MetroPolice : public CAI_BaseActor { DECLARE_CLASS( CNPC_MetroPolice, CAI_BaseActor ); DECLARE_DATADESC(); public: CNPC_MetroPolice() = default; virtual bool CreateComponents(); bool CreateBehaviors(); void Spawn( void ); void Precache( void ); Class_T Classify( void ); Disposition_t IRelationType(CBaseEntity *pTarget); float MaxYawSpeed( void ); void HandleAnimEvent( animevent_t *pEvent ); Activity NPC_TranslateActivity( Activity newActivity ); Vector EyeDirection3D( void ) { return CAI_BaseHumanoid::EyeDirection3D(); } // cops don't have eyes virtual void Event_Killed( const CTakeDamageInfo &info ); virtual void OnScheduleChange(); float GetIdealAccel( void ) const; int ObjectCaps( void ) { return UsableNPCObjectCaps(BaseClass::ObjectCaps()); } void PrecriminalUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); // These are overridden so that the cop can shove and move a non-criminal player safely CBaseEntity *CheckTraceHullAttack( float flDist, const Vector &mins, const Vector &maxs, int iDamage, int iDmgType, float forceScale, bool bDamageAnyNPC ); CBaseEntity *CheckTraceHullAttack( const Vector &vStart, const Vector &vEnd, const Vector &mins, const Vector &maxs, int iDamage, int iDmgType, float flForceScale, bool bDamageAnyNPC ); virtual int SelectSchedule( void ); virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); virtual int TranslateSchedule( int scheduleType ); void StartTask( const Task_t *pTask ); void RunTask( const Task_t *pTask ); virtual Vector GetActualShootTrajectory( const Vector &shootOrigin ); virtual void FireBullets( const FireBulletsInfo_t &info ); virtual bool HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt); virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon ); //virtual bool OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval ); bool OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); bool ShouldBruteForceFailedNav() { return false; } virtual void GatherConditions( void ); virtual bool OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval ); // Can't move and shoot when the enemy is an airboat virtual bool ShouldMoveAndShoot(); // TraceAttack virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ); // Speaking virtual void SpeakSentence( int nSentenceType ); // Set up the shot regulator based on the equipped weapon virtual void OnUpdateShotRegulator( ); bool ShouldKnockOutTarget( CBaseEntity *pTarget ); void KnockOutTarget( CBaseEntity *pTarget ); void StunnedTarget( CBaseEntity *pTarget ); void AdministerJustice( void ); bool QueryHearSound( CSound *pSound ); void SetBatonState( bool state ); bool BatonActive( void ); CAI_Sentence< CNPC_MetroPolice > *GetSentences() { return &m_Sentences; } virtual bool AllowedToIgnite( void ) { return true; } void PlayFlinchGesture( void ); protected: // Determines the best type of flinch anim to play. virtual Activity GetFlinchActivity( bool bHeavyDamage, bool bGesture ); // Only move and shoot when attacking virtual bool OnBeginMoveAndShoot(); virtual void OnEndMoveAndShoot(); private: bool PlayerIsCriminal( void ); void ReleaseManhack( void ); // Speech-related methods void AnnounceTakeCoverFromDanger( CSound *pSound ); void AnnounceEnemyType( CBaseEntity *pEnemy ); void AnnounceEnemyKill( CBaseEntity *pEnemy ); void AnnounceHarrassment( ); void AnnounceOutOfAmmo( ); // Behavior-related sentences void SpeakFuncTankSentence( int nSentenceType ); void SpeakAssaultSentence( int nSentenceType ); void SpeakStandoffSentence( int nSentenceType ); virtual void LostEnemySound( void ); virtual void FoundEnemySound( void ); virtual void AlertSound( void ); virtual void PainSound( const CTakeDamageInfo &info ); virtual void DeathSound( const CTakeDamageInfo &info ); virtual void IdleSound( void ); virtual bool ShouldPlayIdleSound( void ); // Burst mode! void SetBurstMode( bool bEnable ); int OnTakeDamage_Alive( const CTakeDamageInfo &info ); int GetSoundInterests( void ); void BuildScheduleTestBits( void ); bool CanDeployManhack( void ); bool ShouldHitPlayer( const Vector &targetDir, float targetDist ); void PrescheduleThink( void ); void SetPlayerCriminalDuration( float time ); void IncrementPlayerCriminalStatus( void ); virtual bool UseAttackSquadSlots() { return true; } WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon ); // Inputs void InputEnableManhackToss( inputdata_t &inputdata ); void InputSetPoliceGoal( inputdata_t &inputdata ); void InputActivateBaton( inputdata_t &inputdata ); void NotifyDeadFriend ( CBaseEntity* pFriend ); // Stitch aiming! void AimBurstRandomly( int nMinCount, int nMaxCount, float flMinDelay, float flMaxDelay ); void AimBurstAtEnemy( float flReactionTime ); void AimBurstInFrontOfEnemy( float flReactionTime ); void AimBurstAlongSideOfEnemy( float flFollowTime ); void AimBurstBehindEnemy( float flFollowTime ); void AimBurstTightGrouping( float flShotTime ); // Anim event handlers void OnAnimEventDeployManhack( animevent_t *pEvent ); void OnAnimEventShove( void ); void OnAnimEventBatonOn( void ); void OnAnimEventBatonOff( void ); void OnAnimEventStartDeployManhack( void ); void OnAnimEventPreDeployManhack( void ); bool HasBaton( void ); // Normal schedule selection int SelectCombatSchedule(); int SelectScheduleNewEnemy(); int SelectScheduleArrestEnemy(); int SelectRangeAttackSchedule(); int SelectScheduleNoDirectEnemy(); int SelectScheduleInvestigateSound(); int SelectShoveSchedule( void ); bool TryToEnterPistolSlot( int nSquadSlot ); // Airboat schedule selection int SelectAirboatCombatSchedule(); int SelectAirboatRangeAttackSchedule(); // Handle flinching bool IsHeavyDamage( const CTakeDamageInfo &info ); // Is my enemy currently in an airboat? bool IsEnemyInAnAirboat() const; // Returns the airboat CBaseEntity *GetEnemyAirboat() const; // Compute a predicted enemy position n seconds into the future void PredictShootTargetPosition( float flDeltaTime, float flMinLeadDist, float flAddVelocity, Vector *pVecTarget, Vector *pVecTargetVel ); // Compute a predicted velocity n seconds into the future (given a known acceleration rate) void PredictShootTargetVelocity( float flDeltaTime, Vector *pVecTargetVel ); // How many shots will I fire in a particular amount of time? int CountShotsInTime( float flDeltaTime ) const; float GetTimeForShots( int nShotCount ) const; // Visualize stitch void VisualizeStitch( const Vector &vecStart, const Vector &vecEnd ); // Visualize line of death void VisualizeLineOfDeath( ); // Modify the stitch length float ComputeDistanceStitchModifier( float flDistanceToTarget ) const; // Adjusts the burst toward the target as it's being fired. void SteerBurstTowardTarget( ); // Methods to compute shot trajectory based on burst mode Vector ComputeBurstLockOnTrajectory( const Vector &shootOrigin ); Vector ComputeBurstDeliberatelyMissTrajectory( const Vector &shootOrigin ); Vector ComputeBurstTrajectory( const Vector &shootOrigin ); Vector ComputeTightBurstTrajectory( const Vector &shootOrigin ); // Are we currently firing a burst? bool IsCurrentlyFiringBurst() const; // Which entity are we actually trying to shoot at? CBaseEntity *GetShootTarget(); // Different burst steering modes void SteerBurstTowardTargetUseSpeedOnly( const Vector &vecShootAt, const Vector &vecShootAtVelocity, float flPredictTime, int nShotsTillPredict ); void SteerBurstTowardTargetUseVelocity( const Vector &vecShootAt, const Vector &vecShootAtVelocity, int nShotsTillPredict ); void SteerBurstTowardTargetUsePosition( const Vector &vecShootAt, const Vector &vecShootAtVelocity, int nShotsTillPredict ); void SteerBurstTowardPredictedPoint( const Vector &vecShootAt, const Vector &vecShootAtVelocity, int nShotsTillPredict ); void SteerBurstWithinLineOfDeath( ); // Set up the shot regulator int SetupBurstShotRegulator( float flReactionTime ); // Choose a random vector somewhere between the two specified vectors void RandomDirectionBetweenVectors( const Vector &vecStart, const Vector &vecEnd, Vector *pResult ); // Stitch selector float StitchAtWeight( float flDist, float flSpeed, float flDot, float flReactionTime, const Vector &vecTargetToGun ); float StitchAcrossWeight( float flDist, float flSpeed, float flDot, float flReactionTime ); float StitchAlongSideWeight( float flDist, float flSpeed, float flDot ); float StitchBehindWeight( float flDist, float flSpeed, float flDot ); float StitchTightWeight( float flDist, float flSpeed, const Vector &vecTargetToGun, const Vector &vecVelocity ); int SelectStitchSchedule(); // Can me enemy see me? bool CanEnemySeeMe( ); // Combat schedule selection int SelectMoveToLedgeSchedule(); // position to shoot at Vector StitchAimTarget( const Vector &posSrc, bool bNoisy ); // Should we attempt to stitch? bool ShouldAttemptToStitch(); // Deliberately aims as close as possible w/o hitting Vector AimCloseToTargetButMiss( CBaseEntity *pTarget, const Vector &shootOrigin ); // Compute the actual reaction time based on distance + speed modifiers float AimBurstAtReactionTime( float flReactonTime, float flDistToTargetSqr, float flCurrentSpeed ); int AimBurstAtSetupHitCount( float flDistToTargetSqr, float flCurrentSpeed ); // How many squad members are trying to arrest the player? int SquadArrestCount(); // He's resisting arrest! void EnemyResistingArrest(); void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ); // Rappel virtual bool IsWaitingToRappel( void ) { return m_RappelBehavior.IsWaitingToRappel(); } void BeginRappel() { m_RappelBehavior.BeginRappel(); } private: enum { BURST_NOT_ACTIVE = 0, BURST_ACTIVE, BURST_LOCK_ON_AFTER_HIT, BURST_LOCKED_ON, BURST_DELIBERATELY_MISS, BURST_TIGHT_GROUPING, }; enum { BURST_STEER_NONE = 0, BURST_STEER_TOWARD_PREDICTED_POINT, BURST_STEER_WITHIN_LINE_OF_DEATH, BURST_STEER_ADJUST_FOR_SPEED_CHANGES, BURST_STEER_EXACTLY_TOWARD_TARGET, }; enum { COND_METROPOLICE_ON_FIRE = BaseClass::NEXT_CONDITION, COND_METROPOLICE_ENEMY_RESISTING_ARREST, COND_METROPOLICE_PLAYER_TOO_CLOSE, COND_METROPOLICE_CHANGE_BATON_STATE, COND_METROPOLICE_PHYSOBJECT_ASSAULT, }; enum { SCHED_METROPOLICE_WALK = BaseClass::NEXT_SCHEDULE, SCHED_METROPOLICE_WAKE_ANGRY, SCHED_METROPOLICE_HARASS, SCHED_METROPOLICE_CHASE_ENEMY, SCHED_METROPOLICE_ESTABLISH_LINE_OF_FIRE, SCHED_METROPOLICE_DRAW_PISTOL, SCHED_METROPOLICE_DEPLOY_MANHACK, SCHED_METROPOLICE_ADVANCE, SCHED_METROPOLICE_CHARGE, SCHED_METROPOLICE_BURNING_RUN, SCHED_METROPOLICE_BURNING_STAND, SCHED_METROPOLICE_SMG_NORMAL_ATTACK, SCHED_METROPOLICE_SMG_BURST_ATTACK, SCHED_METROPOLICE_AIM_STITCH_AT_AIRBOAT, SCHED_METROPOLICE_AIM_STITCH_IN_FRONT_OF_AIRBOAT, SCHED_METROPOLICE_AIM_STITCH_TIGHTLY, SCHED_METROPOLICE_AIM_STITCH_ALONG_SIDE_OF_AIRBOAT, SCHED_METROPOLICE_AIM_STITCH_BEHIND_AIRBOAT, SCHED_METROPOLICE_ESTABLISH_STITCH_LINE_OF_FIRE, SCHED_METROPOLICE_INVESTIGATE_SOUND, SCHED_METROPOLICE_WARN_AND_ARREST_ENEMY, SCHED_METROPOLICE_ARREST_ENEMY, SCHED_METROPOLICE_ENEMY_RESISTING_ARREST, SCHED_METROPOLICE_WARN_TARGET, SCHED_METROPOLICE_HARASS_TARGET, SCHED_METROPOLICE_SUPPRESS_TARGET, SCHED_METROPOLICE_RETURN_FROM_HARASS, SCHED_METROPOLICE_SHOVE, SCHED_METROPOLICE_ACTIVATE_BATON, SCHED_METROPOLICE_DEACTIVATE_BATON, SCHED_METROPOLICE_ALERT_FACE_BESTSOUND, SCHED_METROPOLICE_RETURN_TO_PRECHASE, SCHED_METROPOLICE_SMASH_PROP, }; enum { TASK_METROPOLICE_HARASS = BaseClass::NEXT_TASK, TASK_METROPOLICE_DIE_INSTANTLY, TASK_METROPOLICE_BURST_ATTACK, TASK_METROPOLICE_STOP_FIRE_BURST, TASK_METROPOLICE_AIM_STITCH_AT_PLAYER, TASK_METROPOLICE_AIM_STITCH_AT_AIRBOAT, TASK_METROPOLICE_AIM_STITCH_TIGHTLY, TASK_METROPOLICE_AIM_STITCH_IN_FRONT_OF_AIRBOAT, TASK_METROPOLICE_AIM_STITCH_ALONG_SIDE_OF_AIRBOAT, TASK_METROPOLICE_AIM_STITCH_BEHIND_AIRBOAT, TASK_METROPOLICE_RELOAD_FOR_BURST, TASK_METROPOLICE_GET_PATH_TO_STITCH, TASK_METROPOLICE_RESET_LEDGE_CHECK_TIME, TASK_METROPOLICE_GET_PATH_TO_BESTSOUND_LOS, TASK_METROPOLICE_AIM_WEAPON_AT_ENEMY, TASK_METROPOLICE_ARREST_ENEMY, TASK_METROPOLICE_LEAD_ARREST_ENEMY, TASK_METROPOLICE_SIGNAL_FIRING_TIME, TASK_METROPOLICE_ACTIVATE_BATON, TASK_METROPOLICE_WAIT_FOR_SENTENCE, TASK_METROPOLICE_GET_PATH_TO_PRECHASE, TASK_METROPOLICE_CLEAR_PRECHASE, }; private: int m_iPistolClips; // How many clips the cop has in reserve int m_iManhacks; // How many manhacks the cop has bool m_fWeaponDrawn; // Is my weapon drawn? (ready to use) bool m_bSimpleCops; // The easy version of the cops int m_LastShootSlot; CRandSimTimer m_TimeYieldShootSlot; CSimpleSimTimer m_BatonSwingTimer; CSimpleSimTimer m_NextChargeTimer; // All related to burst firing Vector m_vecBurstTargetPos; Vector m_vecBurstDelta; int m_nBurstHits; int m_nMaxBurstHits; int m_nBurstReloadCount; Vector m_vecBurstLineOfDeathDelta; Vector m_vecBurstLineOfDeathOrigin; int m_nBurstMode; int m_nBurstSteerMode; float m_flBurstSteerDistance; float m_flBurstPredictTime; Vector m_vecBurstPredictedVelocityDir; float m_vecBurstPredictedSpeed; float m_flValidStitchTime; float m_flNextLedgeCheckTime; float m_flTaskCompletionTime; bool m_bShouldActivateBaton; float m_flBatonDebounceTime; // Minimum amount of time before turning the baton off float m_flLastPhysicsFlinchTime; float m_flLastDamageFlinchTime; // Sentences float m_flNextPainSoundTime; float m_flNextLostSoundTime; int m_nIdleChatterType; bool m_bPlayerIsNear; // Policing state bool m_bPlayerTooClose; bool m_bKeepFacingPlayer; float m_flChasePlayerTime; Vector m_vecPreChaseOrigin; float m_flPreChaseYaw; int m_nNumWarnings; int m_iNumPlayerHits; // Outputs COutputEvent m_OnStunnedPlayer; COutputEvent m_OnCupCopped; AIHANDLE m_hManhack; CHandle m_hBlockingProp; CAI_ActBusyBehavior m_ActBusyBehavior; CAI_StandoffBehavior m_StandoffBehavior; CAI_AssaultBehavior m_AssaultBehavior; CAI_FuncTankBehavior m_FuncTankBehavior; CAI_RappelBehavior m_RappelBehavior; CAI_PolicingBehavior m_PolicingBehavior; CAI_FollowBehavior m_FollowBehavior; CAI_Sentence< CNPC_MetroPolice > m_Sentences; int m_nRecentDamage; float m_flRecentDamageTime; // The last hit direction, measured as a yaw relative to our orientation float m_flLastHitYaw; static float gm_flTimeLastSpokePeek; public: DEFINE_CUSTOM_AI; }; #endif // NPC_METROPOLICE_H