//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #ifndef NPC_BASEZOMBIE_H #define NPC_BASEZOMBIE_H #ifdef _WIN32 #pragma once #endif #include "ai_basenpc.h" #include "ai_blended_movement.h" #include "soundenvelope.h" #include "ai_behavior_actbusy.h" #define ZOM_ATTN_FOOTSTEP ATTN_IDLE #define ENVELOPE_CONTROLLER (CSoundEnvelopeController::GetController()) #define ZOMBIE_MELEE_REACH 55 extern int AE_ZOMBIE_ATTACK_RIGHT; extern int AE_ZOMBIE_ATTACK_LEFT; extern int AE_ZOMBIE_ATTACK_BOTH; extern int AE_ZOMBIE_SWATITEM; extern int AE_ZOMBIE_STARTSWAT; extern int AE_ZOMBIE_STEP_LEFT; extern int AE_ZOMBIE_STEP_RIGHT; extern int AE_ZOMBIE_SCUFF_LEFT; extern int AE_ZOMBIE_SCUFF_RIGHT; extern int AE_ZOMBIE_ATTACK_SCREAM; extern int AE_ZOMBIE_GET_UP; extern int AE_ZOMBIE_POUND; #define ZOMBIE_BODYGROUP_HEADCRAB 1 // The crab on our head // Pass these to claw attack so we know where to draw the blood. #define ZOMBIE_BLOOD_LEFT_HAND 0 #define ZOMBIE_BLOOD_RIGHT_HAND 1 #define ZOMBIE_BLOOD_BOTH_HANDS 2 #define ZOMBIE_BLOOD_BITE 3 enum HeadcrabRelease_t { RELEASE_NO, RELEASE_IMMEDIATE, // release the headcrab right now! RELEASE_SCHEDULED, // release the headcrab through the AI schedule. RELEASE_VAPORIZE, // just destroy the crab. RELEASE_RAGDOLL, // release a dead crab RELEASE_RAGDOLL_SLICED_OFF // toss the crab up a bit }; //========================================================= // schedules //========================================================= enum { SCHED_ZOMBIE_CHASE_ENEMY = LAST_SHARED_SCHEDULE, SCHED_ZOMBIE_MOVE_SWATITEM, SCHED_ZOMBIE_SWATITEM, SCHED_ZOMBIE_ATTACKITEM, SCHED_ZOMBIE_RELEASECRAB, SCHED_ZOMBIE_MOVE_TO_AMBUSH, SCHED_ZOMBIE_WAIT_AMBUSH, SCHED_ZOMBIE_WANDER_MEDIUM, // medium range wandering behavior. SCHED_ZOMBIE_WANDER_FAIL, SCHED_ZOMBIE_WANDER_STANDOFF, SCHED_ZOMBIE_MELEE_ATTACK1, SCHED_ZOMBIE_POST_MELEE_WAIT, LAST_BASE_ZOMBIE_SCHEDULE, }; //========================================================= // tasks //========================================================= enum { TASK_ZOMBIE_DELAY_SWAT = LAST_SHARED_TASK, TASK_ZOMBIE_GET_PATH_TO_PHYSOBJ, TASK_ZOMBIE_SWAT_ITEM, TASK_ZOMBIE_DIE, TASK_ZOMBIE_RELEASE_HEADCRAB, TASK_ZOMBIE_WAIT_POST_MELEE, LAST_BASE_ZOMBIE_TASK, }; //========================================================= // Zombie conditions //========================================================= enum Zombie_Conds { COND_ZOMBIE_CAN_SWAT_ATTACK = LAST_SHARED_CONDITION, COND_ZOMBIE_RELEASECRAB, COND_ZOMBIE_LOCAL_MELEE_OBSTRUCTION, LAST_BASE_ZOMBIE_CONDITION, }; typedef CAI_BlendingHost< CAI_BehaviorHost > CAI_BaseZombieBase; //========================================================= //========================================================= abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase { DECLARE_CLASS( CNPC_BaseZombie, CAI_BaseZombieBase ); public: CNPC_BaseZombie( void ); ~CNPC_BaseZombie( void ); void Spawn( void ); void Precache( void ); void StartTouch( CBaseEntity *pOther ); bool CreateBehaviors(); float MaxYawSpeed( void ); bool OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval ); Class_T Classify( void ); Disposition_t IRelationType( CBaseEntity *pTarget ); void HandleAnimEvent( animevent_t *pEvent ); void OnStateChange( NPC_STATE OldState, NPC_STATE NewState ); void KillMe( void ) { m_iHealth = 5; OnTakeDamage( CTakeDamageInfo( this, this, m_iHealth * 2, DMG_GENERIC ) ); } int MeleeAttack1Conditions ( float flDot, float flDist ); virtual float GetClawAttackRange() const { return ZOMBIE_MELEE_REACH; } // No range attacks int RangeAttack1Conditions ( float flDot, float flDist ) { return( 0 ); } virtual float GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDamageInfo &info ); void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr ); int OnTakeDamage_Alive( const CTakeDamageInfo &info ); virtual float GetReactionDelay( CBaseEntity *pEnemy ) { return 0.0; } virtual int SelectSchedule ( void ); virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); virtual void BuildScheduleTestBits( void ); virtual int TranslateSchedule( int scheduleType ); virtual Activity NPC_TranslateActivity( Activity baseAct ); void StartTask( const Task_t *pTask ); void RunTask( const Task_t *pTask ); void GatherConditions( void ); void PrescheduleThink( void ); virtual void Event_Killed( const CTakeDamageInfo &info ); virtual bool BecomeRagdoll( const CTakeDamageInfo &info, const Vector &forceVector ); void StopLoopingSounds(); virtual void OnScheduleChange( void ); virtual void PoundSound(); // Custom damage/death bool ShouldIgnite( const CTakeDamageInfo &info ); bool ShouldIgniteZombieGib( void ); virtual bool IsChopped( const CTakeDamageInfo &info ); virtual bool IsSquashed( const CTakeDamageInfo &info ) { return false; } virtual void DieChopped( const CTakeDamageInfo &info ); virtual void Ignite( float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false ); void CopyRenderColorTo( CBaseEntity *pOther ); virtual bool ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold ); virtual HeadcrabRelease_t ShouldReleaseHeadcrab( const CTakeDamageInfo &info, float flDamageThreshold ); // Headcrab releasing/breaking apart void RemoveHead( void ); virtual void SetZombieModel( void ) { }; virtual void BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce ); virtual bool CanBecomeLiveTorso() { return false; } virtual bool HeadcrabFits( CBaseAnimating *pCrab ); void ReleaseHeadcrab( const Vector &vecOrigin, const Vector &vecVelocity, bool fRemoveHead, bool fRagdollBody, bool fRagdollCrab = false ); void SetHeadcrabSpawnLocation( int iCrabAttachment, CBaseAnimating *pCrab ); // Slumping/sleeping bool IsSlumped( void ); bool IsGettingUp( void ); // Swatting physics objects int GetSwatActivity( void ); bool FindNearestPhysicsObject( int iMaxMass ); float DistToPhysicsEnt( void ); virtual bool CanSwatPhysicsObjects( void ) { return true; } // Returns whether we must be very near our enemy to attack them. virtual bool MustCloseToAttack(void) { return true; } virtual CBaseEntity *ClawAttack( float flDist, int iDamage, QAngle &qaViewPunch, Vector &vecVelocityPunch, int BloodOrigin ); // Sounds & sound envelope virtual bool ShouldPlayFootstepMoan( void ); virtual void PainSound( const CTakeDamageInfo &info ) = 0; virtual void AlertSound( void ) = 0; virtual void IdleSound( void ) = 0; virtual void AttackSound( void ) = 0; virtual void AttackHitSound( void ) = 0; virtual void AttackMissSound( void ) = 0; virtual void FootstepSound( bool fRightFoot ) = 0; virtual void FootscuffSound( bool fRightFoot ) = 0; // make a sound Alyx can hear when in darkness mode void MakeAISpookySound( float volume, float duration = 0.5 ); virtual bool CanPlayMoanSound(); virtual void MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize ); bool ShouldPlayIdleSound( void ) { return false; } virtual const char *GetMoanSound( int nSound ) = 0; virtual const char *GetHeadcrabClassname( void ) = 0; virtual const char *GetLegsModel( void ) = 0; virtual const char *GetTorsoModel( void ) = 0; virtual const char *GetHeadcrabModel( void ) = 0; virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy ); virtual Vector HeadTarget( const Vector &posSrc ); virtual float GetAutoAimRadius(); virtual void TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition ); bool OnInsufficientStopDist( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); virtual bool AllowedToIgnite( void ) { return true; } public: CAI_ActBusyBehavior m_ActBusyBehavior; protected: CSoundPatch *m_pMoanSound; bool m_fIsTorso; // is this is a half-zombie? bool m_fIsHeadless; // is this zombie headless float m_flNextFlinch; bool m_bHeadShot; // Used to determine the survival of our crab beyond our death. // // Zombies catch on fire if they take too much burn damage in a given time period. // float m_flBurnDamage; // Keeps track of how much burn damage we've incurred in the last few seconds. float m_flBurnDamageResetTime; // Time at which we reset the burn damage. EHANDLE m_hPhysicsEnt; float m_flNextMoanSound; float m_flNextSwat; float m_flNextSwatScan; float m_crabHealth; float m_flMoanPitch; EHANDLE m_hObstructor; static int g_numZombies; // counts total number of existing zombies. int m_iMoanSound; // each zombie picks one of the 4 and keeps it. static int ACT_ZOM_SWATLEFTMID; static int ACT_ZOM_SWATRIGHTMID; static int ACT_ZOM_SWATLEFTLOW; static int ACT_ZOM_SWATRIGHTLOW; static int ACT_ZOM_RELEASECRAB; static int ACT_ZOM_FALL; DECLARE_DATADESC(); DEFINE_CUSTOM_AI; private: bool m_bIsSlumped; }; #endif // NPC_BASEZOMBIE_H