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