//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #ifndef NPC_ANTLION_H #define NPC_ANTLION_H #ifdef _WIN32 #pragma once #endif #include "ai_blended_movement.h" #include "soundent.h" #include "ai_behavior_follow.h" #include "ai_behavior_assault.h" class CAntlionTemplateMaker; #define ANTLION_FOLLOW_DISTANCE 350 #define ANTLION_FOLLOW_DISTANCE_SQR (ANTLION_FOLLOW_DISTANCE*ANTLION_FOLLOW_DISTANCE) #define ANTLION_SKIN_COUNT 4 class CNPC_Antlion; // Antlion follow behavior class CAI_AntlionFollowBehavior : public CAI_FollowBehavior { typedef CAI_FollowBehavior BaseClass; public: CAI_AntlionFollowBehavior() : BaseClass( AIF_ANTLION ) { } bool FarFromFollowTarget( void ) { return ( GetFollowTarget() && (GetAbsOrigin() - GetFollowTarget()->GetAbsOrigin()).LengthSqr() > ANTLION_FOLLOW_DISTANCE_SQR ); } bool ShouldFollow( void ) { if ( GetFollowTarget() == NULL ) return false; if ( GetEnemy() != NULL ) return false; return true; } }; // // Antlion class // enum AntlionMoveState_e { ANTLION_MOVE_FREE, ANTLION_MOVE_FOLLOW, ANTLION_MOVE_FIGHT_TO_GOAL, }; #define SF_ANTLION_BURROW_ON_ELUDED ( 1 << 16 ) #define SF_ANTLION_USE_GROUNDCHECKS ( 1 << 17 ) #define SF_ANTLION_WORKER ( 1 << 18 ) // Use the "worker" model typedef CAI_BlendingHost< CAI_BehaviorHost > CAI_BaseAntlionBase; class CNPC_Antlion : public CAI_BaseAntlionBase { public: DECLARE_CLASS( CNPC_Antlion, CAI_BaseAntlionBase ); CNPC_Antlion( void ); virtual float InnateRange1MinRange( void ) { return 50*12; } virtual float InnateRange1MaxRange( void ) { return 250*12; } bool IsWorker( void ) const { return HasSpawnFlags( SF_ANTLION_WORKER ); } // NOTE: IsAntlionWorker function must agree! float GetIdealAccel( void ) const; float MaxYawSpeed( void ); bool FInViewCone( CBaseEntity *pEntity ); bool FInViewCone( const Vector &vecSpot ); void Activate( void ); void HandleAnimEvent( animevent_t *pEvent ); void StartTask( const Task_t *pTask ); void RunTask( const Task_t *pTask ); void IdleSound( void ); void PainSound( const CTakeDamageInfo &info ); void Precache( void ); void Spawn( void ); int OnTakeDamage_Alive( const CTakeDamageInfo &info ); void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ); void BuildScheduleTestBits( void ); void GatherConditions( void ); void PrescheduleThink( void ); void ZapThink( void ); void BurrowUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); bool CreateVPhysics(); bool IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos ) const; bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sender = NULL ); bool QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC = false ); bool ShouldPlayIdleSound( void ); bool OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval ); bool IsValidEnemy(CBaseEntity *pEnemy); bool QueryHearSound( CSound *pSound ); bool IsLightDamage( const CTakeDamageInfo &info ); bool CreateBehaviors( void ); bool ShouldHearBugbait( void ) { return ( m_bIgnoreBugbait == false ); } int SelectSchedule( void ); void Touch( CBaseEntity *pOther ); virtual int RangeAttack1Conditions( float flDot, float flDist ); virtual int MeleeAttack1Conditions( float flDot, float flDist ); virtual int MeleeAttack2Conditions( float flDot, float flDist ); virtual int GetSoundInterests( void ) { return (BaseClass::GetSoundInterests())|(SOUND_DANGER|SOUND_PHYSICS_DANGER|SOUND_THUMPER|SOUND_BUGBAIT); } virtual bool IsHeavyDamage( const CTakeDamageInfo &info ); Class_T Classify( void ) { return CLASS_ANTLION; } void Event_Killed( const CTakeDamageInfo &info ); bool FValidateHintType ( CAI_Hint *pHint ); void GatherEnemyConditions( CBaseEntity *pEnemy ); bool IsAllied( void ); bool ShouldGib( const CTakeDamageInfo &info ); bool CorpseGib( const CTakeDamageInfo &info ); float GetMaxJumpSpeed() const { return 1024.0f; } void SetFightTarget( CBaseEntity *pTarget ); void InputFightToPosition( inputdata_t &inputdata ); void InputStopFightToPosition( inputdata_t &inputdata ); void InputJumpAtTarget( inputdata_t &inputdata ); void SetFollowTarget( CBaseEntity *pTarget ); int TranslateSchedule( int scheduleType ); virtual Activity NPC_TranslateActivity( Activity baseAct ); bool ShouldResumeFollow( void ); bool ShouldAbandonFollow( void ); void SetMoveState( AntlionMoveState_e state ); int ChooseMoveSchedule( void ); DECLARE_DATADESC(); bool m_bStartBurrowed; float m_flNextJumpPushTime; void SetParentSpawnerName( const char *szName ) { m_strParentSpawner = MAKE_STRING( szName ); } const char *GetParentSpawnerName( void ) { return STRING( m_strParentSpawner ); } virtual void StopLoopingSounds( void ); bool AllowedToBePushed( void ); virtual Vector BodyTarget( const Vector &posSrc, bool bNoisy = true ); virtual float GetAutoAimRadius() { return 36.0f; } void ClearBurrowPoint( const Vector &origin ); void Flip( bool bZapped = false ); bool CanBecomeRagdoll(); virtual void NotifyDeadFriend( CBaseEntity *pFriend ); private: inline CBaseEntity *EntityToWatch( void ); void UpdateHead( void ); bool FindChasePosition( const Vector &targetPos, Vector &result ); bool GetGroundPosition( const Vector &testPos, Vector &result ); bool GetPathToSoundFleePoint( int soundType ); inline bool IsFlipped( void ); void Burrow( void ); void Unburrow( void ); void InputUnburrow( inputdata_t &inputdata ); void InputBurrow( inputdata_t &inputdata ); void InputBurrowAway( inputdata_t &inputdata ); void InputDisableJump( inputdata_t &inputdata ); void InputEnableJump( inputdata_t &inputdata ); void InputIgnoreBugbait( inputdata_t &inputdata ); void InputHearBugbait( inputdata_t &inputdata ); bool FindBurrow( const Vector &origin, float distance, int type, bool excludeNear = true ); void CreateDust( bool placeDecal = true ); bool ValidBurrowPoint( const Vector &point ); bool CheckLanding( void ); bool Alone( void ); bool CheckAlertRadius( void ); bool ShouldJump( void ); void MeleeAttack( float distance, float damage, QAngle& viewPunch, Vector& shove ); void SetWings( bool state ); void StartJump( void ); void LockJumpNode( void ); bool IsUnusableNode(int iNodeID, CAI_Hint *pHint); bool OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult ); void ManageFleeCapabilities( bool bEnable ); int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); bool IsFirmlyOnGround( void ); void CascadePush( const Vector &vecForce ); virtual bool CanRunAScriptedNPCInteraction( bool bForced = false ); virtual void Ignite ( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner ); virtual bool GetSpitVector( const Vector &vecStartPos, const Vector &vecTarget, Vector *vecOut ); virtual bool InnateWeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ); virtual bool FCanCheckAttacks( void ); bool SeenEnemyWithinTime( float flTime ); void DelaySquadAttack( float flDuration ); #if HL2_EPISODIC void DoPoisonBurst(); #endif float m_flIdleDelay; float m_flBurrowTime; float m_flJumpTime; float m_flAlertRadius; float m_flPounceTime; int m_iUnBurrowAttempts; int m_iContext; //for FValidateHintType context Vector m_vecSaveSpitVelocity; // Saved when we start to attack and used if we failed to get a clear shot once we release CAI_AntlionFollowBehavior m_FollowBehavior; CAI_AssaultBehavior m_AssaultBehavior; AntlionMoveState_e m_MoveState; COutputEvent m_OnReachFightGoal; //Reached our scripted destination to fight to COutputEvent m_OnUnBurrowed; //Unburrowed Vector m_vecSavedJump; Vector m_vecLastJumpAttempt; float m_flIgnoreSoundTime; // Sound time to ignore if earlier than float m_flNextAcknowledgeTime; // Next time an antlion can make an acknowledgement noise float m_flSuppressFollowTime; // Amount of time to suppress our follow time float m_flObeyFollowTime; // A range of time the antlions must be obedient Vector m_vecHeardSound; bool m_bHasHeardSound; bool m_bAgitatedSound; //Playing agitated sound? bool m_bWingsOpen; //Are the wings open? bool m_bIgnoreBugbait; //If the antlion should ignore bugbait sounds string_t m_strParentSpawner; //Name of our spawner EHANDLE m_hFollowTarget; EHANDLE m_hFightGoalTarget; float m_flEludeDistance; //Distance until the antlion will consider himself "eluded" if so flagged bool m_bLeapAttack; bool m_bDisableJump; float m_flTimeDrown; float m_flTimeDrownSplash; bool m_bDontExplode; // Suppresses worker poison burst when drowning, failing to unburrow, etc. bool m_bLoopingStarted; bool m_bSuppressUnburrowEffects; // Don't kick up dust when spawning #if HL2_EPISODIC bool m_bHasDoneAirAttack; ///< only allowed to apply this damage once per glide #endif bool m_bForcedStuckJump; int m_nBodyBone; // Used to trigger a heavy damage interrupt if sustained damage is taken int m_nSustainedDamage; float m_flLastDamageTime; float m_flZapDuration; protected: int m_poseHead_Yaw, m_poseHead_Pitch; virtual void PopulatePoseParameters( void ); private: HSOUNDSCRIPTHANDLE m_hFootstep; DEFINE_CUSTOM_AI; //================================================== // AntlionConditions //================================================== enum { COND_ANTLION_FLIPPED = LAST_SHARED_CONDITION, COND_ANTLION_ON_NPC, COND_ANTLION_CAN_JUMP, COND_ANTLION_FOLLOW_TARGET_TOO_FAR, COND_ANTLION_RECEIVED_ORDERS, COND_ANTLION_IN_WATER, COND_ANTLION_CAN_JUMP_AT_TARGET, COND_ANTLION_SQUADMATE_KILLED }; //================================================== // AntlionSchedules //================================================== enum { SCHED_ANTLION_CHASE_ENEMY_BURROW = LAST_SHARED_SCHEDULE, SCHED_ANTLION_JUMP, SCHED_ANTLION_RUN_TO_BURROW_IN, SCHED_ANTLION_BURROW_IN, SCHED_ANTLION_BURROW_WAIT, SCHED_ANTLION_BURROW_OUT, SCHED_ANTLION_WAIT_FOR_UNBORROW_TRIGGER, SCHED_ANTLION_WAIT_FOR_CLEAR_UNBORROW, SCHED_ANTLION_WAIT_UNBORROW, SCHED_ANTLION_FLEE_THUMPER, SCHED_ANTLION_CHASE_BUGBAIT, SCHED_ANTLION_FLIP, SCHED_ANTLION_DISMOUNT_NPC, SCHED_ANTLION_RUN_TO_FIGHT_GOAL, SCHED_ANTLION_RUN_TO_FOLLOW_GOAL, SCHED_ANTLION_BUGBAIT_IDLE_STAND, SCHED_ANTLION_BURROW_AWAY, SCHED_ANTLION_FLEE_PHYSICS_DANGER, SCHED_ANTLION_POUNCE, SCHED_ANTLION_POUNCE_MOVING, SCHED_ANTLION_DROWN, SCHED_ANTLION_WORKER_RANGE_ATTACK1, SCHED_ANTLION_WORKER_RUN_RANDOM, SCHED_ANTLION_TAKE_COVER_FROM_ENEMY, SCHED_ANTLION_ZAP_FLIP, SCHED_ANTLION_WORKER_FLANK_RANDOM, SCHED_ANTLION_TAKE_COVER_FROM_SAVEPOSITION }; //================================================== // AntlionTasks //================================================== enum { TASK_ANTLION_SET_CHARGE_GOAL = LAST_SHARED_TASK, TASK_ANTLION_FIND_BURROW_IN_POINT, TASK_ANTLION_FIND_BURROW_OUT_POINT, TASK_ANTLION_BURROW, TASK_ANTLION_UNBURROW, TASK_ANTLION_VANISH, TASK_ANTLION_BURROW_WAIT, TASK_ANTLION_CHECK_FOR_UNBORROW, TASK_ANTLION_JUMP, TASK_ANTLION_WAIT_FOR_TRIGGER, TASK_ANTLION_GET_THUMPER_ESCAPE_PATH, TASK_ANTLION_GET_PATH_TO_BUGBAIT, TASK_ANTLION_FACE_BUGBAIT, TASK_ANTLION_DISMOUNT_NPC, TASK_ANTLION_REACH_FIGHT_GOAL, TASK_ANTLION_GET_PHYSICS_DANGER_ESCAPE_PATH, TASK_ANTLION_FACE_JUMP, TASK_ANTLION_DROWN, TASK_ANTLION_GET_PATH_TO_RANDOM_NODE, TASK_ANTLION_FIND_COVER_FROM_SAVEPOSITION, }; }; //----------------------------------------------------------------------------- // Purpose: Shield //----------------------------------------------------------------------------- class CAntlionRepellant : public CPointEntity { DECLARE_DATADESC(); public: DECLARE_CLASS( CAntlionRepellant, CPointEntity ); ~CAntlionRepellant(); public: void Spawn( void ); void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); float GetRadius( void ); void SetRadius( float flRadius ) { m_flRepelRadius = flRadius; } static bool IsPositionRepellantFree( Vector vDesiredPos ); void OnRestore( void ); private: float m_flRepelRadius; bool m_bEnabled; }; extern bool IsAntlion( CBaseEntity *pEntity ); extern bool IsAntlionWorker( CBaseEntity *pEntity ); #ifdef HL2_EPISODIC extern float AntlionWorkerBurstRadius( void ); #endif // HL2_EPISODIC #endif // NPC_ANTLION_H