|
|
//========= 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
//=============================================================================
|