📄 hl1_npc_leech.cpp
字号:
#include "cbase.h"
#include "AI_Default.h"
#include "AI_Task.h"
#include "AI_Schedule.h"
#include "AI_Node.h"
#include "AI_Hull.h"
#include "AI_Hint.h"
#include "AI_Route.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "EntityList.h"
#include "activitylist.h"
#include "animation.h"
#include "basecombatweapon.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "ammodef.h"
#include "hl1_ai_basenpc.h"
#include "AI_Senses.h"
// Animation events
#define LEECH_AE_ATTACK 1
#define LEECH_AE_FLOP 2
//#define DEBUG_BEAMS 0
ConVar sk_leech_health( "sk_leech_health", "2" );
ConVar sk_leech_dmg_bite( "sk_leech_dmg_bite", "2" );
// Movement constants
#define LEECH_ACCELERATE 10
#define LEECH_CHECK_DIST 45
#define LEECH_SWIM_SPEED 50
#define LEECH_SWIM_ACCEL 80
#define LEECH_SWIM_DECEL 10
#define LEECH_TURN_RATE 90
#define LEECH_SIZEX 10
#define LEECH_FRAMETIME 0.1
class CNPC_Leech : public CHL1BaseNPC
{
DECLARE_CLASS( CNPC_Leech, CHL1BaseNPC );
public:
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
static const char *pAttackSounds[];
static const char *pAlertSounds[];
void SwimThink( void );
void DeadThink( void );
void SwitchLeechState( void );
float ObstacleDistance( CBaseEntity *pTarget );
void UpdateMotion( void );
void RecalculateWaterlevel( void );
void Touch( CBaseEntity *pOther );
void SetObjectCollisionBox( void )
{
SetAbsMins( GetLocalOrigin() + Vector(-8,-8,0) );
SetAbsMaxs( GetLocalOrigin() + Vector(8,8,2) );
}
Disposition_t IRelationType(CBaseEntity *pTarget);
void HandleAnimEvent( animevent_t *pEvent );
void AttackSound( void );
void AlertSound( void );
void Activate( void );
Class_T Classify( void ) { return CLASS_INSECT; };
void Event_Killed( const CTakeDamageInfo &info );
bool ShouldGib( const CTakeDamageInfo &info );
/* // Base entity functions
void Killed( entvars_t *pevAttacker, int iGib );
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
*/
private:
// UNDONE: Remove unused boid vars, do group behavior
float m_flTurning;// is this boid turning?
bool m_fPathBlocked;// TRUE if there is an obstacle ahead
float m_flAccelerate;
float m_obstacle;
float m_top;
float m_bottom;
float m_height;
float m_waterTime;
float m_sideTime; // Timer to randomly check clearance on sides
float m_zTime;
float m_stateTime;
float m_attackSoundTime;
Vector m_oldOrigin;
};
LINK_ENTITY_TO_CLASS( monster_leech, CNPC_Leech );
BEGIN_DATADESC( CNPC_Leech )
DEFINE_FIELD( CNPC_Leech, m_flTurning, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Leech, m_fPathBlocked, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Leech, m_flAccelerate, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Leech, m_obstacle, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Leech, m_top, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Leech, m_bottom, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Leech, m_height, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Leech, m_waterTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Leech, m_sideTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Leech, m_zTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Leech, m_stateTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Leech, m_attackSoundTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Leech, m_oldOrigin, FIELD_VECTOR ),
DEFINE_THINKFUNC( CNPC_Leech, SwimThink ),
DEFINE_THINKFUNC( CNPC_Leech, DeadThink ),
END_DATADESC()
const char *CNPC_Leech::pAttackSounds[] =
{
"leech/leech_bite1.wav",
"leech/leech_bite2.wav",
"leech/leech_bite3.wav",
};
const char *CNPC_Leech::pAlertSounds[] =
{
"leech/leech_alert1.wav",
"leech/leech_alert2.wav",
};
bool CNPC_Leech::ShouldGib( const CTakeDamageInfo &info )
{
return false;
}
void CNPC_Leech::Spawn( void )
{
Precache();
SetModel( "models/leech.mdl" );
SetHullType(HULL_TINY_CENTERED);
SetHullSizeNormal();
UTIL_SetSize( this, Vector(-1,-1,0), Vector(1,1,2));
// Don't push the minz down too much or the water check will fail because this entity is really point-sized
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_FLY );
AddFlag( FL_SWIM );
m_iHealth = sk_leech_health.GetInt();
m_flFieldOfView = -0.5; // 180 degree FOV
SetDistLook( 750 );
NPCInit();
SetThink( &CNPC_Leech::SwimThink );
SetUse( NULL );
SetTouch( NULL );
SetViewOffset( vec3_origin );
m_flTurning = 0;
m_fPathBlocked = FALSE;
SetActivity( ACT_SWIM );
SetState( NPC_STATE_IDLE );
m_stateTime = gpGlobals->curtime + random->RandomFloat( 1, 5 );
SetRenderColor( 255, 255, 255, 255 );
m_bloodColor = DONT_BLEED;
SetCollisionGroup( COLLISION_GROUP_DEBRIS );
}
void CNPC_Leech::Activate( void )
{
RecalculateWaterlevel();
BaseClass::Activate();
}
void CNPC_Leech::DeadThink( void )
{
if ( IsSequenceFinished() )
{
if ( GetActivity() == ACT_DIEFORWARD )
{
SetThink( NULL );
StopAnimation();
return;
}
else if ( GetFlags() & FL_ONGROUND )
{
AddSolidFlags( FSOLID_NOT_SOLID );
SetActivity( ACT_DIEFORWARD );
}
}
StudioFrameAdvance();
SetNextThink( gpGlobals->curtime + 0.1 );
// Apply damage velocity, but keep out of the walls
if ( GetAbsVelocity().x != 0 || GetAbsVelocity().y != 0 )
{
trace_t tr;
// Look 0.5 seconds ahead
UTIL_TraceLine( GetLocalOrigin(), GetLocalOrigin() + GetAbsVelocity() * 0.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction != 1.0)
{
Vector vVelocity = GetAbsVelocity();
vVelocity.x = 0;
vVelocity.y = 0;
SetAbsVelocity( vVelocity );
}
}
}
Disposition_t CNPC_Leech::IRelationType( CBaseEntity *pTarget )
{
if ( pTarget->IsPlayer() )
return D_HT;
return BaseClass::IRelationType( pTarget );
}
void CNPC_Leech::Touch( CBaseEntity *pOther )
{
if ( !pOther->IsPlayer() )
return;
if ( pOther == GetTouchTrace().m_pEnt )
{
if ( pOther->GetAbsVelocity() == vec3_origin )
return;
SetBaseVelocity( pOther->GetAbsVelocity() );
AddFlag( FL_BASEVELOCITY );
}
}
void CNPC_Leech::HandleAnimEvent( animevent_t *pEvent )
{
CBaseEntity *pEnemy = GetEnemy();
switch( pEvent->event )
{
case LEECH_AE_FLOP:
// Play flop sound
break;
case LEECH_AE_ATTACK:
AttackSound();
if ( pEnemy != NULL )
{
Vector dir, face;
AngleVectors( GetAbsAngles(), &face );
face.z = 0;
dir = (pEnemy->GetLocalOrigin() - GetLocalOrigin() );
dir.z = 0;
VectorNormalize( dir );
VectorNormalize( face );
if ( DotProduct(dir, face) > 0.9 ) // Only take damage if the leech is facing the prey
{
CTakeDamageInfo info( this, this, sk_leech_dmg_bite.GetInt(), DMG_SLASH );
CalculateMeleeDamageForce( &info, dir, pEnemy->GetAbsOrigin() );
pEnemy->TakeDamage( info );
}
}
m_stateTime -= 2;
break;
default:
BaseClass::HandleAnimEvent( pEvent );
break;
}
}
void CNPC_Leech::Precache( void )
{
engine->PrecacheModel("models/leech.mdl");
PRECACHE_SOUND_ARRAY(pAttackSounds);
PRECACHE_SOUND_ARRAY(pAlertSounds);
}
void CNPC_Leech::AttackSound( void )
{
if ( gpGlobals->curtime > m_attackSoundTime )
{
CPASAttenuationFilter filter( this );
enginesound->EmitSound(filter, entindex(), CHAN_VOICE, pAttackSounds[ random->RandomInt(0,ARRAYSIZE(pAttackSounds)-1) ], 1.0, ATTN_NORM, 0, PITCH_NORM );
m_attackSoundTime = gpGlobals->curtime + 0.5;
}
}
void CNPC_Leech::AlertSound( void )
{
CPASAttenuationFilter filter( this );
enginesound->EmitSound(filter, entindex(), CHAN_VOICE, pAlertSounds[ random->RandomInt(0,ARRAYSIZE(pAlertSounds)-1) ], 1.0, ATTN_NORM * 0.5, 0, PITCH_NORM );
}
void CNPC_Leech::SwitchLeechState( void )
{
m_stateTime = gpGlobals->curtime + random->RandomFloat( 3, 6 );
if ( m_NPCState == NPC_STATE_COMBAT )
{
SetEnemy ( NULL );
SetState( NPC_STATE_IDLE );
// We may be up against the player, so redo the side checks
m_sideTime = 0;
}
else
{
GetSenses()->Look( GetSenses()->GetDistLook() );
CBaseEntity *pEnemy = BestEnemy();
if ( pEnemy && pEnemy->GetWaterLevel() != 0 )
{
SetEnemy ( pEnemy );
SetState( NPC_STATE_COMBAT );
m_stateTime = gpGlobals->curtime + random->RandomFloat( 18, 25 );
AlertSound();
}
}
}
void CNPC_Leech::RecalculateWaterlevel( void )
{
// Calculate boundaries
Vector vecTest = GetLocalOrigin() - Vector(0,0,400);
trace_t tr;
UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
if ( tr.fraction != 1.0 )
m_bottom = tr.endpos.z + 1;
else
m_bottom = vecTest.z;
m_top = UTIL_WaterLevel( GetLocalOrigin(), GetLocalOrigin().z, GetLocalOrigin().z + 400 ) - 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -