You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
698 lines
17 KiB
698 lines
17 KiB
//========= 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; |
|
}
|
|
|