summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_headcrab.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_headcrab.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/hl1/hl1_npc_headcrab.cpp')
-rw-r--r--game/server/hl1/hl1_npc_headcrab.cpp698
1 files changed, 698 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_headcrab.cpp b/game/server/hl1/hl1_npc_headcrab.cpp
new file mode 100644
index 0000000..ed41a93
--- /dev/null
+++ b/game/server/hl1/hl1_npc_headcrab.cpp
@@ -0,0 +1,698 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Implements the headcrab, a tiny, jumpy alien parasite.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "game.h"
+#include "ai_default.h"
+#include "ai_schedule.h"
+#include "ai_hull.h"
+#include "ai_route.h"
+#include "ai_motor.h"
+#include "npcevent.h"
+#include "hl1_npc_headcrab.h"
+#include "gib.h"
+//#include "AI_Interactions.h"
+#include "ndebugoverlay.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "movevars_shared.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+
+extern void ClearMultiDamage(void);
+extern void ApplyMultiDamage( void );
+
+ConVar sk_headcrab_health( "sk_headcrab_health","20");
+ConVar sk_headcrab_dmg_bite( "sk_headcrab_dmg_bite","10");
+
+#define CRAB_ATTN_IDLE (float)1.5
+#define HEADCRAB_GUTS_GIB_COUNT 1
+#define HEADCRAB_LEGS_GIB_COUNT 3
+#define HEADCRAB_ALL_GIB_COUNT 5
+
+#define HEADCRAB_MAX_JUMP_DIST 256
+
+#define HEADCRAB_RUNMODE_ACCELERATE 1
+#define HEADCRAB_RUNMODE_IDLE 2
+#define HEADCRAB_RUNMODE_DECELERATE 3
+#define HEADCRAB_RUNMODE_FULLSPEED 4
+#define HEADCRAB_RUNMODE_PAUSE 5
+
+#define HEADCRAB_RUN_MINSPEED 0.5
+#define HEADCRAB_RUN_MAXSPEED 1.0
+
+#define HC_AE_JUMPATTACK ( 2 )
+
+BEGIN_DATADESC( CNPC_Headcrab )
+ // m_nGibCount - don't save
+
+ // Function Pointers
+ DEFINE_ENTITYFUNC( LeapTouch ),
+ DEFINE_FIELD( m_vecJumpVel, FIELD_VECTOR ),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( monster_headcrab, CNPC_Headcrab );
+
+
+enum
+{
+ SCHED_HEADCRAB_RANGE_ATTACK1 = LAST_SHARED_SCHEDULE,
+ SCHED_FAST_HEADCRAB_RANGE_ATTACK1,
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::Spawn( void )
+{
+ Precache();
+
+ SetRenderColor( 255, 255, 255, 255 );
+
+ SetModel( "models/headcrab.mdl" );
+ m_iHealth = sk_headcrab_health.GetFloat();
+
+ SetHullType(HULL_TINY);
+ SetHullSizeNormal();
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+ SetMoveType( MOVETYPE_STEP );
+ SetViewOffset( Vector(6, 0, 11) ); // Position of the eyes relative to NPC's origin.
+
+ m_bloodColor = BLOOD_COLOR_GREEN;
+ m_flFieldOfView = 0.5;
+ m_NPCState = NPC_STATE_NONE;
+ m_nGibCount = HEADCRAB_ALL_GIB_COUNT;
+
+ CapabilitiesClear();
+ CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 );
+
+ NPCInit();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::Precache( void )
+{
+ PrecacheModel( "models/headcrab.mdl" );
+// PrecacheModel( "models/hc_squashed01.mdl" );
+// PrecacheModel( "models/gibs/hc_gibs.mdl" );
+
+ PrecacheScriptSound( "Headcrab.Bite" );
+ PrecacheScriptSound( "Headcrab.Attack" );
+ PrecacheScriptSound( "Headcrab.Idle" );
+ PrecacheScriptSound( "Headcrab.Die" );
+ PrecacheScriptSound( "Headcrab.Alert" );
+ PrecacheScriptSound( "Headcrab.Pain" );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::IdleSound()
+{
+ HeadCrabSound( "Headcrab.Idle" );
+}
+
+
+void CNPC_Headcrab::AlertSound()
+{
+ HeadCrabSound( "Headcrab.Alert" );
+}
+
+
+void CNPC_Headcrab::PainSound( const CTakeDamageInfo &info )
+{
+ HeadCrabSound( "Headcrab.Pain" );
+}
+
+
+void CNPC_Headcrab::DeathSound( const CTakeDamageInfo &info )
+{
+ HeadCrabSound( "Headcrab.Die" );
+}
+
+void CNPC_Headcrab::HeadCrabSound( const char *pchSound )
+{
+ CPASAttenuationFilter filter( this, ATTN_IDLE );
+
+ CSoundParameters params;
+ if ( GetParametersForSound( pchSound, params, NULL ) )
+ {
+ EmitSound_t ep( params );
+
+ ep.m_flVolume = GetSoundVolume();
+ ep.m_nPitch = GetVoicePitch();
+
+ EmitSound( filter, entindex(), ep );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pTask -
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::StartTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ case TASK_RANGE_ATTACK1:
+ {
+ SetIdealActivity( ACT_RANGE_ATTACK1 );
+ SetTouch( &CNPC_Headcrab::LeapTouch );
+ break;
+ }
+
+ default:
+ {
+ BaseClass::StartTask( pTask );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pTask -
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::RunTask( const Task_t *pTask )
+{
+ switch ( pTask->iTask )
+ {
+ case TASK_RANGE_ATTACK1:
+ case TASK_RANGE_ATTACK2:
+ {
+ if ( IsSequenceFinished() )
+ {
+ TaskComplete();
+ SetTouch( NULL );
+ SetIdealActivity( ACT_IDLE );
+ }
+ break;
+ }
+
+ default:
+ {
+ CAI_BaseNPC::RunTask( pTask );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+int CNPC_Headcrab::SelectSchedule( void )
+{
+ switch ( m_NPCState )
+ {
+ case NPC_STATE_ALERT:
+ {
+ if (HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ))
+ {
+ if ( fabs( GetMotor()->DeltaIdealYaw() ) < ( 1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction
+ {
+ return SCHED_TAKE_COVER_FROM_ORIGIN;
+ }
+ else if ( SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 )
+ {
+ return SCHED_SMALL_FLINCH;
+ }
+ }
+ else if (HasCondition( COND_HEAR_DANGER ) ||
+ HasCondition( COND_HEAR_PLAYER ) ||
+ HasCondition( COND_HEAR_WORLD ) ||
+ HasCondition( COND_HEAR_COMBAT ))
+ {
+ return SCHED_ALERT_FACE_BESTSOUND;
+ }
+ else
+ {
+ return SCHED_PATROL_WALK;
+ }
+ break;
+ }
+ }
+
+ // no special cases here, call the base class
+ return BaseClass::SelectSchedule();
+}
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CNPC_Headcrab::Touch( CBaseEntity *pOther )
+{
+ // If someone has smacked me into a wall then gib!
+/* if (m_NPCState == NPC_STATE_DEAD)
+ {
+ if (GetAbsVelocity().Length() > 250)
+ {
+ trace_t tr;
+ Vector vecDir = GetAbsVelocity();
+ VectorNormalize(vecDir);
+ UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vecDir * 100,
+ MASK_SOLID_BRUSHONLY, pev, COLLISION_GROUP_NONE, &tr);
+ float dotPr = DotProduct(vecDir,tr.plane.normal);
+ if ((tr.fraction != 1.0) &&
+ (dotPr < -0.8) )
+ {
+ Event_Gibbed();
+ // Throw headcrab guts
+ CGib::SpawnSpecificGibs( this, HEADCRAB_GUTS_GIB_COUNT, 300, 400, "models/gibs/hc_gibs.mdl");
+ }
+
+ }
+ }*/
+ BaseClass::Touch(pOther);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pevInflictor -
+// pevAttacker -
+// flDamage -
+// bitsDamageType -
+// Output :
+//-----------------------------------------------------------------------------
+int CNPC_Headcrab::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
+{
+ CTakeDamageInfo info = inputInfo;
+
+ //
+ // Don't take any acid damage.
+ //
+ if ( info.GetDamageType() & DMG_ACID )
+ {
+ return 0;
+ }
+
+ return BaseClass::OnTakeDamage_Alive( info );
+}
+
+float CNPC_Headcrab::GetDamageAmount( void )
+{
+ return sk_headcrab_dmg_bite.GetFloat();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : Type -
+// Output : CAI_Schedule *
+//-----------------------------------------------------------------------------
+int CNPC_Headcrab::TranslateSchedule( int scheduleType )
+{
+ switch( scheduleType )
+ {
+ case SCHED_RANGE_ATTACK1:
+ return SCHED_HEADCRAB_RANGE_ATTACK1;
+
+ case SCHED_FAIL_TAKE_COVER:
+ return SCHED_ALERT_FACE;
+ }
+
+ return BaseClass::TranslateSchedule( scheduleType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::PrescheduleThink( void )
+{
+ BaseClass::PrescheduleThink();
+
+ //
+ // Make the crab coo a little bit in combat state.
+ //
+ if (( m_NPCState == NPC_STATE_COMBAT ) && ( random->RandomFloat( 0, 5 ) < 0.1 ))
+ {
+ IdleSound();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: For innate melee attack
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+int CNPC_Headcrab::RangeAttack1Conditions ( float flDot, float flDist )
+{
+ if ( gpGlobals->curtime < m_flNextAttack )
+ {
+ return( 0 );
+ }
+
+ if ( !(GetFlags() & FL_ONGROUND) )
+ {
+ return( 0 );
+ }
+
+ if ( flDist > 256 )
+ {
+ return( COND_TOO_FAR_TO_ATTACK );
+ }
+ else if ( flDot < 0.65 )
+ {
+ return( COND_NOT_FACING_ATTACK );
+ }
+
+ return( COND_CAN_RANGE_ATTACK1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Indicates this monster's place in the relationship table.
+// Output :
+//-----------------------------------------------------------------------------
+Class_T CNPC_Headcrab::Classify( void )
+{
+ return CLASS_ALIEN_PREY;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the real center of the monster. The bounding box is much larger
+// than the actual creature so this is needed for targetting.
+// Output : Vector
+//-----------------------------------------------------------------------------
+Vector CNPC_Headcrab::Center( void )
+{
+ return Vector( GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z + 6 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &posSrc -
+// Output : Vector
+//-----------------------------------------------------------------------------
+Vector CNPC_Headcrab::BodyTarget( const Vector &posSrc, bool bNoisy )
+{
+ return( Center() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+float CNPC_Headcrab::MaxYawSpeed ( void )
+{
+ switch ( GetActivity() )
+ {
+ case ACT_IDLE:
+ return 30;
+ break;
+
+ case ACT_RUN:
+ case ACT_WALK:
+ return 20;
+ break;
+
+ case ACT_TURN_LEFT:
+ case ACT_TURN_RIGHT:
+ return 15;
+ break;
+
+ case ACT_RANGE_ATTACK1:
+ return 30;
+ break;
+
+ default:
+ return 30;
+ break;
+ }
+
+ return BaseClass::MaxYawSpeed();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: LeapTouch - this is the headcrab's touch function when it is in the air.
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::LeapTouch( CBaseEntity *pOther )
+{
+ if ( pOther->Classify() == Classify() )
+ {
+ return;
+ }
+
+ // Don't hit if back on ground
+ if ( !(GetFlags() & FL_ONGROUND) && ( pOther->IsNPC() || pOther->IsPlayer() ) )
+ {
+ BiteSound();
+ TouchDamage( pOther );
+ }
+
+ SetTouch( NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make the sound of this headcrab chomping a target.
+// Input :
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::BiteSound( void )
+{
+ HeadCrabSound( "Headcrab.Bite" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deal the damage from the headcrab's touch attack.
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::TouchDamage( CBaseEntity *pOther )
+{
+ CTakeDamageInfo info( this, this, GetDamageAmount(), DMG_SLASH );
+ CalculateMeleeDamageForce( &info, GetAbsVelocity(), GetAbsOrigin() );
+ pOther->TakeDamage( info );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Catches the monster-specific messages that occur when tagged
+// animation frames are played.
+// Input : *pEvent -
+//-----------------------------------------------------------------------------
+void CNPC_Headcrab::HandleAnimEvent( animevent_t *pEvent )
+{
+ switch ( pEvent->event )
+ {
+ case HC_AE_JUMPATTACK:
+ {
+ SetGroundEntity( NULL );
+
+ //
+ // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
+ //
+ UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0 , 0 , 1 ));
+
+ Vector vecJumpDir;
+ CBaseEntity *pEnemy = GetEnemy();
+ if ( pEnemy )
+ {
+ Vector vecEnemyEyePos = pEnemy->EyePosition();
+
+ float gravity = GetCurrentGravity();
+ if ( gravity <= 1 )
+ {
+ gravity = 1;
+ }
+
+ //
+ // How fast does the headcrab need to travel to reach my enemy's eyes given gravity?
+ //
+ float height = ( vecEnemyEyePos.z - GetAbsOrigin().z );
+ if ( height < 16 )
+ {
+ height = 16;
+ }
+ else if ( height > 120 )
+ {
+ height = 120;
+ }
+ float speed = sqrt( 2 * gravity * height );
+ float time = speed / gravity;
+
+ //
+ // Scale the sideways velocity to get there at the right time
+ //
+ vecJumpDir = vecEnemyEyePos - GetAbsOrigin();
+ vecJumpDir = vecJumpDir / time;
+
+ //
+ // Speed to offset gravity at the desired height.
+ //
+ vecJumpDir.z = speed;
+
+ //
+ // Don't jump too far/fast.
+ //
+ float distance = vecJumpDir.Length();
+ if ( distance > 650 )
+ {
+ vecJumpDir = vecJumpDir * ( 650.0 / distance );
+ }
+ }
+ else
+ {
+ //
+ // Jump hop, don't care where.
+ //
+ Vector forward, up;
+ AngleVectors( GetAbsAngles(), &forward, NULL, &up );
+ vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
+ }
+
+ int iSound = random->RandomInt( 0 , 2 );
+ if ( iSound != 0 )
+ {
+ AttackSound();
+ }
+
+ SetAbsVelocity( vecJumpDir );
+ m_flNextAttack = gpGlobals->curtime + 2;
+ break;
+ }
+
+ default:
+ {
+ CAI_BaseNPC::HandleAnimEvent( pEvent );
+ break;
+ }
+ }
+}
+
+void CNPC_Headcrab::AttackSound( void )
+{
+ HeadCrabSound( "Headcrab.Attack" );
+}
+
+
+//------------------------------------------------------------------------------
+//
+// Schedules
+//
+//------------------------------------------------------------------------------
+
+AI_BEGIN_CUSTOM_NPC( monster_headcrab, CNPC_Headcrab )
+
+ //=========================================================
+ // > SCHED_HEADCRAB_RANGE_ATTACK1
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_HEADCRAB_RANGE_ATTACK1,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_FACE_IDEAL 0"
+ " TASK_RANGE_ATTACK1 0"
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
+ " TASK_FACE_IDEAL 0"
+ " TASK_WAIT_RANDOM 0.5"
+ " "
+ " Interrupts"
+ " COND_ENEMY_OCCLUDED"
+ " COND_NO_PRIMARY_AMMO"
+ )
+
+ //=========================================================
+ // > SCHED_FAST_HEADCRAB_RANGE_ATTACK1
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_FAST_HEADCRAB_RANGE_ATTACK1,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_FACE_IDEAL 0"
+ " TASK_RANGE_ATTACK1 0"
+ " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
+ " "
+ " Interrupts"
+ " COND_ENEMY_OCCLUDED"
+ " COND_NO_PRIMARY_AMMO"
+ )
+
+AI_END_CUSTOM_NPC()
+
+
+class CNPC_BabyCrab : public CNPC_Headcrab
+{
+ DECLARE_CLASS( CNPC_BabyCrab, CNPC_Headcrab );
+public:
+ void Spawn( void );
+ void Precache( void );
+
+ unsigned int PhysicsSolidMaskForEntity( void ) const;
+
+ int RangeAttack1Conditions ( float flDot, float flDist );
+ float MaxYawSpeed( void ){ return 120.0f; }
+ float GetDamageAmount( void );
+
+ virtual int GetVoicePitch( void ) { return PITCH_NORM + random->RandomInt( 40,50 ); }
+ virtual float GetSoundVolume( void ) { return 0.8; }
+};
+LINK_ENTITY_TO_CLASS( monster_babycrab, CNPC_BabyCrab );
+
+unsigned int CNPC_BabyCrab::PhysicsSolidMaskForEntity( void ) const
+{
+ unsigned int iMask = BaseClass::PhysicsSolidMaskForEntity();
+
+ iMask &= ~CONTENTS_MONSTERCLIP;
+
+ return iMask;
+}
+
+void CNPC_BabyCrab::Spawn( void )
+{
+ CNPC_Headcrab::Spawn();
+ SetModel( "models/baby_headcrab.mdl" );
+ m_nRenderMode = kRenderTransTexture;
+
+ SetRenderColor( 255, 255, 255, 192 );
+
+ UTIL_SetSize(this, Vector(-12, -12, 0), Vector(12, 12, 24));
+
+ m_iHealth = sk_headcrab_health.GetFloat() * 0.25; // less health than full grown
+}
+
+void CNPC_BabyCrab::Precache( void )
+{
+ PrecacheModel( "models/baby_headcrab.mdl" );
+ CNPC_Headcrab::Precache();
+}
+
+int CNPC_BabyCrab::RangeAttack1Conditions( float flDot, float flDist )
+{
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ if ( GetGroundEntity() && ( GetGroundEntity()->GetFlags() & ( FL_CLIENT | FL_NPC ) ) )
+ return COND_CAN_RANGE_ATTACK1;
+
+ // A little less accurate, but jump from closer
+ if ( flDist <= 180 && flDot >= 0.55 )
+ return COND_CAN_RANGE_ATTACK1;
+ }
+
+ return COND_NONE;
+}
+
+float CNPC_BabyCrab::GetDamageAmount( void )
+{
+ return sk_headcrab_dmg_bite.GetFloat() * 0.3;
+}