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.
215 lines
5.3 KiB
215 lines
5.3 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "hl1_ai_basenpc.h" |
|
#include "scripted.h" |
|
#include "soundent.h" |
|
#include "animation.h" |
|
#include "entitylist.h" |
|
#include "ai_navigator.h" |
|
#include "ai_motor.h" |
|
#include "player.h" |
|
#include "vstdlib/random.h" |
|
#include "engine/IEngineSound.h" |
|
#include "npcevent.h" |
|
|
|
#include "effect_dispatch_data.h" |
|
#include "te_effect_dispatch.h" |
|
#include "cplane.h" |
|
#include "ai_squad.h" |
|
|
|
#define HUMAN_GIBS 1 |
|
#define ALIEN_GIBS 2 |
|
|
|
//========================================================= |
|
// NoFriendlyFire - checks for possibility of friendly fire |
|
// |
|
// Builds a large box in front of the grunt and checks to see |
|
// if any squad members are in that box. |
|
//========================================================= |
|
bool CHL1BaseNPC::NoFriendlyFire( void ) |
|
{ |
|
if ( !m_pSquad ) |
|
{ |
|
return true; |
|
} |
|
|
|
CPlane backPlane; |
|
CPlane leftPlane; |
|
CPlane rightPlane; |
|
|
|
Vector vecLeftSide; |
|
Vector vecRightSide; |
|
Vector v_left; |
|
|
|
Vector vForward, vRight, vUp; |
|
QAngle vAngleToEnemy; |
|
|
|
if ( GetEnemy() != NULL ) |
|
{ |
|
//!!!BUGBUG - to fix this, the planes must be aligned to where the monster will be firing its gun, not the direction it is facing!!! |
|
VectorAngles( ( GetEnemy()->WorldSpaceCenter() - GetAbsOrigin() ), vAngleToEnemy ); |
|
|
|
AngleVectors ( vAngleToEnemy, &vForward, &vRight, &vUp ); |
|
} |
|
else |
|
{ |
|
// if there's no enemy, pretend there's a friendly in the way, so the grunt won't shoot. |
|
return false; |
|
} |
|
|
|
vecLeftSide = GetAbsOrigin() - ( vRight * ( WorldAlignSize().x * 1.5 ) ); |
|
vecRightSide = GetAbsOrigin() + ( vRight * ( WorldAlignSize().x * 1.5 ) ); |
|
v_left = vRight * -1; |
|
|
|
leftPlane.InitializePlane ( vRight, vecLeftSide ); |
|
rightPlane.InitializePlane ( v_left, vecRightSide ); |
|
backPlane.InitializePlane ( vForward, GetAbsOrigin() ); |
|
|
|
AISquadIter_t iter; |
|
for ( CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) ) |
|
{ |
|
if ( pSquadMember == NULL ) |
|
continue; |
|
|
|
if ( pSquadMember == this ) |
|
continue; |
|
|
|
if ( backPlane.PointInFront ( pSquadMember->GetAbsOrigin() ) && |
|
leftPlane.PointInFront ( pSquadMember->GetAbsOrigin() ) && |
|
rightPlane.PointInFront ( pSquadMember->GetAbsOrigin()) ) |
|
{ |
|
// this guy is in the check volume! Don't shoot! |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CHL1BaseNPC::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) |
|
{ |
|
if ( info.GetDamage() >= 1.0 && !(info.GetDamageType() & DMG_SHOCK ) ) |
|
{ |
|
UTIL_BloodSpray( ptr->endpos, vecDir, BloodColor(), 4, FX_BLOODSPRAY_ALL ); |
|
} |
|
|
|
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator ); |
|
} |
|
|
|
|
|
bool CHL1BaseNPC::ShouldGib( const CTakeDamageInfo &info ) |
|
{ |
|
if ( info.GetDamageType() & DMG_NEVERGIB ) |
|
return false; |
|
|
|
if ( ( g_pGameRules->Damage_ShouldGibCorpse( info.GetDamageType() ) && m_iHealth < GIB_HEALTH_VALUE ) || ( info.GetDamageType() & DMG_ALWAYSGIB ) ) |
|
return true; |
|
|
|
return false; |
|
|
|
} |
|
|
|
bool CHL1BaseNPC::HasHumanGibs( void ) |
|
{ |
|
Class_T myClass = Classify(); |
|
|
|
if ( myClass == CLASS_HUMAN_MILITARY || |
|
myClass == CLASS_PLAYER_ALLY || |
|
myClass == CLASS_HUMAN_PASSIVE || |
|
myClass == CLASS_PLAYER ) |
|
|
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
|
|
bool CHL1BaseNPC::HasAlienGibs( void ) |
|
{ |
|
Class_T myClass = Classify(); |
|
|
|
if ( myClass == CLASS_ALIEN_MILITARY || |
|
myClass == CLASS_ALIEN_MONSTER || |
|
myClass == CLASS_INSECT || |
|
myClass == CLASS_ALIEN_PREDATOR || |
|
myClass == CLASS_ALIEN_PREY ) |
|
|
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
|
|
void CHL1BaseNPC::Precache( void ) |
|
{ |
|
PrecacheModel( "models/gibs/agibs.mdl" ); |
|
PrecacheModel( "models/gibs/hgibs.mdl" ); |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CHL1BaseNPC::CorpseGib( const CTakeDamageInfo &info ) |
|
{ |
|
CEffectData data; |
|
|
|
data.m_vOrigin = WorldSpaceCenter(); |
|
data.m_vNormal = data.m_vOrigin - info.GetDamagePosition(); |
|
VectorNormalize( data.m_vNormal ); |
|
|
|
data.m_flScale = RemapVal( m_iHealth, 0, -500, 1, 3 ); |
|
data.m_flScale = clamp( data.m_flScale, 1, 3 ); |
|
|
|
if ( HasAlienGibs() ) |
|
data.m_nMaterial = ALIEN_GIBS; |
|
else if ( HasHumanGibs() ) |
|
data.m_nMaterial = HUMAN_GIBS; |
|
|
|
data.m_nColor = BloodColor(); |
|
|
|
DispatchEffect( "HL1Gib", data ); |
|
|
|
CSoundEnt::InsertSound( SOUND_MEAT, GetAbsOrigin(), 256, 0.5f, this ); |
|
|
|
BaseClass::CorpseGib( info ); |
|
|
|
return true; |
|
} |
|
|
|
int CHL1BaseNPC::IRelationPriority( CBaseEntity *pTarget ) |
|
{ |
|
return BaseClass::IRelationPriority( pTarget ); |
|
} |
|
|
|
void CHL1BaseNPC::EjectShell( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int iType ) |
|
{ |
|
CEffectData data; |
|
data.m_vStart = vecVelocity; |
|
data.m_vOrigin = vecOrigin; |
|
data.m_vAngles = QAngle( 0, rotation, 0 ); |
|
data.m_fFlags = iType; |
|
|
|
DispatchEffect( "HL1ShellEject", data ); |
|
} |
|
|
|
// HL1 version - never return Ragdoll as the automatic schedule at the end of a |
|
// scripted sequence |
|
int CHL1BaseNPC::SelectDeadSchedule() |
|
{ |
|
// Alread dead (by animation event maybe?) |
|
// Is it safe to set it to SCHED_NONE? |
|
if ( m_lifeState == LIFE_DEAD ) |
|
return SCHED_NONE; |
|
|
|
CleanupOnDeath(); |
|
return SCHED_DIE; |
|
}
|
|
|