source-engine/game/server/hl2/npc_strider.h

646 lines
21 KiB
C
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef NPC_STRIDER_H
#define NPC_STRIDER_H
#include "ai_blended_movement.h"
#include "ai_pathfinder.h"
#include "ai_navigator.h"
#include "ai_utils.h"
#include "smoke_trail.h"
#include "physics_bone_follower.h"
#include "physics_prop_ragdoll.h"
#if defined( _WIN32 )
#pragma once
#endif
#include "tier0/memdbgon.h"
class CNPC_Strider;
class CNPC_Bullseye;
class CStriderMinigun;
//-----------------------------------------------------------------------------
//
// Support for moving Strider air nodes to the correct Z for the Strider
// regardless of Hammer placement
//
//-----------------------------------------------------------------------------
class CAI_Network;
class CAI_Node;
struct StriderMinigunViewcone_t;
struct AI_EnemyInfo_t;
void AdjustStriderNodePosition( CAI_Network *pNetwork, CAI_Node *pNode );
//-----------------------------------------------------------------------------
//
// Strider Minigun
//
//-----------------------------------------------------------------------------
abstract_class IMinigunHost
{
public:
virtual void ShootMinigun( const Vector *pTarget, float aimError, const Vector &vecSpread = vec3_origin ) = 0;
virtual void UpdateMinigunControls( float &yaw, float &pitch ) = 0;
virtual void GetViewCone( StriderMinigunViewcone_t &cone ) = 0;
virtual void NewTarget() = 0;
virtual void OnMinigunStartShooting( CBaseEntity *pTarget ) = 0;
virtual void OnMinigunStopShooting( CBaseEntity *pTarget ) = 0;
virtual CAI_BaseNPC *GetEntity() = 0;
};
abstract_class IStriderMinigunHost : public IMinigunHost
{
public:
virtual float GetMinigunRateOfFire() = 0;
virtual float GetMinigunOnTargetTime() = 0;
virtual float GetMinigunShootDuration() = 0;
virtual float GetMinigunShootDowntime() = 0;
virtual float GetMinigunShootVariation() = 0;
};
//-----------------------------------------------------------------------------
//
// npc_strider
//
//-----------------------------------------------------------------------------
const int NUM_STRIDER_IK_TARGETS = 6;
//---------------------------------------------------------
class CNPC_Strider : public CAI_BlendingHost<CAI_BaseNPC>,
public IStriderMinigunHost
{
DECLARE_CLASS( CNPC_Strider, CAI_BaseNPC );
DECLARE_SERVERCLASS();
public:
CNPC_Strider();
~CNPC_Strider();
//---------------------------------
void Precache();
void Spawn();
bool CreateVPhysics();
void InitBoneFollowers( void );
void PostNPCInit();
void Activate();
void UpdateOnRemove();
void InitBoneControllers();
void OnRestore();
Class_T Classify();
bool ShouldAttractAutoAim( CBaseEntity *pAimingEnt );
virtual float GetAutoAimRadius() { return 80.0f; }
int DrawDebugTextOverlays();
void UpdateEfficiency( bool bInPVS ) { SetEfficiency( ( GetSleepState() != AISS_AWAKE ) ? AIE_DORMANT : AIE_NORMAL ); SetMoveEfficiency( AIME_NORMAL ); }
virtual bool ShouldProbeCollideAgainstEntity( CBaseEntity *pEntity );
//---------------------------------
virtual Vector GetNodeViewOffset() { return BaseClass::GetDefaultEyeOffset(); }
Vector EyePosition();
const Vector & GetViewOffset();
Vector EyePositionCrouched() { return GetAbsOrigin() - Vector( 0, 0, 330 ); }
//---------------------------------
// CBaseAnimating
void CalculateIKLocks( float currentTime );
float GetIdealAccel() const { return GetIdealSpeed(); }
//---------------------------------
// Behavior
//---------------------------------
void NPCThink();
void PrescheduleThink();
void GatherConditions();
void CheckFlinches() {} // Strider handles on own
void GatherHeightConditions( const Vector &vTestPos, CBaseEntity *pEntity );
void OnStateChange( NPC_STATE oldState, NPC_STATE newState );
void BuildScheduleTestBits();
int SelectSchedule();
int TranslateSchedule( int scheduleType );
void StartTask( const Task_t *pTask );
void RunTask( const Task_t *pTask );
bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter* sourceEnt );
void HandleAnimEvent( animevent_t *pEvent );
Disposition_t IRelationType( CBaseEntity *pTarget );
void AddEntityRelationship( CBaseEntity *pEntity, Disposition_t nDisposition, int nPriority );
bool ScheduledMoveToGoalEntity( int scheduleType, CBaseEntity *pGoalEntity, Activity movementActivity );
bool ScheduledFollowPath( int scheduleType, CBaseEntity *pPathStart, Activity movementActivity );
//---------------------------------
// Inputs
//---------------------------------
void InputSetMinigunTime( inputdata_t &inputdata );
void InputSetMinigunTarget( inputdata_t &inputdata );
void InputDisableMinigun( inputdata_t &inputdata );
void InputEnableMinigun( inputdata_t &inputdata );
void InputSetCannonTarget( inputdata_t &inputdata );
void InputFlickRagdoll( inputdata_t &inputdata );
void InputDisableCollisionWith( inputdata_t &inputdata );
void InputEnableCollisionWith( inputdata_t &inputdata );
void InputCrouch( inputdata_t &inputdata );
void InputCrouchInstantly( inputdata_t &inputdata );
void InputStand( inputdata_t &inputdata );
void InputSetHeight( inputdata_t &inputdata );
void InputSetTargetPath( inputdata_t &inputdata );
void InputClearTargetPath( inputdata_t &inputdata );
void InputDisableCrouchWalk( inputdata_t &inputdata );
void InputEnableCrouchWalk( inputdata_t &inputdata );
void InputEnableAggressiveBehavior( inputdata_t &inputdata );
void InputDisableAggressiveBehavior( inputdata_t &inputdata );
void InputStopShootingMinigunForSeconds( inputdata_t &inputdata );
void InputDisableCrouch( inputdata_t &inputdata );
void InputDisableMoveToLOS( inputdata_t &inputdata );
void InputExplode( inputdata_t &inputdata );
void InputScaleGroundSpeed( inputdata_t &inputdata );
//---------------------------------
// Combat
//---------------------------------
bool HasPass() { return m_PlayerFreePass.HasPass(); }
bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
Vector BodyTarget( const Vector &posSrc, bool bNoisy );
bool IsValidEnemy( CBaseEntity *pTarget );
bool UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position, CBaseEntity *pInformer = NULL );
float StriderEnemyDistance( CBaseEntity *pEnemy );
bool FCanCheckAttacks();
int RangeAttack2Conditions( float flDot, float flDist );
int MeleeAttack1Conditions( float flDot, float flDist );
int MeleeAttack2Conditions( float flDot, float flDist );
bool WeaponLOSCondition(const Vector &ownerPos, const Vector &targetPos, bool bSetConditions);
bool CurrentWeaponLOSCondition(const Vector &targetPos, bool bSetConditions);
bool IsValidShootPosition ( const Vector &vecCoverLocation, CAI_Node *pNode, CAI_Hint const *pHint );
bool TestShootPosition(const Vector &vecShootPos, const Vector &targetPos );
Vector Weapon_ShootPosition();
void MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType );
void DoImpactEffect( trace_t &tr, int nDamageType );
void DoMuzzleFlash( void );
bool CanShootThrough( const trace_t &tr, const Vector &vecTarget );
void CreateFocus();
CNPC_Bullseye * GetFocus();
bool GetWeaponLosZ( const Vector &vOrigin, float minZ, float maxZ, float increment, CBaseEntity *pTarget, float *pResult );
//---------------------------------
// Sounds & speech
//---------------------------------
void AlertSound();
void PainSound( const CTakeDamageInfo &info );
void DeathSound( const CTakeDamageInfo &info );
void HuntSound();
//---------------------------------
// Damage handling
//---------------------------------
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
int TakeDamageFromCombineBall( const CTakeDamageInfo &info );
void Event_Killed( const CTakeDamageInfo &info );
void RagdollDeathEffect( CRagdollProp *pRagdoll, float flDuration );
bool BecomeRagdoll( const CTakeDamageInfo &info, const Vector &forceVector );
void StartSmoking();
void StopSmoking( float flDelay = 0.1 );
bool IsSmoking() { return m_hSmoke != NULL; }
void Explode();
//---------------------------------
// Posture
//---------------------------------
float GetMaxHeightModel() const { return 500.0; }
float GetMaxHeight() const { return 490.0; }
float GetMinHeight() const { return 200.0; }
float GetHeightRange() const { return GetMaxHeight() - GetMinHeight(); }
void SetHeight( float h );
float GetHeight() { return GetPoseParameter( gm_BodyHeightPoseParam ); }
void SetIdealHeight( float h );
void SetAbsIdealHeight( float z );
float GetIdealHeight() { return m_idealHeight; }
Vector GetAdjustedOrigin() { Vector result = GetAbsOrigin(); result.z -= GetMaxHeightModel() - GetHeight(); return result; }
bool IsInCrouchedPosture() { return GetIdealHeight() < GetMaxHeight() * .5; }
bool IsInStandingPosture() { return !IsInCrouchedPosture(); }
bool IsStriderCrouching();
bool IsStriderStanding();
void SetupGlobalModelData();
virtual bool CanBecomeServerRagdoll( void ) { return false; }
//---------------------------------
// Navigation & Movement
//---------------------------------
class CNavigator : public CAI_ComponentWithOuter<CNPC_Strider, CAI_Navigator>
{
typedef CAI_ComponentWithOuter<CNPC_Strider, CAI_Navigator> BaseClass;
public:
CNavigator( CNPC_Strider *pOuter )
: BaseClass( pOuter )
{
}
void MoveCalcBaseGoal( AILocalMoveGoal_t *pMoveGoal );
bool MoveUpdateWaypoint( AIMoveResult_t *pResult );
bool DoFindPathToPos();
bool ShouldOptimizeInitialPathSegment( AI_Waypoint_t *pFirstWaypoint );
bool GetStoppingPath( CAI_WaypointList *pClippedWaypoints );
};
class CPathfinder : public CAI_Pathfinder
{
typedef CAI_Pathfinder BaseClass;
public:
CPathfinder( CNPC_Strider *pOuter ) : BaseClass( pOuter ) {}
virtual bool CanUseLocalNavigation() { return false; }
};
friend class CNavigator;
friend void AdjustStriderNodePosition( CAI_Network *pNetwork, CAI_Node *pNode );
bool OverrideMove( float flInterval );
void MaintainTurnActivity( void );
bool IsUnusableNode(int iNodeID, CAI_Hint *pHint); // Override for special NPC behavior
void TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition );
bool HasPendingTargetPath();
void SetTargetPath();
float GetDefaultNavGoalTolerance();
void OnMovementComplete();
float GetSequenceGroundSpeed( CStudioHdr *pStudioHdr, int iSequence );
float MaxYawSpeed();
CAI_Navigator * CreateNavigator() { return new CNavigator( this ); }
CAI_Pathfinder *CreatePathfinder() { return new CPathfinder( this ); }
//---------------------------------
// Minigun
//---------------------------------
void ShootMinigun( const Vector *pTarget, float aimError, const Vector &vecSpread = vec3_origin );
void UpdateMinigunControls( float &yaw, float &pitch );
void GetViewCone( StriderMinigunViewcone_t &cone );
void NewTarget() { m_flTargetAcquiredTime = gpGlobals->curtime; }
void OnMinigunStartShooting( CBaseEntity *pTarget ) {};
void OnMinigunStopShooting( CBaseEntity *pTarget );
float GetMinigunRateOfFire();
float GetMinigunOnTargetTime();
float GetMinigunShootDuration();
float GetMinigunShootDowntime();
float GetMinigunShootVariation();
CAI_BaseNPC * GetEntity() { return this; }
bool IsUsingAggressiveBehavior() { return m_bUseAggressiveBehavior; }
//---------------------------------
// Cannon
//---------------------------------
Vector CannonPosition();
CBaseEntity * GetCannonTarget();
bool HasCannonTarget() const;
bool IsCannonTarget( CBaseEntity *pTarget ) const;
bool AimCannonAt( CBaseEntity *pEntity, float flInterval );
void FireCannon();
void CannonHitThink();
//---------------------------------
// Collision handling
//---------------------------------
void VPhysicsShadowCollision( int index, gamevcollisionevent_t *pEvent );
bool TestCollision( const Ray_t &ray, unsigned int mask, trace_t& trace );
// Conservative collision volumes
static float gm_strideLength;
#ifdef HL2_EPISODIC
void StriderBusterAttached( CBaseEntity *pAttached );
void StriderBusterDetached( CBaseEntity *pAttached );
#endif // HL2_EPISODIC
public:
//---------------------------------
// Misc
//---------------------------------
bool CarriedByDropship();
void CarriedThink();
//---------------------------------
// Foot handling
//---------------------------------
Vector LeftFootHit( float eventtime );
Vector RightFootHit( float eventtime );
Vector BackFootHit( float eventtime );
void StompHit( int followerBoneIndex );
void FootFX( const Vector &origin );
Vector CalculateStompHitPosition( CBaseEntity *pEnemy );
bool IsLegBoneFollower( CBoneFollower *pFollower );
CBoneFollower *GetBoneFollowerByIndex( int nIndex );
int GetBoneFollowerIndex( CBoneFollower *pFollower );
protected:
// Because the strider is a leaf class, we can use
// static variables to store this information, and save some memory.
// Should the strider end up having inheritors, their activate may
// stomp these numbers, in which case you should make these ordinary members
// again.
//
// The strider also caches some pose parameters in SetupGlobalModelData().
static int m_poseMiniGunYaw, m_poseMiniGunPitch;
static bool m_sbStaticPoseParamsLoaded;
virtual void PopulatePoseParameters( void );
private:
bool ShouldExplodeFromDamage( const CTakeDamageInfo &info );
bool m_bExploding;
//-----------------------------------------------------
// Conditions, Schedules, Tasks
//-----------------------------------------------------
enum
{
SCHED_STRIDER_RANGE_ATTACK1 = BaseClass::NEXT_SCHEDULE,
SCHED_STRIDER_RANGE_ATTACK2, // Immolator
SCHED_STRIDER_CROUCH,
SCHED_STRIDER_STAND,
SCHED_STRIDER_DODGE,
SCHED_STRIDER_STOMPL,
SCHED_STRIDER_STOMPR,
SCHED_STRIDER_FLICKL,
SCHED_STRIDER_FLICKR,
SCHED_STRIDER_HUNT,
SCHED_STRIDER_DIE,
SCHED_STRIDER_ATTACK_CANNON_TARGET,
SCHED_STRIDER_CHASE_ENEMY,
SCHED_STRIDER_COMBAT_FACE,
SCHED_STRIDER_AGGRESSIVE_COMBAT_STAND,
SCHED_STRIDER_ESTABLISH_LINE_OF_FIRE_CANNON,
SCHED_STRIDER_FALL_TO_GROUND,
TASK_STRIDER_AIM = BaseClass::NEXT_TASK,
TASK_STRIDER_DODGE,
TASK_STRIDER_STOMP,
TASK_STRIDER_BREAKDOWN,
TASK_STRIDER_START_MOVING,
TASK_STRIDER_REFRESH_HUNT_PATH,
TASK_STRIDER_GET_PATH_TO_CANNON_TARGET,
TASK_STRIDER_FACE_CANNON_TARGET,
TASK_STRIDER_SET_HEIGHT,
TASK_STRIDER_GET_PATH_TO_CANNON_LOS,
TASK_STRIDER_SET_CANNON_HEIGHT,
TASK_STRIDER_FIRE_CANNON,
TASK_STRIDER_FALL_TO_GROUND,
COND_STRIDER_DO_FLICK = BaseClass::NEXT_CONDITION,
COND_TRACK_PATH_GO,
COND_STRIDER_SHOULD_CROUCH,
COND_STRIDER_SHOULD_STAND,
COND_STRIDER_MINIGUN_SHOOTING,
COND_STRIDER_MINIGUN_NOT_SHOOTING,
COND_STRIDER_HAS_CANNON_TARGET,
COND_STRIDER_ENEMY_UPDATED,
COND_STRIDER_HAS_LOS_Z,
};
string_t m_iszStriderBusterName;
string_t m_iszMagnadeClassname;
string_t m_iszHunterClassname;
CStriderMinigun *m_pMinigun;
int m_miniGunAmmo;
int m_miniGunDirectAmmo;
float m_nextShootTime;
float m_nextStompTime;
float m_ragdollTime;
float m_miniGunShootDuration;
float m_aimYaw;
float m_aimPitch;
Vector m_blastHit;
Vector m_blastNormal;
CNetworkVector( m_vecHitPos );
CNetworkArray( Vector, m_vecIKTarget, NUM_STRIDER_IK_TARGETS );
CRandSimTimer m_PostureAnimationTimer;
EHANDLE m_hRagdoll;
EHANDLE m_hCannonTarget;
CSimpleSimTimer m_AttemptCannonLOSTimer;
float m_flSpeedScale;
float m_flTargetSpeedScale;
CSimpleSimTimer m_LowZCorrectionTimer;
// Contained Bone Follower manager
CBoneFollowerManager m_BoneFollowerManager;
int m_BodyTargetBone;
bool m_bDisableBoneFollowers;
int m_iVisibleEnemies;
float m_flTargetAcquiredTime;
bool m_bCrouchLocked; // Designer made the strider crouch. Don't let the AI stand him up.
bool m_bNoCrouchWalk;
bool m_bDontCrouch;
bool m_bNoMoveToLOS;
bool m_bFastCrouch;
bool m_bMinigunEnabled; // If false, minigun disabled by level designer until further notice.
float m_idealHeight;
float m_HeightVelocity;
// FIXME: move to a base class to handle turning for blended movement derived characters
float m_prevYaw;
float m_doTurn;
float m_doLeft;
float m_doRight;
float m_flNextTurnAct;
string_t m_strTrackName;
EHANDLE m_hFocus;
float m_flTimeLastAlertSound;
float m_flTimeNextHuntSound;
bool m_bUseAggressiveBehavior;
float m_flTimePlayerMissileDetected;
EHANDLE m_hPlayersMissile;
bool m_bMinigunUseDirectFire;
CHandle<SmokeTrail> m_hSmoke;
CSimpleSimTimer m_EnemyUpdatedTimer;
CAI_FreePass m_PlayerFreePass;
#ifdef HL2_EPISODIC
CUtlVector< EHANDLE > m_hAttachedBusters; // List of busters attached to us
#endif // HL2_EPISODIC
static float gm_zCannonDist;
static float gm_zMinigunDist;
static Vector gm_vLocalRelativePositionCannon;
static Vector gm_vLocalRelativePositionMinigun;
static int gm_YawControl;
static int gm_PitchControl;
static int gm_CannonAttachment;
static int gm_BodyHeightPoseParam;
DEFINE_CUSTOM_AI;
DECLARE_DATADESC();
};
//-----------------------------------------------------------------------------
//---------------------------------------------------------
enum StriderMinigunPeg_t
{
MINIGUN_PEGGED_DONT_CARE = 0,
MINIGUN_PEGGED_UP,
MINIGUN_PEGGED_DOWN,
MINIGUN_PEGGED_LEFT,
MINIGUN_PEGGED_RIGHT,
};
//---------------------------------------------------------
struct StriderMinigunViewcone_t
{
Vector origin;
Vector axis;
float cosAngle;
float length;
};
//---------------------------------------------------------
struct StriderMinigunAnimController_t
{
float current;
float target;
float rate;
void Update( float dt, bool approach = true )
{
if( approach )
{
current = Approach( target, current, rate * dt );
}
else
{
current = target;
}
}
void Random( float minTarget, float maxTarget, float minRate, float maxRate )
{
target = random->RandomFloat( minTarget, maxTarget );
rate = random->RandomFloat( minRate, maxRate );
}
};
//---------------------------------------------------------
class CStriderMinigun
{
public:
DECLARE_DATADESC();
void Init();
void SetTarget( IStriderMinigunHost *pHost, CBaseEntity *pTarget, bool bOverrideEnemy = false );
CBaseEntity *GetTarget() { return m_hTarget.Get(); }
void Think( IStriderMinigunHost *pHost, float dt );
void SetState( int newState );
bool ShouldFindTarget( IMinigunHost *pHost );
void AimAtPoint( IStriderMinigunHost *pHost, const Vector &vecPoint, bool bSnap = false );
void AimAtTarget( IStriderMinigunHost *pHost, CBaseEntity *pTarget, bool bSnap = false );
void ShootAtTarget( IStriderMinigunHost *pHost, CBaseEntity *pTarget, float shootTime );
void StartShooting( IStriderMinigunHost *pHost, CBaseEntity *pTarget, float duration );
void ExtendShooting( float timeExtend );
void SetShootDuration( float duration );
void StopShootingForSeconds( IStriderMinigunHost *pHost, CBaseEntity *pTarget, float duration );
bool IsPegged( int dir = MINIGUN_PEGGED_DONT_CARE );
bool CanStartShooting( IStriderMinigunHost *pHost, CBaseEntity *pTargetEnt );
float GetBurstTimeRemaining() { return m_burstTime - gpGlobals->curtime; }
void RecordShotOnTarget() { m_iOnTargetShots++; }
void ClearOnTarget() { m_iOnTargetShots = 0; }
bool IsOnTarget( int numShots = 0 ) { return ( numShots == 0 ) ? (m_iOnTargetShots > 0) : (m_iOnTargetShots >= numShots); }
void Enable( IMinigunHost *pHost, bool enable );
float GetAimError();
enum minigunstates_t
{
MINIGUN_OFF = 0,
MINIGUN_SHOOTING = 1,
};
int GetState() { return m_minigunState; }
bool IsShooting() { return GetState() == MINIGUN_SHOOTING; }
private:
bool m_enable;
int m_minigunState;
float m_nextBulletTime; // Minigun is shooting, when can I fire my next bullet?
float m_burstTime; // If firing, how long till done? If not, how long till I can?
float m_nextTwitchTime;
int m_randomState;
EHANDLE m_hTarget;
StriderMinigunAnimController_t m_yaw;
StriderMinigunAnimController_t m_pitch;
bool m_bWarnedAI;
float m_shootDuration;
Vector m_vecAnchor; // A burst starts here and goes to the target's orgin.
bool m_bOverrideEnemy; // The minigun wants something other than the Strider's enemy as a target right now.
Vector m_vecLastTargetPos; // Last place minigun saw the target.
int m_iOnTargetShots;
};
class CSparkTrail : public CPointEntity
{
DECLARE_CLASS( CSparkTrail, CPointEntity );
void Spawn( void );
void SparkThink( void );
virtual void Precache();
DECLARE_DATADESC();
};
#include "tier0/memdbgoff.h"
#endif // NPC_STRIDER_H
//=============================================================================