This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

670 lines
22 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef NPC_STRIDER_H
#define NPC_STRIDER_H
#include "ai_blended_movement.h"
#include "ai_navigator.h"
#include "ai_pathfinder.h"
#include "ai_utils.h"
#include "physics_bone_follower.h"
#include "physics_prop_ragdoll.h"
#include "smoke_trail.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
//=============================================================================