diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/hl2/npc_zombine.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/hl2/npc_zombine.cpp')
| -rw-r--r-- | mp/src/game/server/hl2/npc_zombine.cpp | 2064 |
1 files changed, 1032 insertions, 1032 deletions
diff --git a/mp/src/game/server/hl2/npc_zombine.cpp b/mp/src/game/server/hl2/npc_zombine.cpp index 611dc682..479d220b 100644 --- a/mp/src/game/server/hl2/npc_zombine.cpp +++ b/mp/src/game/server/hl2/npc_zombine.cpp @@ -1,1032 +1,1032 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Combine Zombie... Zombie Combine... its like a... Zombine... get it?
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "ai_basenpc.h"
-#include "ai_default.h"
-#include "ai_schedule.h"
-#include "ai_hull.h"
-#include "ai_motor.h"
-#include "ai_memory.h"
-#include "ai_route.h"
-#include "ai_squad.h"
-#include "soundent.h"
-#include "game.h"
-#include "npcevent.h"
-#include "entitylist.h"
-#include "ai_task.h"
-#include "activitylist.h"
-#include "engine/IEngineSound.h"
-#include "npc_BaseZombie.h"
-#include "movevars_shared.h"
-#include "IEffects.h"
-#include "props.h"
-#include "physics_npc_solver.h"
-#include "hl2_player.h"
-#include "hl2_gamerules.h"
-
-#include "basecombatweapon.h"
-#include "basegrenade_shared.h"
-#include "grenade_frag.h"
-
-#include "ai_interactions.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-enum
-{
- SQUAD_SLOT_ZOMBINE_SPRINT1 = LAST_SHARED_SQUADSLOT,
- SQUAD_SLOT_ZOMBINE_SPRINT2,
-};
-
-#define MIN_SPRINT_TIME 3.5f
-#define MAX_SPRINT_TIME 5.5f
-
-#define MIN_SPRINT_DISTANCE 64.0f
-#define MAX_SPRINT_DISTANCE 1024.0f
-
-#define SPRINT_CHANCE_VALUE 10
-#define SPRINT_CHANCE_VALUE_DARKNESS 50
-
-#define GRENADE_PULL_MAX_DISTANCE 256.0f
-
-#define ZOMBINE_MAX_GRENADES 1
-
-int ACT_ZOMBINE_GRENADE_PULL;
-int ACT_ZOMBINE_GRENADE_WALK;
-int ACT_ZOMBINE_GRENADE_RUN;
-int ACT_ZOMBINE_GRENADE_IDLE;
-int ACT_ZOMBINE_ATTACK_FAST;
-int ACT_ZOMBINE_GRENADE_FLINCH_BACK;
-int ACT_ZOMBINE_GRENADE_FLINCH_FRONT;
-int ACT_ZOMBINE_GRENADE_FLINCH_WEST;
-int ACT_ZOMBINE_GRENADE_FLINCH_EAST;
-
-int AE_ZOMBINE_PULLPIN;
-
-extern bool IsAlyxInDarknessMode();
-
-ConVar sk_zombie_soldier_health( "sk_zombie_soldier_health","0");
-
-float g_flZombineGrenadeTimes = 0;
-
-class CNPC_Zombine : public CAI_BlendingHost<CNPC_BaseZombie>, public CDefaultPlayerPickupVPhysics
-{
- DECLARE_DATADESC();
- DECLARE_CLASS( CNPC_Zombine, CAI_BlendingHost<CNPC_BaseZombie> );
-
-public:
-
- void Spawn( void );
- void Precache( void );
-
- void SetZombieModel( void );
-
- virtual void PrescheduleThink( void );
- virtual int SelectSchedule( void );
- virtual void BuildScheduleTestBits( void );
-
- virtual void HandleAnimEvent( animevent_t *pEvent );
-
- virtual const char *GetLegsModel( void );
- virtual const char *GetTorsoModel( void );
- virtual const char *GetHeadcrabClassname( void );
- virtual const char *GetHeadcrabModel( void );
-
- virtual void PainSound( const CTakeDamageInfo &info );
- virtual void DeathSound( const CTakeDamageInfo &info );
- virtual void AlertSound( void );
- virtual void IdleSound( void );
- virtual void AttackSound( void );
- virtual void AttackHitSound( void );
- virtual void AttackMissSound( void );
- virtual void FootstepSound( bool fRightFoot );
- virtual void FootscuffSound( bool fRightFoot );
- virtual void MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize );
-
- virtual void Event_Killed( const CTakeDamageInfo &info );
- virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
- virtual void RunTask( const Task_t *pTask );
- virtual int MeleeAttack1Conditions ( float flDot, float flDist );
-
- virtual bool ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold );
-
- virtual void OnScheduleChange ( void );
- virtual bool CanRunAScriptedNPCInteraction( bool bForced );
-
- void GatherGrenadeConditions( void );
-
- virtual Activity NPC_TranslateActivity( Activity baseAct );
-
- const char *GetMoanSound( int nSound );
-
- bool AllowedToSprint( void );
- void Sprint( bool bMadSprint = false );
- void StopSprint( void );
-
- void DropGrenade( Vector vDir );
-
- bool IsSprinting( void ) { return m_flSprintTime > gpGlobals->curtime; }
- bool HasGrenade( void ) { return m_hGrenade != NULL; }
-
- int TranslateSchedule( int scheduleType );
-
- void InputStartSprint ( inputdata_t &inputdata );
- void InputPullGrenade ( inputdata_t &inputdata );
-
- virtual CBaseEntity *OnFailedPhysGunPickup ( Vector vPhysgunPos );
-
- //Called when we want to let go of a grenade and let the physcannon pick it up.
- void ReleaseGrenade( Vector vPhysgunPos );
-
- virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt );
-
- enum
- {
- COND_ZOMBINE_GRENADE = LAST_BASE_ZOMBIE_CONDITION,
- };
-
- enum
- {
- SCHED_ZOMBINE_PULL_GRENADE = LAST_BASE_ZOMBIE_SCHEDULE,
- };
-
-public:
- DEFINE_CUSTOM_AI;
-
-private:
-
- float m_flSprintTime;
- float m_flSprintRestTime;
-
- float m_flSuperFastAttackTime;
- float m_flGrenadePullTime;
-
- int m_iGrenadeCount;
-
- EHANDLE m_hGrenade;
-
-protected:
- static const char *pMoanSounds[];
-
-};
-
-LINK_ENTITY_TO_CLASS( npc_zombine, CNPC_Zombine );
-
-BEGIN_DATADESC( CNPC_Zombine )
- DEFINE_FIELD( m_flSprintTime, FIELD_TIME ),
- DEFINE_FIELD( m_flSprintRestTime, FIELD_TIME ),
- DEFINE_FIELD( m_flSuperFastAttackTime, FIELD_TIME ),
- DEFINE_FIELD( m_hGrenade, FIELD_EHANDLE ),
- DEFINE_FIELD( m_flGrenadePullTime, FIELD_TIME ),
- DEFINE_FIELD( m_iGrenadeCount, FIELD_INTEGER ),
- DEFINE_INPUTFUNC( FIELD_VOID, "StartSprint", InputStartSprint ),
- DEFINE_INPUTFUNC( FIELD_VOID, "PullGrenade", InputPullGrenade ),
-END_DATADESC()
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-const char *CNPC_Zombine::pMoanSounds[] =
-{
- "ATV_engine_null",
-};
-
-void CNPC_Zombine::Spawn( void )
-{
- Precache();
-
- m_fIsTorso = false;
- m_fIsHeadless = false;
-
-#ifdef HL2_EPISODIC
- SetBloodColor( BLOOD_COLOR_ZOMBIE );
-#else
- SetBloodColor( BLOOD_COLOR_GREEN );
-#endif // HL2_EPISODIC
-
- m_iHealth = sk_zombie_soldier_health.GetFloat();
- SetMaxHealth( m_iHealth );
-
- m_flFieldOfView = 0.2;
-
- CapabilitiesClear();
-
- BaseClass::Spawn();
-
- m_flSprintTime = 0.0f;
- m_flSprintRestTime = 0.0f;
-
- m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 1.0, 4.0 );
-
- g_flZombineGrenadeTimes = gpGlobals->curtime;
- m_flGrenadePullTime = gpGlobals->curtime;
-
- m_iGrenadeCount = ZOMBINE_MAX_GRENADES;
-}
-
-void CNPC_Zombine::Precache( void )
-{
- BaseClass::Precache();
-
- PrecacheModel( "models/zombie/zombie_soldier.mdl" );
-
- PrecacheScriptSound( "Zombie.FootstepRight" );
- PrecacheScriptSound( "Zombie.FootstepLeft" );
- PrecacheScriptSound( "Zombine.ScuffRight" );
- PrecacheScriptSound( "Zombine.ScuffLeft" );
- PrecacheScriptSound( "Zombie.AttackHit" );
- PrecacheScriptSound( "Zombie.AttackMiss" );
- PrecacheScriptSound( "Zombine.Pain" );
- PrecacheScriptSound( "Zombine.Die" );
- PrecacheScriptSound( "Zombine.Alert" );
- PrecacheScriptSound( "Zombine.Idle" );
- PrecacheScriptSound( "Zombine.ReadyGrenade" );
-
- PrecacheScriptSound( "ATV_engine_null" );
- PrecacheScriptSound( "Zombine.Charge" );
- PrecacheScriptSound( "Zombie.Attack" );
-}
-
-void CNPC_Zombine::SetZombieModel( void )
-{
- SetModel( "models/zombie/zombie_soldier.mdl" );
- SetHullType( HULL_HUMAN );
-
- SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );
-
- SetHullSizeNormal( true );
- SetDefaultEyeOffset();
- SetActivity( ACT_IDLE );
-}
-
-void CNPC_Zombine::PrescheduleThink( void )
-{
- GatherGrenadeConditions();
-
- if( gpGlobals->curtime > m_flNextMoanSound )
- {
- if( CanPlayMoanSound() )
- {
- // Classic guy idles instead of moans.
- IdleSound();
-
- m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 10.0, 15.0 );
- }
- else
- {
- m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 2.5, 5.0 );
- }
- }
-
- if ( HasGrenade () )
- {
- CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetSmoothedVelocity() * 0.5f , 256, 0.1, this, SOUNDENT_CHANNEL_ZOMBINE_GRENADE );
-
- if( IsSprinting() && GetEnemy() && GetEnemy()->Classify() == CLASS_PLAYER_ALLY_VITAL && HasCondition( COND_SEE_ENEMY ) )
- {
- if( GetAbsOrigin().DistToSqr(GetEnemy()->GetAbsOrigin()) < Square( 144 ) )
- {
- StopSprint();
- }
- }
- }
-
- BaseClass::PrescheduleThink();
-}
-
-void CNPC_Zombine::OnScheduleChange( void )
-{
- if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) && IsSprinting() == true )
- {
- m_flSuperFastAttackTime = gpGlobals->curtime + 1.0f;
- }
-
- BaseClass::OnScheduleChange();
-}
-bool CNPC_Zombine::CanRunAScriptedNPCInteraction( bool bForced )
-{
- if ( HasGrenade() == true )
- return false;
-
- return BaseClass::CanRunAScriptedNPCInteraction( bForced );
-}
-
-int CNPC_Zombine::SelectSchedule( void )
-{
- if ( GetHealth() <= 0 )
- return BaseClass::SelectSchedule();
-
- if ( HasCondition( COND_ZOMBINE_GRENADE ) )
- {
- ClearCondition( COND_ZOMBINE_GRENADE );
-
- return SCHED_ZOMBINE_PULL_GRENADE;
- }
-
- return BaseClass::SelectSchedule();
-}
-
-void CNPC_Zombine::BuildScheduleTestBits( void )
-{
- BaseClass::BuildScheduleTestBits();
-
- SetCustomInterruptCondition( COND_ZOMBINE_GRENADE );
-}
-
-Activity CNPC_Zombine::NPC_TranslateActivity( Activity baseAct )
-{
- if ( baseAct == ACT_MELEE_ATTACK1 )
- {
- if ( m_flSuperFastAttackTime > gpGlobals->curtime || HasGrenade() )
- {
- return (Activity)ACT_ZOMBINE_ATTACK_FAST;
- }
- }
-
- if ( baseAct == ACT_IDLE )
- {
- if ( HasGrenade() )
- {
- return (Activity)ACT_ZOMBINE_GRENADE_IDLE;
- }
- }
-
- return BaseClass::NPC_TranslateActivity( baseAct );
-}
-
-int CNPC_Zombine::MeleeAttack1Conditions ( float flDot, float flDist )
-{
- int iBase = BaseClass::MeleeAttack1Conditions( flDot, flDist );
-
- if( HasGrenade() )
- {
- //Adrian: stop spriting if we get close enough to melee and we have a grenade
- //this gives NPCs time to move away from you (before it was almost impossible cause of the high sprint speed)
- if ( iBase == COND_CAN_MELEE_ATTACK1 )
- {
- StopSprint();
- }
- }
-
- return iBase;
-}
-
-void CNPC_Zombine::GatherGrenadeConditions( void )
-{
- if ( m_iGrenadeCount <= 0 )
- return;
-
- if ( g_flZombineGrenadeTimes > gpGlobals->curtime )
- return;
-
- if ( m_flGrenadePullTime > gpGlobals->curtime )
- return;
-
- if ( m_flSuperFastAttackTime >= gpGlobals->curtime )
- return;
-
- if ( HasGrenade() )
- return;
-
- if ( GetEnemy() == NULL )
- return;
-
- if ( FVisible( GetEnemy() ) == false )
- return;
-
- if ( IsSprinting() )
- return;
-
- if ( IsOnFire() )
- return;
-
- if ( IsRunningDynamicInteraction() == true )
- return;
-
- if ( m_ActBusyBehavior.IsActive() )
- return;
-
- CBasePlayer *pPlayer = AI_GetSinglePlayer();
-
- if ( pPlayer && pPlayer->FVisible( this ) )
- {
- float flLengthToPlayer = (pPlayer->GetAbsOrigin() - GetAbsOrigin()).Length();
- float flLengthToEnemy = flLengthToPlayer;
-
- if ( pPlayer != GetEnemy() )
- {
- flLengthToEnemy = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin()).Length();
- }
-
- if ( flLengthToPlayer <= GRENADE_PULL_MAX_DISTANCE && flLengthToEnemy <= GRENADE_PULL_MAX_DISTANCE )
- {
- float flPullChance = 1.0f - ( flLengthToEnemy / GRENADE_PULL_MAX_DISTANCE );
- m_flGrenadePullTime = gpGlobals->curtime + 0.5f;
-
- if ( flPullChance >= random->RandomFloat( 0.0f, 1.0f ) )
- {
- g_flZombineGrenadeTimes = gpGlobals->curtime + 10.0f;
- SetCondition( COND_ZOMBINE_GRENADE );
- }
- }
- }
-}
-
-int CNPC_Zombine::TranslateSchedule( int scheduleType )
-{
- return BaseClass::TranslateSchedule( scheduleType );
-}
-
-void CNPC_Zombine::DropGrenade( Vector vDir )
-{
- if ( m_hGrenade == NULL )
- return;
-
- m_hGrenade->SetParent( NULL );
- m_hGrenade->SetOwnerEntity( NULL );
-
- Vector vGunPos;
- QAngle angles;
- GetAttachment( "grenade_attachment", vGunPos, angles );
-
- IPhysicsObject *pPhysObj = m_hGrenade->VPhysicsGetObject();
-
- if ( pPhysObj == NULL )
- {
- m_hGrenade->SetMoveType( MOVETYPE_VPHYSICS );
- m_hGrenade->SetSolid( SOLID_VPHYSICS );
- m_hGrenade->SetCollisionGroup( COLLISION_GROUP_WEAPON );
-
- m_hGrenade->CreateVPhysics();
- }
-
- if ( pPhysObj )
- {
- pPhysObj->Wake();
- pPhysObj->SetPosition( vGunPos, angles, true );
- pPhysObj->ApplyForceCenter( vDir * 0.2f );
-
- pPhysObj->RecheckCollisionFilter();
- }
-
- m_hGrenade = NULL;
-}
-
-void CNPC_Zombine::Event_Killed( const CTakeDamageInfo &info )
-{
- BaseClass::Event_Killed( info );
-
- if ( HasGrenade() )
- {
- DropGrenade( vec3_origin );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: This is a generic function (to be implemented by sub-classes) to
-// handle specific interactions between different types of characters
-// (For example the barnacle grabbing an NPC)
-// Input : Constant for the type of interaction
-// Output : true - if sub-class has a response for the interaction
-// false - if sub-class has no response
-//-----------------------------------------------------------------------------
-bool CNPC_Zombine::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt )
-{
- if ( interactionType == g_interactionBarnacleVictimGrab )
- {
- if ( HasGrenade() )
- {
- DropGrenade( vec3_origin );
- }
- }
-
- return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
-}
-
-void CNPC_Zombine::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
-{
- BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
-
- //Only knock grenades off their hands if it's a player doing the damage.
- if ( info.GetAttacker() && info.GetAttacker()->IsNPC() )
- return;
-
- if ( info.GetDamageType() & ( DMG_BULLET | DMG_CLUB ) )
- {
- if ( ptr->hitgroup == HITGROUP_LEFTARM )
- {
- if ( HasGrenade() )
- {
- DropGrenade( info.GetDamageForce() );
- StopSprint();
- }
- }
- }
-}
-
-void CNPC_Zombine::HandleAnimEvent( animevent_t *pEvent )
-{
- if ( pEvent->event == AE_ZOMBINE_PULLPIN )
- {
- Vector vecStart;
- QAngle angles;
- GetAttachment( "grenade_attachment", vecStart, angles );
-
- CBaseGrenade *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, vec3_origin, AngularImpulse( 0, 0, 0 ), this, 3.5f, true );
-
- if ( pGrenade )
- {
- // Move physobject to shadow
- IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
-
- if ( pPhysicsObject )
- {
- pGrenade->VPhysicsDestroyObject();
-
- int iAttachment = LookupAttachment( "grenade_attachment");
-
- pGrenade->SetMoveType( MOVETYPE_NONE );
- pGrenade->SetSolid( SOLID_NONE );
- pGrenade->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
-
- pGrenade->SetAbsOrigin( vecStart );
- pGrenade->SetAbsAngles( angles );
-
- pGrenade->SetParent( this, iAttachment );
-
- pGrenade->SetDamage( 200.0f );
- m_hGrenade = pGrenade;
-
- EmitSound( "Zombine.ReadyGrenade" );
-
- // Tell player allies nearby to regard me!
- CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
- CAI_BaseNPC *pNPC;
- for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
- {
- pNPC = ppAIs[i];
-
- if( pNPC->Classify() == CLASS_PLAYER_ALLY || ( pNPC->Classify() == CLASS_PLAYER_ALLY_VITAL && pNPC->FVisible(this) ) )
- {
- int priority;
- Disposition_t disposition;
-
- priority = pNPC->IRelationPriority(this);
- disposition = pNPC->IRelationType(this);
-
- pNPC->AddEntityRelationship( this, disposition, priority + 1 );
- }
- }
- }
-
- m_iGrenadeCount--;
- }
-
- return;
- }
-
- if ( pEvent->event == AE_NPC_ATTACK_BROADCAST )
- {
- if ( HasGrenade() )
- return;
- }
-
- BaseClass::HandleAnimEvent( pEvent );
-}
-
-bool CNPC_Zombine::AllowedToSprint( void )
-{
- if ( IsOnFire() )
- return false;
-
- //If you're sprinting then there's no reason to sprint again.
- if ( IsSprinting() )
- return false;
-
- int iChance = SPRINT_CHANCE_VALUE;
-
- CHL2_Player *pPlayer = dynamic_cast <CHL2_Player*> ( AI_GetSinglePlayer() );
-
- if ( pPlayer )
- {
- if ( HL2GameRules()->IsAlyxInDarknessMode() && pPlayer->FlashlightIsOn() == false )
- {
- iChance = SPRINT_CHANCE_VALUE_DARKNESS;
- }
-
- //Bigger chance of this happening if the player is not looking at the zombie
- if ( pPlayer->FInViewCone( this ) == false )
- {
- iChance *= 2;
- }
- }
-
- if ( HasGrenade() )
- {
- iChance *= 4;
- }
-
- //Below 25% health they'll always sprint
- if ( ( GetHealth() > GetMaxHealth() * 0.5f ) )
- {
- if ( IsStrategySlotRangeOccupied( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 ) == true )
- return false;
-
- if ( random->RandomInt( 0, 100 ) > iChance )
- return false;
-
- if ( m_flSprintRestTime > gpGlobals->curtime )
- return false;
- }
-
- float flLength = ( GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() ).Length();
-
- if ( flLength > MAX_SPRINT_DISTANCE )
- return false;
-
- return true;
-}
-
-void CNPC_Zombine::StopSprint( void )
-{
- GetNavigator()->SetMovementActivity( ACT_WALK );
-
- m_flSprintTime = gpGlobals->curtime;
- m_flSprintRestTime = m_flSprintTime + random->RandomFloat( 2.5f, 5.0f );
-}
-
-void CNPC_Zombine::Sprint( bool bMadSprint )
-{
- if ( IsSprinting() )
- return;
-
- OccupyStrategySlotRange( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 );
- GetNavigator()->SetMovementActivity( ACT_RUN );
-
- float flSprintTime = random->RandomFloat( MIN_SPRINT_TIME, MAX_SPRINT_TIME );
-
- //If holding a grenade then sprint until it blows up.
- if ( HasGrenade() || bMadSprint == true )
- {
- flSprintTime = 9999;
- }
-
- m_flSprintTime = gpGlobals->curtime + flSprintTime;
-
- //Don't sprint for this long after I'm done with this sprint run.
- m_flSprintRestTime = m_flSprintTime + random->RandomFloat( 2.5f, 5.0f );
-
- EmitSound( "Zombine.Charge" );
-}
-
-void CNPC_Zombine::RunTask( const Task_t *pTask )
-{
- switch ( pTask->iTask )
- {
- case TASK_WAIT_FOR_MOVEMENT_STEP:
- case TASK_WAIT_FOR_MOVEMENT:
- {
- BaseClass::RunTask( pTask );
-
- if ( IsOnFire() && IsSprinting() )
- {
- StopSprint();
- }
-
- //Only do this if I have an enemy
- if ( GetEnemy() )
- {
- if ( AllowedToSprint() == true )
- {
- Sprint( ( GetHealth() <= GetMaxHealth() * 0.5f ) );
- return;
- }
-
- if ( HasGrenade() )
- {
- if ( IsSprinting() )
- {
- GetNavigator()->SetMovementActivity( (Activity)ACT_ZOMBINE_GRENADE_RUN );
- }
- else
- {
- GetNavigator()->SetMovementActivity( (Activity)ACT_ZOMBINE_GRENADE_WALK );
- }
-
- return;
- }
-
- if ( GetNavigator()->GetMovementActivity() != ACT_WALK )
- {
- if ( IsSprinting() == false )
- {
- GetNavigator()->SetMovementActivity( ACT_WALK );
- }
- }
- }
- else
- {
- GetNavigator()->SetMovementActivity( ACT_WALK );
- }
-
- break;
- }
- default:
- {
- BaseClass::RunTask( pTask );
- break;
- }
- }
-}
-
-void CNPC_Zombine::InputStartSprint ( inputdata_t &inputdata )
-{
- Sprint();
-}
-
-void CNPC_Zombine::InputPullGrenade ( inputdata_t &inputdata )
-{
- g_flZombineGrenadeTimes = gpGlobals->curtime + 5.0f;
- SetCondition( COND_ZOMBINE_GRENADE );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns a moan sound for this class of zombie.
-//-----------------------------------------------------------------------------
-const char *CNPC_Zombine::GetMoanSound( int nSound )
-{
- return pMoanSounds[ nSound % ARRAYSIZE( pMoanSounds ) ];
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Sound of a footstep
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::FootstepSound( bool fRightFoot )
-{
- if( fRightFoot )
- {
- EmitSound( "Zombie.FootstepRight" );
- }
- else
- {
- EmitSound( "Zombie.FootstepLeft" );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Overloaded so that explosions don't split the zombine in twain.
-//-----------------------------------------------------------------------------
-bool CNPC_Zombine::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold )
-{
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Sound of a foot sliding/scraping
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::FootscuffSound( bool fRightFoot )
-{
- if( fRightFoot )
- {
- EmitSound( "Zombine.ScuffRight" );
- }
- else
- {
- EmitSound( "Zombine.ScuffLeft" );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play a random attack hit sound
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::AttackHitSound( void )
-{
- EmitSound( "Zombie.AttackHit" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play a random attack miss sound
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::AttackMissSound( void )
-{
- // Play a random attack miss sound
- EmitSound( "Zombie.AttackMiss" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::PainSound( const CTakeDamageInfo &info )
-{
- // We're constantly taking damage when we are on fire. Don't make all those noises!
- if ( IsOnFire() )
- {
- return;
- }
-
- EmitSound( "Zombine.Pain" );
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::DeathSound( const CTakeDamageInfo &info )
-{
- EmitSound( "Zombine.Die" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::AlertSound( void )
-{
- EmitSound( "Zombine.Alert" );
-
- // Don't let a moan sound cut off the alert sound.
- m_flNextMoanSound += random->RandomFloat( 2.0, 4.0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play a random idle sound.
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::IdleSound( void )
-{
- if( GetState() == NPC_STATE_IDLE && random->RandomFloat( 0, 1 ) == 0 )
- {
- // Moan infrequently in IDLE state.
- return;
- }
-
- if( IsSlumped() )
- {
- // Sleeping zombies are quiet.
- return;
- }
-
- EmitSound( "Zombine.Idle" );
- MakeAISpookySound( 360.0f );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play a random attack sound.
-//-----------------------------------------------------------------------------
-void CNPC_Zombine::AttackSound( void )
-{
-
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-const char *CNPC_Zombine::GetHeadcrabModel( void )
-{
- return "models/headcrabclassic.mdl";
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-const char *CNPC_Zombine::GetLegsModel( void )
-{
- return "models/zombie/zombie_soldier_legs.mdl";
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-const char *CNPC_Zombine::GetTorsoModel( void )
-{
- return "models/zombie/zombie_soldier_torso.mdl";
-}
-
-//---------------------------------------------------------
-// Classic zombie only uses moan sound if on fire.
-//---------------------------------------------------------
-void CNPC_Zombine::MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize )
-{
- if( IsOnFire() )
- {
- BaseClass::MoanSound( pEnvelope, iEnvelopeSize );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the classname (ie "npc_headcrab") to spawn when our headcrab bails.
-//-----------------------------------------------------------------------------
-const char *CNPC_Zombine::GetHeadcrabClassname( void )
-{
- return "npc_headcrab";
-}
-
-void CNPC_Zombine::ReleaseGrenade( Vector vPhysgunPos )
-{
- if ( HasGrenade() == false )
- return;
-
- Vector vDir = vPhysgunPos - m_hGrenade->GetAbsOrigin();
- VectorNormalize( vDir );
-
- Activity aActivity;
-
- Vector vForward, vRight;
- GetVectors( &vForward, &vRight, NULL );
-
- float flDotForward = DotProduct( vForward, vDir );
- float flDotRight = DotProduct( vRight, vDir );
-
- bool bNegativeForward = false;
- bool bNegativeRight = false;
-
- if ( flDotForward < 0.0f )
- {
- bNegativeForward = true;
- flDotForward = flDotForward * -1;
- }
-
- if ( flDotRight < 0.0f )
- {
- bNegativeRight = true;
- flDotRight = flDotRight * -1;
- }
-
- if ( flDotRight > flDotForward )
- {
- if ( bNegativeRight == true )
- aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_WEST;
- else
- aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_EAST;
- }
- else
- {
- if ( bNegativeForward == true )
- aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_BACK;
- else
- aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_FRONT;
- }
-
- AddGesture( aActivity );
-
- DropGrenade( vec3_origin );
-
- if ( IsSprinting() )
- {
- StopSprint();
- }
- else
- {
- Sprint();
- }
-}
-
-CBaseEntity *CNPC_Zombine::OnFailedPhysGunPickup( Vector vPhysgunPos )
-{
- CBaseEntity *pGrenade = m_hGrenade;
- ReleaseGrenade( vPhysgunPos );
- return pGrenade;
-}
-
-//-----------------------------------------------------------------------------
-//
-// Schedules
-//
-//-----------------------------------------------------------------------------
-
-AI_BEGIN_CUSTOM_NPC( npc_zombine, CNPC_Zombine )
-
- //Squad slots
- DECLARE_SQUADSLOT( SQUAD_SLOT_ZOMBINE_SPRINT1 )
- DECLARE_SQUADSLOT( SQUAD_SLOT_ZOMBINE_SPRINT2 )
-
- DECLARE_CONDITION( COND_ZOMBINE_GRENADE )
-
- DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_PULL )
- DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_WALK )
- DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_RUN )
- DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_IDLE )
- DECLARE_ACTIVITY( ACT_ZOMBINE_ATTACK_FAST )
- DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_BACK )
- DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_FRONT )
- DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_WEST)
- DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_EAST )
-
- DECLARE_ANIMEVENT( AE_ZOMBINE_PULLPIN )
-
-
- DEFINE_SCHEDULE
- (
- SCHED_ZOMBINE_PULL_GRENADE,
-
- " Tasks"
- " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ZOMBINE_GRENADE_PULL"
-
-
- " Interrupts"
-
- )
-
-AI_END_CUSTOM_NPC()
-
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Combine Zombie... Zombie Combine... its like a... Zombine... get it? +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "ai_basenpc.h" +#include "ai_default.h" +#include "ai_schedule.h" +#include "ai_hull.h" +#include "ai_motor.h" +#include "ai_memory.h" +#include "ai_route.h" +#include "ai_squad.h" +#include "soundent.h" +#include "game.h" +#include "npcevent.h" +#include "entitylist.h" +#include "ai_task.h" +#include "activitylist.h" +#include "engine/IEngineSound.h" +#include "npc_BaseZombie.h" +#include "movevars_shared.h" +#include "IEffects.h" +#include "props.h" +#include "physics_npc_solver.h" +#include "hl2_player.h" +#include "hl2_gamerules.h" + +#include "basecombatweapon.h" +#include "basegrenade_shared.h" +#include "grenade_frag.h" + +#include "ai_interactions.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +enum +{ + SQUAD_SLOT_ZOMBINE_SPRINT1 = LAST_SHARED_SQUADSLOT, + SQUAD_SLOT_ZOMBINE_SPRINT2, +}; + +#define MIN_SPRINT_TIME 3.5f +#define MAX_SPRINT_TIME 5.5f + +#define MIN_SPRINT_DISTANCE 64.0f +#define MAX_SPRINT_DISTANCE 1024.0f + +#define SPRINT_CHANCE_VALUE 10 +#define SPRINT_CHANCE_VALUE_DARKNESS 50 + +#define GRENADE_PULL_MAX_DISTANCE 256.0f + +#define ZOMBINE_MAX_GRENADES 1 + +int ACT_ZOMBINE_GRENADE_PULL; +int ACT_ZOMBINE_GRENADE_WALK; +int ACT_ZOMBINE_GRENADE_RUN; +int ACT_ZOMBINE_GRENADE_IDLE; +int ACT_ZOMBINE_ATTACK_FAST; +int ACT_ZOMBINE_GRENADE_FLINCH_BACK; +int ACT_ZOMBINE_GRENADE_FLINCH_FRONT; +int ACT_ZOMBINE_GRENADE_FLINCH_WEST; +int ACT_ZOMBINE_GRENADE_FLINCH_EAST; + +int AE_ZOMBINE_PULLPIN; + +extern bool IsAlyxInDarknessMode(); + +ConVar sk_zombie_soldier_health( "sk_zombie_soldier_health","0"); + +float g_flZombineGrenadeTimes = 0; + +class CNPC_Zombine : public CAI_BlendingHost<CNPC_BaseZombie>, public CDefaultPlayerPickupVPhysics +{ + DECLARE_DATADESC(); + DECLARE_CLASS( CNPC_Zombine, CAI_BlendingHost<CNPC_BaseZombie> ); + +public: + + void Spawn( void ); + void Precache( void ); + + void SetZombieModel( void ); + + virtual void PrescheduleThink( void ); + virtual int SelectSchedule( void ); + virtual void BuildScheduleTestBits( void ); + + virtual void HandleAnimEvent( animevent_t *pEvent ); + + virtual const char *GetLegsModel( void ); + virtual const char *GetTorsoModel( void ); + virtual const char *GetHeadcrabClassname( void ); + virtual const char *GetHeadcrabModel( void ); + + virtual void PainSound( const CTakeDamageInfo &info ); + virtual void DeathSound( const CTakeDamageInfo &info ); + virtual void AlertSound( void ); + virtual void IdleSound( void ); + virtual void AttackSound( void ); + virtual void AttackHitSound( void ); + virtual void AttackMissSound( void ); + virtual void FootstepSound( bool fRightFoot ); + virtual void FootscuffSound( bool fRightFoot ); + virtual void MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize ); + + virtual void Event_Killed( const CTakeDamageInfo &info ); + virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ); + virtual void RunTask( const Task_t *pTask ); + virtual int MeleeAttack1Conditions ( float flDot, float flDist ); + + virtual bool ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold ); + + virtual void OnScheduleChange ( void ); + virtual bool CanRunAScriptedNPCInteraction( bool bForced ); + + void GatherGrenadeConditions( void ); + + virtual Activity NPC_TranslateActivity( Activity baseAct ); + + const char *GetMoanSound( int nSound ); + + bool AllowedToSprint( void ); + void Sprint( bool bMadSprint = false ); + void StopSprint( void ); + + void DropGrenade( Vector vDir ); + + bool IsSprinting( void ) { return m_flSprintTime > gpGlobals->curtime; } + bool HasGrenade( void ) { return m_hGrenade != NULL; } + + int TranslateSchedule( int scheduleType ); + + void InputStartSprint ( inputdata_t &inputdata ); + void InputPullGrenade ( inputdata_t &inputdata ); + + virtual CBaseEntity *OnFailedPhysGunPickup ( Vector vPhysgunPos ); + + //Called when we want to let go of a grenade and let the physcannon pick it up. + void ReleaseGrenade( Vector vPhysgunPos ); + + virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt ); + + enum + { + COND_ZOMBINE_GRENADE = LAST_BASE_ZOMBIE_CONDITION, + }; + + enum + { + SCHED_ZOMBINE_PULL_GRENADE = LAST_BASE_ZOMBIE_SCHEDULE, + }; + +public: + DEFINE_CUSTOM_AI; + +private: + + float m_flSprintTime; + float m_flSprintRestTime; + + float m_flSuperFastAttackTime; + float m_flGrenadePullTime; + + int m_iGrenadeCount; + + EHANDLE m_hGrenade; + +protected: + static const char *pMoanSounds[]; + +}; + +LINK_ENTITY_TO_CLASS( npc_zombine, CNPC_Zombine ); + +BEGIN_DATADESC( CNPC_Zombine ) + DEFINE_FIELD( m_flSprintTime, FIELD_TIME ), + DEFINE_FIELD( m_flSprintRestTime, FIELD_TIME ), + DEFINE_FIELD( m_flSuperFastAttackTime, FIELD_TIME ), + DEFINE_FIELD( m_hGrenade, FIELD_EHANDLE ), + DEFINE_FIELD( m_flGrenadePullTime, FIELD_TIME ), + DEFINE_FIELD( m_iGrenadeCount, FIELD_INTEGER ), + DEFINE_INPUTFUNC( FIELD_VOID, "StartSprint", InputStartSprint ), + DEFINE_INPUTFUNC( FIELD_VOID, "PullGrenade", InputPullGrenade ), +END_DATADESC() + +//--------------------------------------------------------- +//--------------------------------------------------------- +const char *CNPC_Zombine::pMoanSounds[] = +{ + "ATV_engine_null", +}; + +void CNPC_Zombine::Spawn( void ) +{ + Precache(); + + m_fIsTorso = false; + m_fIsHeadless = false; + +#ifdef HL2_EPISODIC + SetBloodColor( BLOOD_COLOR_ZOMBIE ); +#else + SetBloodColor( BLOOD_COLOR_GREEN ); +#endif // HL2_EPISODIC + + m_iHealth = sk_zombie_soldier_health.GetFloat(); + SetMaxHealth( m_iHealth ); + + m_flFieldOfView = 0.2; + + CapabilitiesClear(); + + BaseClass::Spawn(); + + m_flSprintTime = 0.0f; + m_flSprintRestTime = 0.0f; + + m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 1.0, 4.0 ); + + g_flZombineGrenadeTimes = gpGlobals->curtime; + m_flGrenadePullTime = gpGlobals->curtime; + + m_iGrenadeCount = ZOMBINE_MAX_GRENADES; +} + +void CNPC_Zombine::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheModel( "models/zombie/zombie_soldier.mdl" ); + + PrecacheScriptSound( "Zombie.FootstepRight" ); + PrecacheScriptSound( "Zombie.FootstepLeft" ); + PrecacheScriptSound( "Zombine.ScuffRight" ); + PrecacheScriptSound( "Zombine.ScuffLeft" ); + PrecacheScriptSound( "Zombie.AttackHit" ); + PrecacheScriptSound( "Zombie.AttackMiss" ); + PrecacheScriptSound( "Zombine.Pain" ); + PrecacheScriptSound( "Zombine.Die" ); + PrecacheScriptSound( "Zombine.Alert" ); + PrecacheScriptSound( "Zombine.Idle" ); + PrecacheScriptSound( "Zombine.ReadyGrenade" ); + + PrecacheScriptSound( "ATV_engine_null" ); + PrecacheScriptSound( "Zombine.Charge" ); + PrecacheScriptSound( "Zombie.Attack" ); +} + +void CNPC_Zombine::SetZombieModel( void ) +{ + SetModel( "models/zombie/zombie_soldier.mdl" ); + SetHullType( HULL_HUMAN ); + + SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless ); + + SetHullSizeNormal( true ); + SetDefaultEyeOffset(); + SetActivity( ACT_IDLE ); +} + +void CNPC_Zombine::PrescheduleThink( void ) +{ + GatherGrenadeConditions(); + + if( gpGlobals->curtime > m_flNextMoanSound ) + { + if( CanPlayMoanSound() ) + { + // Classic guy idles instead of moans. + IdleSound(); + + m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 10.0, 15.0 ); + } + else + { + m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 2.5, 5.0 ); + } + } + + if ( HasGrenade () ) + { + CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetSmoothedVelocity() * 0.5f , 256, 0.1, this, SOUNDENT_CHANNEL_ZOMBINE_GRENADE ); + + if( IsSprinting() && GetEnemy() && GetEnemy()->Classify() == CLASS_PLAYER_ALLY_VITAL && HasCondition( COND_SEE_ENEMY ) ) + { + if( GetAbsOrigin().DistToSqr(GetEnemy()->GetAbsOrigin()) < Square( 144 ) ) + { + StopSprint(); + } + } + } + + BaseClass::PrescheduleThink(); +} + +void CNPC_Zombine::OnScheduleChange( void ) +{ + if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) && IsSprinting() == true ) + { + m_flSuperFastAttackTime = gpGlobals->curtime + 1.0f; + } + + BaseClass::OnScheduleChange(); +} +bool CNPC_Zombine::CanRunAScriptedNPCInteraction( bool bForced ) +{ + if ( HasGrenade() == true ) + return false; + + return BaseClass::CanRunAScriptedNPCInteraction( bForced ); +} + +int CNPC_Zombine::SelectSchedule( void ) +{ + if ( GetHealth() <= 0 ) + return BaseClass::SelectSchedule(); + + if ( HasCondition( COND_ZOMBINE_GRENADE ) ) + { + ClearCondition( COND_ZOMBINE_GRENADE ); + + return SCHED_ZOMBINE_PULL_GRENADE; + } + + return BaseClass::SelectSchedule(); +} + +void CNPC_Zombine::BuildScheduleTestBits( void ) +{ + BaseClass::BuildScheduleTestBits(); + + SetCustomInterruptCondition( COND_ZOMBINE_GRENADE ); +} + +Activity CNPC_Zombine::NPC_TranslateActivity( Activity baseAct ) +{ + if ( baseAct == ACT_MELEE_ATTACK1 ) + { + if ( m_flSuperFastAttackTime > gpGlobals->curtime || HasGrenade() ) + { + return (Activity)ACT_ZOMBINE_ATTACK_FAST; + } + } + + if ( baseAct == ACT_IDLE ) + { + if ( HasGrenade() ) + { + return (Activity)ACT_ZOMBINE_GRENADE_IDLE; + } + } + + return BaseClass::NPC_TranslateActivity( baseAct ); +} + +int CNPC_Zombine::MeleeAttack1Conditions ( float flDot, float flDist ) +{ + int iBase = BaseClass::MeleeAttack1Conditions( flDot, flDist ); + + if( HasGrenade() ) + { + //Adrian: stop spriting if we get close enough to melee and we have a grenade + //this gives NPCs time to move away from you (before it was almost impossible cause of the high sprint speed) + if ( iBase == COND_CAN_MELEE_ATTACK1 ) + { + StopSprint(); + } + } + + return iBase; +} + +void CNPC_Zombine::GatherGrenadeConditions( void ) +{ + if ( m_iGrenadeCount <= 0 ) + return; + + if ( g_flZombineGrenadeTimes > gpGlobals->curtime ) + return; + + if ( m_flGrenadePullTime > gpGlobals->curtime ) + return; + + if ( m_flSuperFastAttackTime >= gpGlobals->curtime ) + return; + + if ( HasGrenade() ) + return; + + if ( GetEnemy() == NULL ) + return; + + if ( FVisible( GetEnemy() ) == false ) + return; + + if ( IsSprinting() ) + return; + + if ( IsOnFire() ) + return; + + if ( IsRunningDynamicInteraction() == true ) + return; + + if ( m_ActBusyBehavior.IsActive() ) + return; + + CBasePlayer *pPlayer = AI_GetSinglePlayer(); + + if ( pPlayer && pPlayer->FVisible( this ) ) + { + float flLengthToPlayer = (pPlayer->GetAbsOrigin() - GetAbsOrigin()).Length(); + float flLengthToEnemy = flLengthToPlayer; + + if ( pPlayer != GetEnemy() ) + { + flLengthToEnemy = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin()).Length(); + } + + if ( flLengthToPlayer <= GRENADE_PULL_MAX_DISTANCE && flLengthToEnemy <= GRENADE_PULL_MAX_DISTANCE ) + { + float flPullChance = 1.0f - ( flLengthToEnemy / GRENADE_PULL_MAX_DISTANCE ); + m_flGrenadePullTime = gpGlobals->curtime + 0.5f; + + if ( flPullChance >= random->RandomFloat( 0.0f, 1.0f ) ) + { + g_flZombineGrenadeTimes = gpGlobals->curtime + 10.0f; + SetCondition( COND_ZOMBINE_GRENADE ); + } + } + } +} + +int CNPC_Zombine::TranslateSchedule( int scheduleType ) +{ + return BaseClass::TranslateSchedule( scheduleType ); +} + +void CNPC_Zombine::DropGrenade( Vector vDir ) +{ + if ( m_hGrenade == NULL ) + return; + + m_hGrenade->SetParent( NULL ); + m_hGrenade->SetOwnerEntity( NULL ); + + Vector vGunPos; + QAngle angles; + GetAttachment( "grenade_attachment", vGunPos, angles ); + + IPhysicsObject *pPhysObj = m_hGrenade->VPhysicsGetObject(); + + if ( pPhysObj == NULL ) + { + m_hGrenade->SetMoveType( MOVETYPE_VPHYSICS ); + m_hGrenade->SetSolid( SOLID_VPHYSICS ); + m_hGrenade->SetCollisionGroup( COLLISION_GROUP_WEAPON ); + + m_hGrenade->CreateVPhysics(); + } + + if ( pPhysObj ) + { + pPhysObj->Wake(); + pPhysObj->SetPosition( vGunPos, angles, true ); + pPhysObj->ApplyForceCenter( vDir * 0.2f ); + + pPhysObj->RecheckCollisionFilter(); + } + + m_hGrenade = NULL; +} + +void CNPC_Zombine::Event_Killed( const CTakeDamageInfo &info ) +{ + BaseClass::Event_Killed( info ); + + if ( HasGrenade() ) + { + DropGrenade( vec3_origin ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: This is a generic function (to be implemented by sub-classes) to +// handle specific interactions between different types of characters +// (For example the barnacle grabbing an NPC) +// Input : Constant for the type of interaction +// Output : true - if sub-class has a response for the interaction +// false - if sub-class has no response +//----------------------------------------------------------------------------- +bool CNPC_Zombine::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt ) +{ + if ( interactionType == g_interactionBarnacleVictimGrab ) + { + if ( HasGrenade() ) + { + DropGrenade( vec3_origin ); + } + } + + return BaseClass::HandleInteraction( interactionType, data, sourceEnt ); +} + +void CNPC_Zombine::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) +{ + BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator ); + + //Only knock grenades off their hands if it's a player doing the damage. + if ( info.GetAttacker() && info.GetAttacker()->IsNPC() ) + return; + + if ( info.GetDamageType() & ( DMG_BULLET | DMG_CLUB ) ) + { + if ( ptr->hitgroup == HITGROUP_LEFTARM ) + { + if ( HasGrenade() ) + { + DropGrenade( info.GetDamageForce() ); + StopSprint(); + } + } + } +} + +void CNPC_Zombine::HandleAnimEvent( animevent_t *pEvent ) +{ + if ( pEvent->event == AE_ZOMBINE_PULLPIN ) + { + Vector vecStart; + QAngle angles; + GetAttachment( "grenade_attachment", vecStart, angles ); + + CBaseGrenade *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, vec3_origin, AngularImpulse( 0, 0, 0 ), this, 3.5f, true ); + + if ( pGrenade ) + { + // Move physobject to shadow + IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject(); + + if ( pPhysicsObject ) + { + pGrenade->VPhysicsDestroyObject(); + + int iAttachment = LookupAttachment( "grenade_attachment"); + + pGrenade->SetMoveType( MOVETYPE_NONE ); + pGrenade->SetSolid( SOLID_NONE ); + pGrenade->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); + + pGrenade->SetAbsOrigin( vecStart ); + pGrenade->SetAbsAngles( angles ); + + pGrenade->SetParent( this, iAttachment ); + + pGrenade->SetDamage( 200.0f ); + m_hGrenade = pGrenade; + + EmitSound( "Zombine.ReadyGrenade" ); + + // Tell player allies nearby to regard me! + CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); + CAI_BaseNPC *pNPC; + for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + pNPC = ppAIs[i]; + + if( pNPC->Classify() == CLASS_PLAYER_ALLY || ( pNPC->Classify() == CLASS_PLAYER_ALLY_VITAL && pNPC->FVisible(this) ) ) + { + int priority; + Disposition_t disposition; + + priority = pNPC->IRelationPriority(this); + disposition = pNPC->IRelationType(this); + + pNPC->AddEntityRelationship( this, disposition, priority + 1 ); + } + } + } + + m_iGrenadeCount--; + } + + return; + } + + if ( pEvent->event == AE_NPC_ATTACK_BROADCAST ) + { + if ( HasGrenade() ) + return; + } + + BaseClass::HandleAnimEvent( pEvent ); +} + +bool CNPC_Zombine::AllowedToSprint( void ) +{ + if ( IsOnFire() ) + return false; + + //If you're sprinting then there's no reason to sprint again. + if ( IsSprinting() ) + return false; + + int iChance = SPRINT_CHANCE_VALUE; + + CHL2_Player *pPlayer = dynamic_cast <CHL2_Player*> ( AI_GetSinglePlayer() ); + + if ( pPlayer ) + { + if ( HL2GameRules()->IsAlyxInDarknessMode() && pPlayer->FlashlightIsOn() == false ) + { + iChance = SPRINT_CHANCE_VALUE_DARKNESS; + } + + //Bigger chance of this happening if the player is not looking at the zombie + if ( pPlayer->FInViewCone( this ) == false ) + { + iChance *= 2; + } + } + + if ( HasGrenade() ) + { + iChance *= 4; + } + + //Below 25% health they'll always sprint + if ( ( GetHealth() > GetMaxHealth() * 0.5f ) ) + { + if ( IsStrategySlotRangeOccupied( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 ) == true ) + return false; + + if ( random->RandomInt( 0, 100 ) > iChance ) + return false; + + if ( m_flSprintRestTime > gpGlobals->curtime ) + return false; + } + + float flLength = ( GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() ).Length(); + + if ( flLength > MAX_SPRINT_DISTANCE ) + return false; + + return true; +} + +void CNPC_Zombine::StopSprint( void ) +{ + GetNavigator()->SetMovementActivity( ACT_WALK ); + + m_flSprintTime = gpGlobals->curtime; + m_flSprintRestTime = m_flSprintTime + random->RandomFloat( 2.5f, 5.0f ); +} + +void CNPC_Zombine::Sprint( bool bMadSprint ) +{ + if ( IsSprinting() ) + return; + + OccupyStrategySlotRange( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 ); + GetNavigator()->SetMovementActivity( ACT_RUN ); + + float flSprintTime = random->RandomFloat( MIN_SPRINT_TIME, MAX_SPRINT_TIME ); + + //If holding a grenade then sprint until it blows up. + if ( HasGrenade() || bMadSprint == true ) + { + flSprintTime = 9999; + } + + m_flSprintTime = gpGlobals->curtime + flSprintTime; + + //Don't sprint for this long after I'm done with this sprint run. + m_flSprintRestTime = m_flSprintTime + random->RandomFloat( 2.5f, 5.0f ); + + EmitSound( "Zombine.Charge" ); +} + +void CNPC_Zombine::RunTask( const Task_t *pTask ) +{ + switch ( pTask->iTask ) + { + case TASK_WAIT_FOR_MOVEMENT_STEP: + case TASK_WAIT_FOR_MOVEMENT: + { + BaseClass::RunTask( pTask ); + + if ( IsOnFire() && IsSprinting() ) + { + StopSprint(); + } + + //Only do this if I have an enemy + if ( GetEnemy() ) + { + if ( AllowedToSprint() == true ) + { + Sprint( ( GetHealth() <= GetMaxHealth() * 0.5f ) ); + return; + } + + if ( HasGrenade() ) + { + if ( IsSprinting() ) + { + GetNavigator()->SetMovementActivity( (Activity)ACT_ZOMBINE_GRENADE_RUN ); + } + else + { + GetNavigator()->SetMovementActivity( (Activity)ACT_ZOMBINE_GRENADE_WALK ); + } + + return; + } + + if ( GetNavigator()->GetMovementActivity() != ACT_WALK ) + { + if ( IsSprinting() == false ) + { + GetNavigator()->SetMovementActivity( ACT_WALK ); + } + } + } + else + { + GetNavigator()->SetMovementActivity( ACT_WALK ); + } + + break; + } + default: + { + BaseClass::RunTask( pTask ); + break; + } + } +} + +void CNPC_Zombine::InputStartSprint ( inputdata_t &inputdata ) +{ + Sprint(); +} + +void CNPC_Zombine::InputPullGrenade ( inputdata_t &inputdata ) +{ + g_flZombineGrenadeTimes = gpGlobals->curtime + 5.0f; + SetCondition( COND_ZOMBINE_GRENADE ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a moan sound for this class of zombie. +//----------------------------------------------------------------------------- +const char *CNPC_Zombine::GetMoanSound( int nSound ) +{ + return pMoanSounds[ nSound % ARRAYSIZE( pMoanSounds ) ]; +} + +//----------------------------------------------------------------------------- +// Purpose: Sound of a footstep +//----------------------------------------------------------------------------- +void CNPC_Zombine::FootstepSound( bool fRightFoot ) +{ + if( fRightFoot ) + { + EmitSound( "Zombie.FootstepRight" ); + } + else + { + EmitSound( "Zombie.FootstepLeft" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Overloaded so that explosions don't split the zombine in twain. +//----------------------------------------------------------------------------- +bool CNPC_Zombine::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold ) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Sound of a foot sliding/scraping +//----------------------------------------------------------------------------- +void CNPC_Zombine::FootscuffSound( bool fRightFoot ) +{ + if( fRightFoot ) + { + EmitSound( "Zombine.ScuffRight" ); + } + else + { + EmitSound( "Zombine.ScuffLeft" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Play a random attack hit sound +//----------------------------------------------------------------------------- +void CNPC_Zombine::AttackHitSound( void ) +{ + EmitSound( "Zombie.AttackHit" ); +} + +//----------------------------------------------------------------------------- +// Purpose: Play a random attack miss sound +//----------------------------------------------------------------------------- +void CNPC_Zombine::AttackMissSound( void ) +{ + // Play a random attack miss sound + EmitSound( "Zombie.AttackMiss" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Zombine::PainSound( const CTakeDamageInfo &info ) +{ + // We're constantly taking damage when we are on fire. Don't make all those noises! + if ( IsOnFire() ) + { + return; + } + + EmitSound( "Zombine.Pain" ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_Zombine::DeathSound( const CTakeDamageInfo &info ) +{ + EmitSound( "Zombine.Die" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Zombine::AlertSound( void ) +{ + EmitSound( "Zombine.Alert" ); + + // Don't let a moan sound cut off the alert sound. + m_flNextMoanSound += random->RandomFloat( 2.0, 4.0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Play a random idle sound. +//----------------------------------------------------------------------------- +void CNPC_Zombine::IdleSound( void ) +{ + if( GetState() == NPC_STATE_IDLE && random->RandomFloat( 0, 1 ) == 0 ) + { + // Moan infrequently in IDLE state. + return; + } + + if( IsSlumped() ) + { + // Sleeping zombies are quiet. + return; + } + + EmitSound( "Zombine.Idle" ); + MakeAISpookySound( 360.0f ); +} + +//----------------------------------------------------------------------------- +// Purpose: Play a random attack sound. +//----------------------------------------------------------------------------- +void CNPC_Zombine::AttackSound( void ) +{ + +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CNPC_Zombine::GetHeadcrabModel( void ) +{ + return "models/headcrabclassic.mdl"; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CNPC_Zombine::GetLegsModel( void ) +{ + return "models/zombie/zombie_soldier_legs.mdl"; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +const char *CNPC_Zombine::GetTorsoModel( void ) +{ + return "models/zombie/zombie_soldier_torso.mdl"; +} + +//--------------------------------------------------------- +// Classic zombie only uses moan sound if on fire. +//--------------------------------------------------------- +void CNPC_Zombine::MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize ) +{ + if( IsOnFire() ) + { + BaseClass::MoanSound( pEnvelope, iEnvelopeSize ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns the classname (ie "npc_headcrab") to spawn when our headcrab bails. +//----------------------------------------------------------------------------- +const char *CNPC_Zombine::GetHeadcrabClassname( void ) +{ + return "npc_headcrab"; +} + +void CNPC_Zombine::ReleaseGrenade( Vector vPhysgunPos ) +{ + if ( HasGrenade() == false ) + return; + + Vector vDir = vPhysgunPos - m_hGrenade->GetAbsOrigin(); + VectorNormalize( vDir ); + + Activity aActivity; + + Vector vForward, vRight; + GetVectors( &vForward, &vRight, NULL ); + + float flDotForward = DotProduct( vForward, vDir ); + float flDotRight = DotProduct( vRight, vDir ); + + bool bNegativeForward = false; + bool bNegativeRight = false; + + if ( flDotForward < 0.0f ) + { + bNegativeForward = true; + flDotForward = flDotForward * -1; + } + + if ( flDotRight < 0.0f ) + { + bNegativeRight = true; + flDotRight = flDotRight * -1; + } + + if ( flDotRight > flDotForward ) + { + if ( bNegativeRight == true ) + aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_WEST; + else + aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_EAST; + } + else + { + if ( bNegativeForward == true ) + aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_BACK; + else + aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_FRONT; + } + + AddGesture( aActivity ); + + DropGrenade( vec3_origin ); + + if ( IsSprinting() ) + { + StopSprint(); + } + else + { + Sprint(); + } +} + +CBaseEntity *CNPC_Zombine::OnFailedPhysGunPickup( Vector vPhysgunPos ) +{ + CBaseEntity *pGrenade = m_hGrenade; + ReleaseGrenade( vPhysgunPos ); + return pGrenade; +} + +//----------------------------------------------------------------------------- +// +// Schedules +// +//----------------------------------------------------------------------------- + +AI_BEGIN_CUSTOM_NPC( npc_zombine, CNPC_Zombine ) + + //Squad slots + DECLARE_SQUADSLOT( SQUAD_SLOT_ZOMBINE_SPRINT1 ) + DECLARE_SQUADSLOT( SQUAD_SLOT_ZOMBINE_SPRINT2 ) + + DECLARE_CONDITION( COND_ZOMBINE_GRENADE ) + + DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_PULL ) + DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_WALK ) + DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_RUN ) + DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_IDLE ) + DECLARE_ACTIVITY( ACT_ZOMBINE_ATTACK_FAST ) + DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_BACK ) + DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_FRONT ) + DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_WEST) + DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_EAST ) + + DECLARE_ANIMEVENT( AE_ZOMBINE_PULLPIN ) + + + DEFINE_SCHEDULE + ( + SCHED_ZOMBINE_PULL_GRENADE, + + " Tasks" + " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ZOMBINE_GRENADE_PULL" + + + " Interrupts" + + ) + +AI_END_CUSTOM_NPC() + + + |