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.
473 lines
12 KiB
473 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger |
|
// events |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "basecombatcharacter.h" |
|
#include "ai_basenpc.h" |
|
#include "decals.h" |
|
#include "filters.h" |
|
#include "npc_bullseye.h" |
|
#include "collisionutils.h" |
|
#include "igamesystem.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
class CBullseyeList : public CAutoGameSystem |
|
{ |
|
public: |
|
CBullseyeList( char const *name ) : CAutoGameSystem( name ) |
|
{ |
|
} |
|
|
|
virtual void LevelShutdownPostEntity() |
|
{ |
|
Clear(); |
|
} |
|
|
|
void Clear() |
|
{ |
|
m_list.Purge(); |
|
} |
|
|
|
void AddToList( CNPC_Bullseye *pBullseye ); |
|
void RemoveFromList( CNPC_Bullseye *pBullseye ); |
|
|
|
CUtlVector< CNPC_Bullseye * > m_list; |
|
}; |
|
|
|
void CBullseyeList::AddToList( CNPC_Bullseye *pBullseye ) |
|
{ |
|
m_list.AddToTail( pBullseye ); |
|
} |
|
|
|
void CBullseyeList::RemoveFromList( CNPC_Bullseye *pBullseye ) |
|
{ |
|
int index = m_list.Find( pBullseye ); |
|
if ( index != m_list.InvalidIndex() ) |
|
{ |
|
m_list.FastRemove( index ); |
|
} |
|
} |
|
|
|
CBullseyeList g_BullseyeList( "CBullseyeList" ); |
|
|
|
int FindBullseyesInCone( CBaseEntity **pList, int listMax, const Vector &coneOrigin, const Vector &coneAxis, float coneAngleCos, float coneLength ) |
|
{ |
|
if ( listMax <= 0 ) |
|
return 0; |
|
|
|
int count = 0; |
|
|
|
for ( int i = g_BullseyeList.m_list.Count() - 1; i >= 0; --i ) |
|
{ |
|
CNPC_Bullseye *pTest = g_BullseyeList.m_list[i]; |
|
|
|
if ( IsPointInCone( pTest->GetAbsOrigin(), coneOrigin, coneAxis, coneAngleCos, coneLength ) ) |
|
{ |
|
pList[count] = pTest; |
|
count++; |
|
if ( count >= listMax ) |
|
break; |
|
} |
|
} |
|
|
|
return count; |
|
} |
|
|
|
|
|
ConVar sk_bullseye_health( "sk_bullseye_health","0"); |
|
|
|
BEGIN_DATADESC( CNPC_Bullseye ) |
|
|
|
DEFINE_FIELD( m_hPainPartner, FIELD_EHANDLE ), |
|
DEFINE_KEYFIELD( m_fAutoaimRadius, FIELD_FLOAT, "autoaimradius" ), |
|
DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "minangle" ), |
|
DEFINE_KEYFIELD( m_flMinDistValidEnemy, FIELD_FLOAT, "mindist" ), |
|
// DEFINE_FIELD( m_bPerfectAccuracy, FIELD_BOOLEAN ), // Don't save |
|
|
|
// Function Pointers |
|
DEFINE_THINKFUNC( BullseyeThink ), |
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "InputTargeted", InputTargeted ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "InputReleased", InputReleased ), |
|
// Outputs |
|
DEFINE_OUTPUT( m_OnTargeted, "OnTargeted"), |
|
DEFINE_OUTPUT( m_OnReleased, "OnReleased"), |
|
|
|
END_DATADESC() |
|
|
|
LINK_ENTITY_TO_CLASS( npc_bullseye, CNPC_Bullseye ); |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CNPC_Bullseye::CNPC_Bullseye( void ) |
|
{ |
|
m_takedamage = DAMAGE_YES; |
|
m_iHealth = sk_bullseye_health.GetFloat(); |
|
m_hPainPartner = NULL; |
|
g_BullseyeList.AddToList( this ); |
|
m_flFieldOfView = 360; |
|
m_flMinDistValidEnemy = 0; |
|
} |
|
|
|
CNPC_Bullseye::~CNPC_Bullseye( void ) |
|
{ |
|
g_BullseyeList.RemoveFromList( this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bullseye::Precache( void ) |
|
{ |
|
BaseClass::Precache(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bullseye::Spawn( void ) |
|
{ |
|
Precache(); |
|
|
|
// This is a dummy model that is never used! |
|
UTIL_SetSize(this, Vector(-16,-16,-16), Vector(16,16,16)); |
|
|
|
SetMoveType( MOVETYPE_NONE ); |
|
SetBloodColor( BLOOD_COLOR_RED ); |
|
ClearEffects(); |
|
SetGravity( 0.0 ); |
|
|
|
m_flFieldOfView = cos( DEG2RAD(m_flFieldOfView) / 2.0 ); |
|
|
|
//Got blood? |
|
if ( m_spawnflags & SF_BULLSEYE_BLEED ) |
|
{ |
|
SetBloodColor(BLOOD_COLOR_RED); |
|
} |
|
else |
|
{ |
|
SetBloodColor(DONT_BLEED); |
|
} |
|
|
|
AddFlag( FL_NPC ); |
|
AddEFlags( EFL_NO_DISSOLVE ); |
|
|
|
SetThink( &CNPC_Bullseye::BullseyeThink ); |
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
|
|
SetSolid( SOLID_BBOX ); |
|
AddSolidFlags( FSOLID_NOT_STANDABLE ); |
|
if( m_spawnflags & SF_BULLSEYE_NONSOLID ) |
|
{ |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
} |
|
|
|
if ( m_spawnflags & SF_BULLSEYE_VPHYSICSSHADOW ) |
|
{ |
|
VPhysicsInitShadow( false, false ); |
|
} |
|
|
|
if( m_spawnflags & SF_BULLSEYE_NODAMAGE ) |
|
{ |
|
m_takedamage = DAMAGE_NO; |
|
} |
|
else |
|
{ |
|
m_takedamage = DAMAGE_YES; |
|
} |
|
AddEffects( EF_NODRAW ); |
|
|
|
//Check our water level |
|
PhysicsCheckWater(); |
|
|
|
CapabilitiesAdd( bits_CAP_SIMPLE_RADIUS_DAMAGE ); |
|
|
|
m_iMaxHealth = GetHealth(); |
|
|
|
if( m_fAutoaimRadius > 0.0f ) |
|
{ |
|
// Make this an aimtarget, since it has some autoaim influence. |
|
AddFlag(FL_AIMTARGET); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bullseye::Activate( void ) |
|
{ |
|
BaseClass::Activate(); |
|
|
|
if ( m_spawnflags & SF_BULLSEYE_PERFECTACC ) |
|
{ |
|
m_bPerfectAccuracy = true; |
|
} |
|
else |
|
{ |
|
m_bPerfectAccuracy = false; |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Override so doesn't fall to ground when killed |
|
//------------------------------------------------------------------------------ |
|
void CNPC_Bullseye::Event_Killed( const CTakeDamageInfo &info ) |
|
{ |
|
BaseClass::Event_Killed( info ); |
|
|
|
if( GetParent() ) |
|
{ |
|
if( GetParent()->ClassMatches("prop_combine_ball") ) |
|
{ |
|
// If this bullseye is parented to a combine ball, explode the combine ball |
|
// and remove this bullseye. |
|
variant_t emptyVariant; |
|
GetParent()->AcceptInput( "explode", this, this, emptyVariant, 0 ); |
|
|
|
// Unhook. |
|
SetParent(NULL); |
|
|
|
UTIL_Remove(this); |
|
return; |
|
} |
|
} |
|
|
|
SetMoveType( MOVETYPE_NONE ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
UTIL_SetSize(this, vec3_origin, vec3_origin ); |
|
|
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
SetThink( &CBaseEntity::SUB_Remove ); |
|
} |
|
|
|
//------------------------------------------------------------------------------ |
|
// Purpose : Override base implimentation to let decals pass through |
|
// me onto the surface beneath |
|
// Input : |
|
// Output : |
|
//------------------------------------------------------------------------------ |
|
void CNPC_Bullseye::DecalTrace( trace_t *pOldTrace, char const *decalName ) |
|
{ |
|
int index = decalsystem->GetDecalIndexForName( decalName ); |
|
if ( index < 0 ) |
|
return; |
|
|
|
// Get direction of original trace |
|
Vector vTraceDir = pOldTrace->endpos - pOldTrace->startpos; |
|
VectorNormalize(vTraceDir); |
|
|
|
// Create a new trace that passes through me |
|
Vector vStartTrace = pOldTrace->endpos - (1.0 * vTraceDir); |
|
Vector vEndTrace = pOldTrace->endpos + (MAX_TRACE_LENGTH * vTraceDir); |
|
|
|
trace_t pNewTrace; |
|
AI_TraceLine(vStartTrace, vEndTrace, MASK_SHOT, this, COLLISION_GROUP_NONE, &pNewTrace); |
|
|
|
CBroadcastRecipientFilter filter; |
|
te->Decal( filter, 0.0, &pNewTrace.endpos, &pNewTrace.startpos, |
|
ENTINDEX( pNewTrace.m_pEnt ), pNewTrace.hitbox, index ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bullseye::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ) |
|
{ |
|
// Get direction of original trace |
|
Vector vTraceDir = pTrace->endpos - pTrace->startpos; |
|
VectorNormalize(vTraceDir); |
|
|
|
// Create a new trace that passes through me |
|
Vector vStartTrace = pTrace->endpos - (1.0 * vTraceDir); |
|
Vector vEndTrace = pTrace->endpos + (MAX_TRACE_LENGTH * vTraceDir); |
|
|
|
trace_t pNewTrace; |
|
AI_TraceLine(vStartTrace, vEndTrace, MASK_SHOT, this, COLLISION_GROUP_NONE, &pNewTrace); |
|
|
|
CBaseEntity *pEntity = pNewTrace.m_pEnt; |
|
|
|
// Only do this for BSP model entities |
|
if ( ( pEntity ) && ( pEntity->IsBSPModel() == false ) ) |
|
return; |
|
|
|
BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// |
|
// |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
Class_T CNPC_Bullseye::Classify( void ) |
|
{ |
|
return CLASS_BULLSEYE; |
|
} |
|
|
|
void CNPC_Bullseye::OnRestore( void ) |
|
{ |
|
if ( m_spawnflags & SF_BULLSEYE_VPHYSICSSHADOW ) |
|
{ |
|
IPhysicsObject *pObject = VPhysicsGetObject(); |
|
|
|
if ( pObject == NULL ) |
|
{ |
|
VPhysicsInitShadow( false, false ); |
|
} |
|
} |
|
|
|
BaseClass::OnRestore(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bullseye::BullseyeThink( void ) |
|
{ |
|
ClearCondition( COND_LIGHT_DAMAGE ); |
|
ClearCondition( COND_HEAVY_DAMAGE ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool CNPC_Bullseye::CanBecomeRagdoll() |
|
{ |
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool CNPC_Bullseye::CanBeAnEnemyOf( CBaseEntity *pEnemy ) |
|
{ |
|
static const float flFullFov = cos( DEG2RAD(360) / 2.0 ); |
|
if ( fabsf( m_flFieldOfView - flFullFov ) > .01 ) |
|
{ |
|
if ( !FInViewCone( pEnemy ) ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
if ( m_flMinDistValidEnemy > 0 ) |
|
{ |
|
float distSq = ( GetAbsOrigin().AsVector2D() - pEnemy->GetAbsOrigin().AsVector2D() ).LengthSqr(); |
|
if ( distSq < Square( m_flMinDistValidEnemy ) ) |
|
{ |
|
return false; |
|
} |
|
} |
|
return BaseClass::CanBeAnEnemyOf( pEnemy ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Bullseyes should always report light damage if any amount of damage is taken |
|
// Input : fDamage - amount of damage |
|
// bitsDamageType - damage type |
|
//----------------------------------------------------------------------------- |
|
bool CNPC_Bullseye::IsLightDamage( const CTakeDamageInfo &info ) |
|
{ |
|
return ( info.GetDamage() > 0 ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pAttacker - |
|
// flDamage - |
|
// &vecDir - |
|
// *ptr - |
|
// bitsDamageType - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bullseye::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) |
|
{ |
|
//If specified, we must be the enemy of the target |
|
if ( m_spawnflags & SF_BULLSEYE_ENEMYDAMAGEONLY ) |
|
{ |
|
CAI_BaseNPC *pInstigator = info.GetAttacker()->MyNPCPointer(); |
|
|
|
if ( pInstigator == NULL ) |
|
return; |
|
|
|
if ( pInstigator->GetEnemy() != this ) |
|
return; |
|
} |
|
|
|
//We can bleed if we want to, we can leave decals behind... |
|
if ( ( m_spawnflags & SF_BULLSEYE_BLEED ) && ( m_takedamage == DAMAGE_NO ) ) |
|
{ |
|
TraceBleed( info.GetDamage(), vecDir, ptr, info.GetDamageType() ); |
|
} |
|
|
|
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pInflictor - |
|
// *pAttacker - |
|
// flDamage - |
|
// bitsDamageType - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CNPC_Bullseye::OnTakeDamage( const CTakeDamageInfo &info ) |
|
{ |
|
SetNextThink( gpGlobals->curtime ); |
|
|
|
//If specified, we must be the enemy of the target |
|
if ( m_spawnflags & SF_BULLSEYE_ENEMYDAMAGEONLY ) |
|
{ |
|
CAI_BaseNPC *pInstigator = info.GetAttacker()->MyNPCPointer(); |
|
|
|
if ( pInstigator == NULL ) |
|
return 0; |
|
|
|
if ( pInstigator->GetEnemy() != this ) |
|
return 0; |
|
} |
|
|
|
//If we're a pain proxy, send the damage through |
|
if ( m_hPainPartner != NULL ) |
|
{ |
|
m_hPainPartner->TakeDamage( info ); |
|
|
|
//Fire all pain indicators but take no real damage |
|
CTakeDamageInfo subInfo = info; |
|
subInfo.SetDamage( 0 ); |
|
return BaseClass::OnTakeDamage( subInfo ); |
|
} |
|
|
|
return BaseClass::OnTakeDamage( info ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pOther - |
|
//----------------------------------------------------------------------------- |
|
void CNPC_Bullseye::SetPainPartner( CBaseEntity *pOther ) |
|
{ |
|
m_hPainPartner = pOther; |
|
} |
|
|
|
void CNPC_Bullseye::InputTargeted( inputdata_t &inputdata ) |
|
{ |
|
m_OnTargeted.FireOutput( inputdata.pActivator, inputdata.pCaller, 0 ); |
|
} |
|
|
|
void CNPC_Bullseye::InputReleased( inputdata_t &inputdata ) |
|
{ |
|
m_OnReleased.FireOutput( inputdata.pActivator, inputdata.pCaller, 0 ); |
|
}
|
|
|