//========= Copyright Valve Corporation, All rights reserved. ============// // // // //============================================================================= #ifndef ZOMBIE_H #define ZOMBIE_H #include "NextBot.h" #include "NextBotBehavior.h" #include "NextBotGroundLocomotion.h" #include "../headless_hatman_body.h" #include "Path/NextBotPathFollow.h" class CZombie; //---------------------------------------------------------------------------- class CZombieLocomotion : public NextBotGroundLocomotion { public: CZombieLocomotion( INextBot *bot ) : NextBotGroundLocomotion( bot ) { } virtual ~CZombieLocomotion() { } 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 virtual bool ShouldCollideWith( const CBaseEntity *object ) const; private: virtual float GetMaxYawRate( void ) const; // return max rate of yaw rotation }; //---------------------------------------------------------------------------- class CZombieIntention : public IIntention { public: CZombieIntention( CZombie *me ); virtual ~CZombieIntention(); 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< CZombie > *m_behavior; }; //---------------------------------------------------------------------------- DECLARE_AUTO_LIST( IZombieAutoList ); class CZombie : public NextBotCombatCharacter, public IZombieAutoList { public: DECLARE_CLASS( CZombie, NextBotCombatCharacter ); DECLARE_SERVERCLASS(); CZombie(); virtual ~CZombie(); static void PrecacheZombie(); virtual void Precache(); virtual void Spawn( void ); virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info ); virtual void Event_Killed( const CTakeDamageInfo &info ); virtual void UpdateOnRemove(); // INextBot virtual CZombieIntention *GetIntentionInterface( void ) const { return m_intention; } virtual CZombieLocomotion *GetLocomotionInterface( void ) const { return m_locomotor; } virtual CHeadlessHatmanBody *GetBodyInterface( void ) const { return m_body; } CBaseAnimating *m_zombieParts; void StartLifeTimer( float flLifeTime ) { m_lifeTimer.Start( flLifeTime ); } bool ShouldSuicide() const; void ForceSuicide() { m_bForceSuicide = true; } enum SkeletonType_t { SKELETON_NORMAL = 0, SKELETON_KING, SKELETON_MINI }; SkeletonType_t GetSkeletonType() const { return m_nType; } void SetSkeletonType( SkeletonType_t nType ); void AddHat( const char *pszModel ); static CZombie* SpawnAtPos( const Vector& vSpawnPos, float flLifeTime = 0.f, int nTeam = TF_TEAM_HALLOWEEN, CBaseEntity *pOwner = NULL, SkeletonType_t nSkeletonType = SKELETON_NORMAL ); float GetAttackRange() const { return m_flAttackRange; } float GetAttackDamage() const { return m_flAttackDamage; } private: CZombieIntention *m_intention; CZombieLocomotion *m_locomotor; CHeadlessHatmanBody *m_body; SkeletonType_t m_nType; CNetworkVar( float, m_flHeadScale ); CHandle< CBaseAnimating > m_hHat; float m_flAttackRange; float m_flAttackDamage; bool m_bSpy; bool m_bForceSuicide; CountdownTimer m_lifeTimer; }; //-------------------------------------------------------------------------------------------------------------- class CZombiePathCost : public IPathCost { public: CZombiePathCost( CZombie *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(); } // 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; dist += jumpPenalty * dist; } else if ( deltaZ < -m_me->GetLocomotionInterface()->GetDeathDropHeight() ) { // too far to drop return -1.0f; } // this term causes the same bot to choose different routes over time, // but keep the same route for a period in case of repaths int timeMod = (int)( gpGlobals->curtime / 10.0f ) + 1; float preference = 1.0f + 50.0f * ( 1.0f + FastCos( (float)( m_me->GetEntity()->entindex() * area->GetID() * timeMod ) ) ); float cost = dist * preference; return cost + fromArea->GetCostSoFar();; } } CZombie *m_me; }; #endif // ZOMBIE_H