//========= Copyright Valve Corporation, All rights reserved. ============// // headless_hatman.h // An NPC that spawns in the Halloween map and wreaks havok // Michael Booth, October 2010 #ifndef HEADLESS_HATMAN_H #define HEADLESS_HATMAN_H #include "NextBot.h" #include "NextBotBehavior.h" #include "NextBotGroundLocomotion.h" #include "headless_hatman_body.h" #include "Path/NextBotPathFollow.h" #include "halloween_base_boss.h" extern ConVar tf_halloween_bot_health_base; extern ConVar tf_halloween_bot_health_per_player; extern ConVar tf_halloween_bot_min_player_count; extern ConVar tf_halloween_bot_speed; extern ConVar tf_halloween_bot_attack_range; extern ConVar tf_halloween_bot_speed_recovery_rate; extern ConVar tf_halloween_bot_speed_penalty; extern ConVar tf_halloween_bot_chase_duration; extern ConVar tf_halloween_bot_terrify_radius; extern ConVar tf_halloween_bot_chase_range; extern ConVar tf_halloween_bot_quit_range; class CTFPlayer; class CHeadlessHatman; //---------------------------------------------------------------------------- class CHeadlessHatmanLocomotion : public NextBotGroundLocomotion { public: CHeadlessHatmanLocomotion( INextBot *bot ) : NextBotGroundLocomotion( bot ) { } virtual ~CHeadlessHatmanLocomotion() { } virtual float GetRunSpeed( void ) const; // get maximum running speed virtual float GetStepHeight( void ) const; // if delta Z is greater than this, we have to jump to get up virtual float GetMaxJumpHeight( void ) const; // return maximum height of a jump /** * Should we collide with this entity? */ virtual bool ShouldCollideWith( const CBaseEntity *object ) const; private: virtual float GetMaxYawRate( void ) const; // return max rate of yaw rotation }; //---------------------------------------------------------------------------- class CHeadlessHatmanIntention : public IIntention { public: CHeadlessHatmanIntention( CHeadlessHatman *me ); virtual ~CHeadlessHatmanIntention(); virtual void Reset( void ); virtual void Update( void ); virtual QueryResultType IsPositionAllowed( const INextBot *me, const Vector &pos ) const; // is the a place we can be? virtual INextBotEventResponder *FirstContainedResponder( void ) const { return m_behavior; } virtual INextBotEventResponder *NextContainedResponder( INextBotEventResponder *current ) const { return NULL; } private: Behavior< CHeadlessHatman > *m_behavior; }; //---------------------------------------------------------------------------- class CHeadlessHatman : public CHalloweenBaseBoss { public: DECLARE_CLASS( CHeadlessHatman, CHalloweenBaseBoss ); DECLARE_SERVERCLASS(); CHeadlessHatman(); virtual ~CHeadlessHatman(); static void PrecacheHeadlessHatman(); virtual void Precache(); virtual void Spawn( void ); virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info ); // INextBot virtual CHeadlessHatmanIntention *GetIntentionInterface( void ) const { return m_intention; } virtual CHeadlessHatmanLocomotion *GetLocomotionInterface( void ) const { return m_locomotor; } virtual CHeadlessHatmanBody *GetBodyInterface( void ) const { return m_body; } virtual void Update( void ); const Vector &GetHomePosition( void ) const; CBaseAnimating *GetAxe( void ) const; virtual HalloweenBossType GetBossType() const { return HALLOWEEN_BOSS_HHH; } private: const char *GetWeaponModel() const; CHeadlessHatmanIntention *m_intention; CHeadlessHatmanLocomotion *m_locomotor; CHeadlessHatmanBody *m_body; CBaseAnimating *m_axe; CUtlVector< AttackerInfo > m_attackerVector; // list of everyone who injured me, and when CountdownTimer m_painTimer; Vector m_homePos; int m_damagePoseParameter; }; inline CBaseAnimating *CHeadlessHatman::GetAxe( void ) const { return m_axe; } inline const Vector &CHeadlessHatman::GetHomePosition( void ) const { return m_homePos; } //-------------------------------------------------------------------------------------------------------------- class CHeadlessHatmanPathCost : public IPathCost { public: CHeadlessHatmanPathCost( CHeadlessHatman *me ) { m_me = me; } // return the cost (weighted distance between) of moving from "fromArea" to "area", or -1 if the move is not allowed virtual float operator()( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length ) const { if ( fromArea == NULL ) { // first area in path, no cost return 0.0f; } else { if ( !m_me->GetLocomotionInterface()->IsAreaTraversable( area ) ) { // our locomotor says we can't move here return -1.0f; } // compute distance traveled along path so far float dist; if ( ladder ) { dist = ladder->m_length; } else if ( length > 0.0 ) { // optimization to avoid recomputing length dist = length; } else { dist = ( area->GetCenter() - fromArea->GetCenter() ).Length(); } float cost = dist + fromArea->GetCostSoFar(); // check height change float deltaZ = fromArea->ComputeAdjacentConnectionHeightChange( area ); if ( deltaZ >= m_me->GetLocomotionInterface()->GetStepHeight() ) { if ( deltaZ >= m_me->GetLocomotionInterface()->GetMaxJumpHeight() ) { // too high to reach return -1.0f; } // jumping is slower than flat ground const float jumpPenalty = 5.0f; cost += jumpPenalty * dist; } else if ( deltaZ < -m_me->GetLocomotionInterface()->GetDeathDropHeight() ) { // too far to drop return -1.0f; } return cost; } } CHeadlessHatman *m_me; }; #endif // HEADLESS_HATMAN_H