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.
308 lines
7.8 KiB
308 lines
7.8 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: A slow-moving, once-human headcrab victim with only melee attacks.
|
||
|
//
|
||
|
// UNDONE: Make head take 100% damage, body take 30% damage.
|
||
|
// UNDONE: Don't flinch every time you get hit.
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "game.h"
|
||
|
#include "ai_default.h"
|
||
|
#include "ai_schedule.h"
|
||
|
#include "ai_hull.h"
|
||
|
#include "ai_route.h"
|
||
|
#include "npcevent.h"
|
||
|
#include "hl1_npc_zombie.h"
|
||
|
#include "gib.h"
|
||
|
//#include "AI_Interactions.h"
|
||
|
#include "ndebugoverlay.h"
|
||
|
#include "vstdlib/random.h"
|
||
|
#include "engine/IEngineSound.h"
|
||
|
|
||
|
ConVar sk_zombie_health( "sk_zombie_health","50");
|
||
|
ConVar sk_zombie_dmg_one_slash( "sk_zombie_dmg_one_slash", "20" );
|
||
|
ConVar sk_zombie_dmg_both_slash( "sk_zombie_dmg_both_slash", "40" );
|
||
|
|
||
|
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( monster_zombie, CNPC_Zombie );
|
||
|
|
||
|
|
||
|
//=========================================================
|
||
|
// Spawn
|
||
|
//=========================================================
|
||
|
void CNPC_Zombie::Spawn()
|
||
|
{
|
||
|
Precache( );
|
||
|
|
||
|
SetModel( "models/zombie.mdl" );
|
||
|
|
||
|
SetRenderColor( 255, 255, 255, 255 );
|
||
|
|
||
|
SetHullType(HULL_HUMAN);
|
||
|
SetHullSizeNormal();
|
||
|
|
||
|
SetSolid( SOLID_BBOX );
|
||
|
AddSolidFlags( FSOLID_NOT_STANDABLE );
|
||
|
SetMoveType( MOVETYPE_STEP );
|
||
|
m_bloodColor = BLOOD_COLOR_GREEN;
|
||
|
m_iHealth = sk_zombie_health.GetFloat();
|
||
|
//pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
|
||
|
m_flFieldOfView = 0.5;
|
||
|
m_NPCState = NPC_STATE_NONE;
|
||
|
CapabilitiesClear();
|
||
|
CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP );
|
||
|
|
||
|
NPCInit();
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// Precache - precaches all resources this monster needs
|
||
|
//=========================================================
|
||
|
void CNPC_Zombie::Precache()
|
||
|
{
|
||
|
PrecacheModel( "models/zombie.mdl" );
|
||
|
|
||
|
PrecacheScriptSound( "Zombie.AttackHit" );
|
||
|
PrecacheScriptSound( "Zombie.AttackMiss" );
|
||
|
PrecacheScriptSound( "Zombie.Pain" );
|
||
|
PrecacheScriptSound( "Zombie.Idle" );
|
||
|
PrecacheScriptSound( "Zombie.Alert" );
|
||
|
PrecacheScriptSound( "Zombie.Attack" );
|
||
|
|
||
|
BaseClass::Precache();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Returns this monster's place in the relationship table.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
Class_T CNPC_Zombie::Classify( void )
|
||
|
{
|
||
|
return CLASS_ALIEN_MONSTER;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// HandleAnimEvent - catches the monster-specific messages
|
||
|
// that occur when tagged animation frames are played.
|
||
|
//=========================================================
|
||
|
void CNPC_Zombie::HandleAnimEvent( animevent_t *pEvent )
|
||
|
{
|
||
|
Vector v_forward, v_right;
|
||
|
switch( pEvent->event )
|
||
|
{
|
||
|
case ZOMBIE_AE_ATTACK_RIGHT:
|
||
|
{
|
||
|
// do stuff for this event.
|
||
|
// ALERT( at_console, "Slash right!\n" );
|
||
|
|
||
|
Vector vecMins = GetHullMins();
|
||
|
Vector vecMaxs = GetHullMaxs();
|
||
|
vecMins.z = vecMins.x;
|
||
|
vecMaxs.z = vecMaxs.x;
|
||
|
|
||
|
CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_one_slash.GetFloat(), DMG_SLASH );
|
||
|
CPASAttenuationFilter filter( this );
|
||
|
if ( pHurt )
|
||
|
{
|
||
|
if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
|
||
|
{
|
||
|
pHurt->ViewPunch( QAngle( 5, 0, 18 ) );
|
||
|
|
||
|
GetVectors( &v_forward, &v_right, NULL );
|
||
|
|
||
|
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
|
||
|
}
|
||
|
// Play a random attack hit sound
|
||
|
EmitSound( filter, entindex(), "Zombie.AttackHit" );
|
||
|
}
|
||
|
else // Play a random attack miss sound
|
||
|
EmitSound( filter, entindex(), "Zombie.AttackMiss" );
|
||
|
|
||
|
if ( random->RandomInt( 0, 1 ) )
|
||
|
AttackSound();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ZOMBIE_AE_ATTACK_LEFT:
|
||
|
{
|
||
|
// do stuff for this event.
|
||
|
// ALERT( at_console, "Slash left!\n" );
|
||
|
Vector vecMins = GetHullMins();
|
||
|
Vector vecMaxs = GetHullMaxs();
|
||
|
vecMins.z = vecMins.x;
|
||
|
vecMaxs.z = vecMaxs.x;
|
||
|
|
||
|
CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_one_slash.GetFloat(), DMG_SLASH );
|
||
|
|
||
|
CPASAttenuationFilter filter2( this );
|
||
|
if ( pHurt )
|
||
|
{
|
||
|
if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
|
||
|
{
|
||
|
pHurt->ViewPunch( QAngle ( 5, 0, -18 ) );
|
||
|
|
||
|
GetVectors( &v_forward, &v_right, NULL );
|
||
|
|
||
|
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
|
||
|
}
|
||
|
EmitSound( filter2, entindex(), "Zombie.AttackHit" );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
EmitSound( filter2, entindex(), "Zombie.AttackMiss" );
|
||
|
}
|
||
|
|
||
|
if ( random->RandomInt( 0,1 ) )
|
||
|
AttackSound();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ZOMBIE_AE_ATTACK_BOTH:
|
||
|
{
|
||
|
// do stuff for this event.
|
||
|
Vector vecMins = GetHullMins();
|
||
|
Vector vecMaxs = GetHullMaxs();
|
||
|
vecMins.z = vecMins.x;
|
||
|
vecMaxs.z = vecMaxs.x;
|
||
|
|
||
|
CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_both_slash.GetFloat(), DMG_SLASH );
|
||
|
|
||
|
|
||
|
CPASAttenuationFilter filter3( this );
|
||
|
if ( pHurt )
|
||
|
{
|
||
|
if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
|
||
|
{
|
||
|
pHurt->ViewPunch( QAngle ( 5, 0, 0 ) );
|
||
|
|
||
|
GetVectors( &v_forward, &v_right, NULL );
|
||
|
pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
|
||
|
}
|
||
|
EmitSound( filter3, entindex(), "Zombie.AttackHit" );
|
||
|
}
|
||
|
else
|
||
|
EmitSound( filter3, entindex(),"Zombie.AttackMiss" );
|
||
|
|
||
|
if ( random->RandomInt( 0,1 ) )
|
||
|
AttackSound();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
BaseClass::HandleAnimEvent( pEvent );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static float DamageForce( const Vector &size, float damage )
|
||
|
{
|
||
|
float force = damage * ((32 * 32 * 72.0) / (size.x * size.y * size.z)) * 5;
|
||
|
|
||
|
if ( force > 1000.0)
|
||
|
{
|
||
|
force = 1000.0;
|
||
|
}
|
||
|
|
||
|
return force;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : pInflictor -
|
||
|
// pAttacker -
|
||
|
// flDamage -
|
||
|
// bitsDamageType -
|
||
|
// Output : int
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CNPC_Zombie::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
|
||
|
{
|
||
|
CTakeDamageInfo info = inputInfo;
|
||
|
|
||
|
// Take 30% damage from bullets
|
||
|
if ( info.GetDamageType() == DMG_BULLET )
|
||
|
{
|
||
|
Vector vecDir = GetAbsOrigin() - info.GetInflictor()->WorldSpaceCenter();
|
||
|
VectorNormalize( vecDir );
|
||
|
float flForce = DamageForce( WorldAlignSize(), info.GetDamage() );
|
||
|
SetAbsVelocity( GetAbsVelocity() + vecDir * flForce );
|
||
|
info.ScaleDamage( 0.3f );
|
||
|
}
|
||
|
|
||
|
// HACK HACK -- until we fix this.
|
||
|
if ( IsAlive() )
|
||
|
PainSound( info );
|
||
|
|
||
|
return BaseClass::OnTakeDamage_Alive( info );
|
||
|
}
|
||
|
|
||
|
void CNPC_Zombie::PainSound( const CTakeDamageInfo &info )
|
||
|
{
|
||
|
if ( random->RandomInt(0,5) < 2)
|
||
|
{
|
||
|
CPASAttenuationFilter filter( this );
|
||
|
EmitSound( filter, entindex(), "Zombie.Pain" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CNPC_Zombie::AlertSound( void )
|
||
|
{
|
||
|
CPASAttenuationFilter filter( this );
|
||
|
EmitSound( filter, entindex(), "Zombie.Alert" );
|
||
|
}
|
||
|
|
||
|
void CNPC_Zombie::IdleSound( void )
|
||
|
{
|
||
|
// Play a random idle sound
|
||
|
CPASAttenuationFilter filter( this );
|
||
|
EmitSound( filter, entindex(), "Zombie.Idle" );
|
||
|
}
|
||
|
|
||
|
void CNPC_Zombie::AttackSound( void )
|
||
|
{
|
||
|
// Play a random attack sound
|
||
|
CPASAttenuationFilter filter( this );
|
||
|
EmitSound( filter, entindex(), "Zombie.Attack" );
|
||
|
}
|
||
|
|
||
|
int CNPC_Zombie::MeleeAttack1Conditions ( float flDot, float flDist )
|
||
|
{
|
||
|
if ( flDist > 64)
|
||
|
{
|
||
|
return COND_TOO_FAR_TO_ATTACK;
|
||
|
}
|
||
|
else if (flDot < 0.7)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
else if (GetEnemy() == NULL)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return COND_CAN_MELEE_ATTACK1;
|
||
|
}
|
||
|
|
||
|
void CNPC_Zombie::RemoveIgnoredConditions ( void )
|
||
|
{
|
||
|
if ( GetActivity() == ACT_MELEE_ATTACK1 )
|
||
|
{
|
||
|
// Nothing stops an attacking zombie.
|
||
|
ClearCondition( COND_LIGHT_DAMAGE );
|
||
|
ClearCondition( COND_HEAVY_DAMAGE );
|
||
|
}
|
||
|
|
||
|
if (( GetActivity() == ACT_SMALL_FLINCH ) || ( GetActivity() == ACT_BIG_FLINCH ))
|
||
|
{
|
||
|
if (m_flNextFlinch < gpGlobals->curtime)
|
||
|
m_flNextFlinch = gpGlobals->curtime + ZOMBIE_FLINCH_DELAY;
|
||
|
}
|
||
|
|
||
|
BaseClass::RemoveIgnoredConditions();
|
||
|
}
|