source-engine/game/server/hl1/hl1_npc_zombie.cpp
2022-04-16 12:05:19 +03:00

308 lines
7.8 KiB
C++

//========= 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();
}