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.
529 lines
16 KiB
529 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Used to fire events based on the orientation of a given entity. |
|
// |
|
// Looks at its target's anglular velocity every frame and fires outputs |
|
// as the angular velocity passes a given threshold value. |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "entityinput.h" |
|
#include "entityoutput.h" |
|
#include "eventqueue.h" |
|
#include "mathlib/mathlib.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
enum |
|
{ |
|
AVELOCITY_SENSOR_NO_LAST_RESULT = -2 |
|
}; |
|
|
|
ConVar g_debug_angularsensor( "g_debug_angularsensor", "0", FCVAR_CHEAT ); |
|
|
|
class CPointAngularVelocitySensor : public CPointEntity |
|
{ |
|
DECLARE_CLASS( CPointAngularVelocitySensor, CPointEntity ); |
|
|
|
public: |
|
|
|
CPointAngularVelocitySensor(); |
|
void Activate(void); |
|
void Spawn(void); |
|
void Think(void); |
|
|
|
private: |
|
|
|
float SampleAngularVelocity(CBaseEntity *pEntity); |
|
int CompareToThreshold(CBaseEntity *pEntity, float flThreshold, bool bFireVelocityOutput); |
|
void FireCompareOutput(int nCompareResult, CBaseEntity *pActivator); |
|
void DrawDebugLines( void ); |
|
|
|
// Input handlers |
|
void InputTest( inputdata_t &inputdata ); |
|
void InputTestWithInterval( inputdata_t &inputdata ); |
|
|
|
EHANDLE m_hTargetEntity; // Entity whose angles are being monitored. |
|
float m_flThreshold; // The threshold angular velocity that we are looking for. |
|
int m_nLastCompareResult; // The comparison result from our last measurement, expressed as -1, 0, or 1 |
|
int m_nLastFireResult; // The last result for which we fire the output. |
|
|
|
float m_flFireTime; |
|
float m_flFireInterval; |
|
float m_flLastAngVelocity; |
|
|
|
QAngle m_lastOrientation; |
|
|
|
Vector m_vecAxis; |
|
bool m_bUseHelper; |
|
|
|
// Outputs |
|
COutputFloat m_AngularVelocity; |
|
|
|
// Compare the target's angular velocity to the threshold velocity and fire the appropriate output. |
|
// These outputs are filtered by m_flFireInterval to ignore excessive oscillations. |
|
COutputEvent m_OnLessThan; |
|
COutputEvent m_OnLessThanOrEqualTo; |
|
COutputEvent m_OnGreaterThan; |
|
COutputEvent m_OnGreaterThanOrEqualTo; |
|
COutputEvent m_OnEqualTo; |
|
|
|
DECLARE_DATADESC(); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS(point_angularvelocitysensor, CPointAngularVelocitySensor); |
|
|
|
|
|
BEGIN_DATADESC( CPointAngularVelocitySensor ) |
|
|
|
// Fields |
|
DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE ), |
|
DEFINE_KEYFIELD(m_flThreshold, FIELD_FLOAT, "threshold"), |
|
DEFINE_FIELD(m_nLastCompareResult, FIELD_INTEGER), |
|
DEFINE_FIELD( m_nLastFireResult, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_flFireTime, FIELD_TIME ), |
|
DEFINE_KEYFIELD( m_flFireInterval, FIELD_FLOAT, "fireinterval" ), |
|
DEFINE_FIELD( m_flLastAngVelocity, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_lastOrientation, FIELD_VECTOR ), |
|
|
|
// Inputs |
|
DEFINE_INPUTFUNC(FIELD_VOID, "Test", InputTest), |
|
DEFINE_INPUTFUNC(FIELD_VOID, "TestWithInterval", InputTestWithInterval), |
|
|
|
// Outputs |
|
DEFINE_OUTPUT(m_OnLessThan, "OnLessThan"), |
|
DEFINE_OUTPUT(m_OnLessThanOrEqualTo, "OnLessThanOrEqualTo"), |
|
DEFINE_OUTPUT(m_OnGreaterThan, "OnGreaterThan"), |
|
DEFINE_OUTPUT(m_OnGreaterThanOrEqualTo, "OnGreaterThanOrEqualTo"), |
|
DEFINE_OUTPUT(m_OnEqualTo, "OnEqualTo"), |
|
DEFINE_OUTPUT(m_AngularVelocity, "AngularVelocity"), |
|
|
|
DEFINE_KEYFIELD( m_vecAxis, FIELD_VECTOR, "axis" ), |
|
DEFINE_KEYFIELD( m_bUseHelper, FIELD_BOOLEAN, "usehelper" ), |
|
|
|
END_DATADESC() |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: constructor provides default values |
|
//----------------------------------------------------------------------------- |
|
CPointAngularVelocitySensor::CPointAngularVelocitySensor() |
|
{ |
|
m_flFireInterval = 0.2f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called when spawning after parsing keyvalues. |
|
//----------------------------------------------------------------------------- |
|
void CPointAngularVelocitySensor::Spawn(void) |
|
{ |
|
m_flThreshold = fabs(m_flThreshold); |
|
m_nLastFireResult = AVELOCITY_SENSOR_NO_LAST_RESULT; |
|
m_nLastCompareResult = AVELOCITY_SENSOR_NO_LAST_RESULT; |
|
// m_flFireInterval = 0.2; |
|
m_lastOrientation = vec3_angle; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called after all entities in the map have spawned. |
|
//----------------------------------------------------------------------------- |
|
void CPointAngularVelocitySensor::Activate(void) |
|
{ |
|
BaseClass::Activate(); |
|
|
|
m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target ); |
|
|
|
if (m_hTargetEntity) |
|
{ |
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Draws magic lines... |
|
//----------------------------------------------------------------------------- |
|
void CPointAngularVelocitySensor::DrawDebugLines( void ) |
|
{ |
|
if ( m_hTargetEntity ) |
|
{ |
|
Vector vForward, vRight, vUp; |
|
AngleVectors( m_hTargetEntity->GetAbsAngles(), &vForward, &vRight, &vUp ); |
|
|
|
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vForward * 64, 255, 0, 0, false, 0 ); |
|
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vRight * 64, 0, 255, 0, false, 0 ); |
|
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vUp * 64, 0, 0, 255, false, 0 ); |
|
} |
|
|
|
if ( m_bUseHelper == true ) |
|
{ |
|
QAngle Angles; |
|
Vector vAxisForward, vAxisRight, vAxisUp; |
|
|
|
Vector vLine = m_vecAxis - GetAbsOrigin(); |
|
|
|
VectorNormalize( vLine ); |
|
|
|
VectorAngles( vLine, Angles ); |
|
AngleVectors( Angles, &vAxisForward, &vAxisRight, &vAxisUp ); |
|
|
|
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vAxisForward * 64, 255, 0, 0, false, 0 ); |
|
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vAxisRight * 64, 0, 255, 0, false, 0 ); |
|
NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + vAxisUp * 64, 0, 0, 255, false, 0 ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the magnitude of the entity's angular velocity. |
|
//----------------------------------------------------------------------------- |
|
float CPointAngularVelocitySensor::SampleAngularVelocity(CBaseEntity *pEntity) |
|
{ |
|
if (pEntity->GetMoveType() == MOVETYPE_VPHYSICS) |
|
{ |
|
IPhysicsObject *pPhys = pEntity->VPhysicsGetObject(); |
|
if (pPhys != NULL) |
|
{ |
|
Vector vecVelocity; |
|
AngularImpulse vecAngVelocity; |
|
pPhys->GetVelocity(&vecVelocity, &vecAngVelocity); |
|
|
|
QAngle angles; |
|
pPhys->GetPosition( NULL, &angles ); |
|
|
|
float dt = gpGlobals->curtime - GetLastThink(); |
|
if ( dt == 0 ) |
|
dt = 0.1; |
|
|
|
// HACKHACK: We don't expect a real 'delta' orientation here, just enough of an error estimate to tell if this thing |
|
// is trying to move, but failing. |
|
QAngle delta = angles - m_lastOrientation; |
|
|
|
if ( ( delta.Length() / dt ) < ( vecAngVelocity.Length() * 0.01 ) ) |
|
{ |
|
return 0.0f; |
|
} |
|
m_lastOrientation = angles; |
|
|
|
if ( m_bUseHelper == false ) |
|
{ |
|
return vecAngVelocity.Length(); |
|
} |
|
else |
|
{ |
|
Vector vLine = m_vecAxis - GetAbsOrigin(); |
|
VectorNormalize( vLine ); |
|
|
|
Vector vecWorldAngVelocity; |
|
pPhys->LocalToWorldVector( &vecWorldAngVelocity, vecAngVelocity ); |
|
float flDot = DotProduct( vecWorldAngVelocity, vLine ); |
|
|
|
return flDot; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
QAngle vecAngVel = pEntity->GetLocalAngularVelocity(); |
|
float flMax = MAX(fabs(vecAngVel[PITCH]), fabs(vecAngVel[YAW])); |
|
|
|
return MAX(flMax, fabs(vecAngVel[ROLL])); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Compares the given entity's angular velocity to the threshold velocity. |
|
// Input : pEntity - Entity whose angular velocity is being measured. |
|
// flThreshold - |
|
// Output : Returns -1 if less than, 0 if equal to, or 1 if greater than the threshold. |
|
//----------------------------------------------------------------------------- |
|
int CPointAngularVelocitySensor::CompareToThreshold(CBaseEntity *pEntity, float flThreshold, bool bFireVelocityOutput) |
|
{ |
|
if (pEntity == NULL) |
|
{ |
|
return 0; |
|
} |
|
|
|
float flAngVelocity = SampleAngularVelocity(pEntity); |
|
|
|
if ( g_debug_angularsensor.GetBool() ) |
|
{ |
|
DrawDebugLines(); |
|
} |
|
|
|
if (bFireVelocityOutput && (flAngVelocity != m_flLastAngVelocity)) |
|
{ |
|
m_AngularVelocity.Set(flAngVelocity, pEntity, this); |
|
m_flLastAngVelocity = flAngVelocity; |
|
} |
|
|
|
if (flAngVelocity > flThreshold) |
|
{ |
|
return 1; |
|
} |
|
|
|
if (flAngVelocity == flThreshold) |
|
{ |
|
return 0; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Called every frame to sense the angular velocity of the target entity. |
|
// Output is filtered by m_flFireInterval to ignore excessive oscillations. |
|
//----------------------------------------------------------------------------- |
|
void CPointAngularVelocitySensor::Think(void) |
|
{ |
|
if (m_hTargetEntity != NULL) |
|
{ |
|
// |
|
// Check to see if the measure entity's angular velocity has been within |
|
// tolerance of the threshold for the given period of time. |
|
// |
|
int nCompare = CompareToThreshold(m_hTargetEntity, m_flThreshold, true); |
|
if (nCompare != m_nLastCompareResult) |
|
{ |
|
// If we've oscillated back to where we last fired the output, don't |
|
// fire the same output again. |
|
if (nCompare == m_nLastFireResult) |
|
{ |
|
m_flFireTime = 0; |
|
} |
|
else if (m_nLastCompareResult != AVELOCITY_SENSOR_NO_LAST_RESULT) |
|
{ |
|
// |
|
// The value has changed -- reset the timer. We'll fire the output if |
|
// it stays at this value until the interval expires. |
|
// |
|
m_flFireTime = gpGlobals->curtime + m_flFireInterval; |
|
} |
|
|
|
m_nLastCompareResult = nCompare; |
|
} |
|
else if ((m_flFireTime != 0) && (gpGlobals->curtime >= m_flFireTime)) |
|
{ |
|
// |
|
// The compare result has held steady long enough -- time to |
|
// fire the output. |
|
// |
|
FireCompareOutput(nCompare, this); |
|
m_nLastFireResult = nCompare; |
|
m_flFireTime = 0; |
|
} |
|
|
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Fires the output after the fire interval if the velocity is stable. |
|
//----------------------------------------------------------------------------- |
|
void CPointAngularVelocitySensor::InputTestWithInterval( inputdata_t &inputdata ) |
|
{ |
|
if (m_hTargetEntity != NULL) |
|
{ |
|
m_flFireTime = gpGlobals->curtime + m_flFireInterval; |
|
m_nLastFireResult = AVELOCITY_SENSOR_NO_LAST_RESULT; |
|
m_nLastCompareResult = CompareToThreshold(m_hTargetEntity, m_flThreshold, true); |
|
|
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler for forcing an instantaneous test of the condition. |
|
//----------------------------------------------------------------------------- |
|
void CPointAngularVelocitySensor::InputTest( inputdata_t &inputdata ) |
|
{ |
|
int nCompareResult = CompareToThreshold(m_hTargetEntity, m_flThreshold, false); |
|
FireCompareOutput(nCompareResult, inputdata.pActivator); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Fires the appropriate output based on the given comparison result. |
|
// Input : nCompareResult - |
|
// pActivator - |
|
//----------------------------------------------------------------------------- |
|
void CPointAngularVelocitySensor::FireCompareOutput( int nCompareResult, CBaseEntity *pActivator ) |
|
{ |
|
if (nCompareResult == -1) |
|
{ |
|
m_OnLessThan.FireOutput(pActivator, this); |
|
m_OnLessThanOrEqualTo.FireOutput(pActivator, this); |
|
} |
|
else if (nCompareResult == 1) |
|
{ |
|
m_OnGreaterThan.FireOutput(pActivator, this); |
|
m_OnGreaterThanOrEqualTo.FireOutput(pActivator, this); |
|
} |
|
else |
|
{ |
|
m_OnEqualTo.FireOutput(pActivator, this); |
|
m_OnLessThanOrEqualTo.FireOutput(pActivator, this); |
|
m_OnGreaterThanOrEqualTo.FireOutput(pActivator, this); |
|
} |
|
} |
|
|
|
// ============================================================================ |
|
// |
|
// Simple velocity sensor |
|
// |
|
// ============================================================================ |
|
|
|
class CPointVelocitySensor : public CPointEntity |
|
{ |
|
DECLARE_CLASS( CPointVelocitySensor, CPointEntity ); |
|
|
|
public: |
|
|
|
void Spawn(); |
|
void Activate( void ); |
|
void Think( void ); |
|
|
|
private: |
|
|
|
void SampleVelocity( void ); |
|
|
|
EHANDLE m_hTargetEntity; // Entity whose angles are being monitored. |
|
Vector m_vecAxis; // Axis along which to measure the speed. |
|
bool m_bEnabled; // Whether we're measuring or not |
|
|
|
// Outputs |
|
float m_fPrevVelocity; // stores velocity from last frame, so we only write the output if it has changed |
|
COutputFloat m_Velocity; |
|
|
|
void InputEnable( inputdata_t &inputdata ); |
|
void InputDisable( inputdata_t &inputdata ); |
|
|
|
DECLARE_DATADESC(); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( point_velocitysensor, CPointVelocitySensor ); |
|
|
|
BEGIN_DATADESC( CPointVelocitySensor ) |
|
|
|
// Fields |
|
DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE ), |
|
DEFINE_KEYFIELD( m_vecAxis, FIELD_VECTOR, "axis" ), |
|
DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "enabled" ), |
|
DEFINE_FIELD( m_fPrevVelocity, FIELD_FLOAT ), |
|
|
|
// Outputs |
|
DEFINE_OUTPUT( m_Velocity, "Velocity" ), |
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), |
|
|
|
END_DATADESC() |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CPointVelocitySensor::Spawn() |
|
{ |
|
Vector vLine = m_vecAxis - GetAbsOrigin(); |
|
VectorNormalize( vLine ); |
|
m_vecAxis = vLine; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CPointVelocitySensor::Activate( void ) |
|
{ |
|
BaseClass::Activate(); |
|
|
|
m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target ); |
|
|
|
if ( m_bEnabled && m_hTargetEntity ) |
|
{ |
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CPointVelocitySensor::InputEnable( inputdata_t &inputdata ) |
|
{ |
|
// Don't interrupt us if we're already enabled |
|
if ( m_bEnabled ) |
|
return; |
|
|
|
m_bEnabled = true; |
|
|
|
if ( m_hTargetEntity ) |
|
{ |
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CPointVelocitySensor::InputDisable( inputdata_t &inputdata ) |
|
{ |
|
m_bEnabled = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called every frame |
|
//----------------------------------------------------------------------------- |
|
void CPointVelocitySensor::Think( void ) |
|
{ |
|
if ( m_hTargetEntity != NULL && m_bEnabled ) |
|
{ |
|
SampleVelocity(); |
|
SetNextThink( gpGlobals->curtime ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the magnitude of the entity's angular velocity. |
|
//----------------------------------------------------------------------------- |
|
void CPointVelocitySensor::SampleVelocity( void ) |
|
{ |
|
if ( m_hTargetEntity == NULL ) |
|
return; |
|
|
|
Vector vecVelocity; |
|
|
|
if ( m_hTargetEntity->GetMoveType() == MOVETYPE_VPHYSICS ) |
|
{ |
|
IPhysicsObject *pPhys = m_hTargetEntity->VPhysicsGetObject(); |
|
if ( pPhys != NULL ) |
|
{ |
|
pPhys->GetVelocity( &vecVelocity, NULL ); |
|
} |
|
} |
|
else |
|
{ |
|
vecVelocity = m_hTargetEntity->GetAbsVelocity(); |
|
} |
|
|
|
/* |
|
float flSpeed = VectorNormalize( vecVelocity ); |
|
float flDot = ( m_vecAxis != vec3_origin ) ? DotProduct( vecVelocity, m_vecAxis ) : 1.0f; |
|
*/ |
|
// We want the component of the velocity vector in the direction of the axis, which since the |
|
// axis is normalized is simply their dot product (eg V . A = |V|*|A|*cos(theta) ) |
|
m_fPrevVelocity = ( m_vecAxis != vec3_origin ) ? DotProduct( vecVelocity, m_vecAxis ) : 1.0f; |
|
|
|
// if it's changed since the last frame, poke the output |
|
if ( m_fPrevVelocity != m_Velocity.Get() ) |
|
{ |
|
m_Velocity.Set( m_fPrevVelocity, NULL, NULL ); |
|
} |
|
}
|
|
|