From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/server/pointanglesensor.cpp | 562 ++++++++++++++++++++++++++++++++ 1 file changed, 562 insertions(+) create mode 100644 mp/src/game/server/pointanglesensor.cpp (limited to 'mp/src/game/server/pointanglesensor.cpp') diff --git a/mp/src/game/server/pointanglesensor.cpp b/mp/src/game/server/pointanglesensor.cpp new file mode 100644 index 00000000..281afff7 --- /dev/null +++ b/mp/src/game/server/pointanglesensor.cpp @@ -0,0 +1,562 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Used to fire events based on the orientation of a given entity. +// +// Looks at its target's angles every frame and fires an output if its +// target's forward vector points at a specified lookat entity for more +// than a specified length of time. +// +// It also fires an output whenever the target's angles change. +// +//=============================================================================// + +#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" + +#define SF_USE_TARGET_FACING (1<<0) // Use the target entity's direction instead of position + +class CPointAngleSensor : public CPointEntity +{ + DECLARE_CLASS(CPointAngleSensor, CPointEntity); +public: + + bool KeyValue(const char *szKeyName, const char *szValue); + void Activate(void); + void Spawn(void); + void Think(void); + + int DrawDebugTextOverlays(void); + +protected: + + void Enable(); + void Disable(); + + // Input handlers + void InputEnable(inputdata_t &inputdata); + void InputDisable(inputdata_t &inputdata); + void InputToggle(inputdata_t &inputdata); + void InputTest(inputdata_t &inputdata); + void InputSetTargetEntity(inputdata_t &inputdata); + + bool IsFacingWithinTolerance(CBaseEntity *pEntity, CBaseEntity *pTarget, float flTolerance, float *pflDot = NULL); + + bool m_bDisabled; // When disabled, we do not think or fire outputs. + string_t m_nLookAtName; // Name of the entity that the target must point at to fire the OnTrue output. + + EHANDLE m_hTargetEntity; // Entity whose angles are being monitored. + EHANDLE m_hLookAtEntity; // Entity that the target must look at to fire the OnTrue output. + + float m_flDuration; // Time in seconds for which the entity must point at the target. + float m_flDotTolerance; // Degrees of error allowed to satisfy the condition, expressed as a dot product. + float m_flFacingTime; // The time at which the target entity pointed at the lookat entity. + bool m_bFired; // Latches the output so it only fires once per true. + + // Outputs + COutputEvent m_OnFacingLookat; // Fired when the target points at the lookat entity. + COutputEvent m_OnNotFacingLookat; // Fired in response to a Test input if the target is not looking at the lookat entity. + COutputVector m_TargetDir; + COutputFloat m_FacingPercentage; // Normalize value representing how close the entity is to facing directly at the target + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS(point_anglesensor, CPointAngleSensor); + + +BEGIN_DATADESC(CPointAngleSensor) + + // Keys + DEFINE_KEYFIELD(m_bDisabled, FIELD_BOOLEAN, "StartDisabled"), + DEFINE_KEYFIELD(m_nLookAtName, FIELD_STRING, "lookatname"), + DEFINE_FIELD(m_hTargetEntity, FIELD_EHANDLE), + DEFINE_FIELD(m_hLookAtEntity, FIELD_EHANDLE), + DEFINE_KEYFIELD(m_flDuration, FIELD_FLOAT, "duration"), + DEFINE_FIELD(m_flDotTolerance, FIELD_FLOAT), + DEFINE_FIELD(m_flFacingTime, FIELD_TIME), + DEFINE_FIELD(m_bFired, FIELD_BOOLEAN), + + // Outputs + DEFINE_OUTPUT(m_OnFacingLookat, "OnFacingLookat"), + DEFINE_OUTPUT(m_OnNotFacingLookat, "OnNotFacingLookat"), + DEFINE_OUTPUT(m_TargetDir, "TargetDir"), + DEFINE_OUTPUT(m_FacingPercentage, "FacingPercentage"), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable), + DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable), + DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), + DEFINE_INPUTFUNC(FIELD_VOID, "Test", InputTest), + DEFINE_INPUTFUNC(FIELD_STRING, "SetTargetEntity", InputSetTargetEntity), + +END_DATADESC() + + +//----------------------------------------------------------------------------- +// Purpose: Handles keyvalues that require special processing. +// Output : Returns true if handled, false if not. +//----------------------------------------------------------------------------- +bool CPointAngleSensor::KeyValue(const char *szKeyName, const char *szValue) +{ + if (FStrEq(szKeyName, "tolerance")) + { + float flTolerance = atof(szValue); + m_flDotTolerance = cos(DEG2RAD(flTolerance)); + } + else + { + return(BaseClass::KeyValue(szKeyName, szValue)); + } + + return(true); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when spawning after parsing keyvalues. +//----------------------------------------------------------------------------- +void CPointAngleSensor::Spawn(void) +{ + BaseClass::Spawn(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called after all entities have spawned on new map or savegame load. +//----------------------------------------------------------------------------- +void CPointAngleSensor::Activate(void) +{ + BaseClass::Activate(); + + if (!m_hTargetEntity) + { + m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target ); + } + + if (!m_hLookAtEntity && (m_nLookAtName != NULL_STRING)) + { + m_hLookAtEntity = gEntList.FindEntityByName( NULL, m_nLookAtName ); + if (!m_hLookAtEntity) + { + DevMsg(1, "Angle sensor '%s' could not find look at entity '%s'.\n", GetDebugName(), STRING(m_nLookAtName)); + } + } + + // It's okay to not have a look at entity, it just means we measure and output the angles + // of the target entity without testing them against the look at entity. + if (!m_bDisabled && m_hTargetEntity) + { + SetNextThink( gpGlobals->curtime ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Determines if one entity is facing within a given tolerance of another +// Input : pEntity - +// pTarget - +// flTolerance - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPointAngleSensor::IsFacingWithinTolerance(CBaseEntity *pEntity, CBaseEntity *pTarget, float flTolerance, float *pflDot) +{ + if (pflDot) + { + *pflDot = 0; + } + + if ((pEntity == NULL) || (pTarget == NULL)) + { + return(false); + } + + Vector forward; + pEntity->GetVectors(&forward, NULL, NULL); + + Vector dir; + // Use either our position relative to the target, or the target's raw facing + if ( HasSpawnFlags( SF_USE_TARGET_FACING ) ) + { + pTarget->GetVectors(&dir, NULL, NULL); + } + else + { + dir = pTarget->GetAbsOrigin() - pEntity->GetAbsOrigin(); + VectorNormalize(dir); + } + + // + // Larger dot product corresponds to a smaller angle. + // + float flDot = dir.Dot(forward); + if (pflDot) + { + *pflDot = flDot; + } + + if (flDot >= m_flDotTolerance) + { + return(true); + } + + return(false); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called every frame. +//----------------------------------------------------------------------------- +void CPointAngleSensor::Think(void) +{ + if (m_hTargetEntity != NULL) + { + Vector forward; + m_hTargetEntity->GetVectors(&forward, NULL, NULL); + m_TargetDir.Set(forward, this, this); + + if (m_hLookAtEntity != NULL) + { + // + // Check to see if the measure entity's forward vector has been within + // given tolerance of the target entity for the given period of time. + // + float flDot; + if (IsFacingWithinTolerance(m_hTargetEntity, m_hLookAtEntity, m_flDotTolerance, &flDot )) + { + if (!m_bFired) + { + if (!m_flFacingTime) + { + m_flFacingTime = gpGlobals->curtime; + } + + if (gpGlobals->curtime >= m_flFacingTime + m_flDuration) + { + m_OnFacingLookat.FireOutput(this, this); + m_bFired = true; + } + } + } + else + { + // Reset the fired state + if ( m_bFired ) + { + m_bFired = false; + } + + // Always reset the time when we've lost our facing + m_flFacingTime = 0; + } + + // Output the angle range we're in + float flPerc = RemapValClamped( flDot, 1.0f, m_flDotTolerance, 1.0f, 0.0f ); + m_FacingPercentage.Set( flPerc, this, this ); + } + + SetNextThink( gpGlobals->curtime ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Input handler for forcing an instantaneous test of the condition. +//----------------------------------------------------------------------------- +void CPointAngleSensor::InputTest(inputdata_t &inputdata) +{ + if (IsFacingWithinTolerance(m_hTargetEntity, m_hLookAtEntity, m_flDotTolerance)) + { + m_OnFacingLookat.FireOutput(inputdata.pActivator, this); + } + else + { + m_OnNotFacingLookat.FireOutput(inputdata.pActivator, this); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAngleSensor::InputSetTargetEntity(inputdata_t &inputdata) +{ + if ((inputdata.value.String() == NULL) || (inputdata.value.StringID() == NULL_STRING) || (inputdata.value.String()[0] == '\0')) + { + m_target = NULL_STRING; + m_hTargetEntity = NULL; + SetNextThink( TICK_NEVER_THINK ); + } + else + { + m_target = AllocPooledString(inputdata.value.String()); + m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target, NULL, inputdata.pActivator, inputdata.pCaller ); + if (!m_bDisabled && m_hTargetEntity) + { + SetNextThink( gpGlobals->curtime ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAngleSensor::InputEnable(inputdata_t &inputdata) +{ + Enable(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAngleSensor::InputDisable(inputdata_t &inputdata) +{ + Disable(); +} + + +//----------------------------------------------------------------------------- +// Purpose: I like separators between my functions. +//----------------------------------------------------------------------------- +void CPointAngleSensor::InputToggle(inputdata_t &inputdata) +{ + if (m_bDisabled) + { + Enable(); + } + else + { + Disable(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAngleSensor::Enable() +{ + m_bDisabled = false; + if (m_hTargetEntity) + { + SetNextThink(gpGlobals->curtime); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointAngleSensor::Disable() +{ + m_bDisabled = true; + SetNextThink(TICK_NEVER_THINK); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CPointAngleSensor::DrawDebugTextOverlays(void) +{ + int nOffset = BaseClass::DrawDebugTextOverlays(); + + if (m_debugOverlays & OVERLAY_TEXT_BIT) + { + float flDot; + bool bFacing = IsFacingWithinTolerance(m_hTargetEntity, m_hLookAtEntity, m_flDotTolerance, &flDot); + + char tempstr[512]; + Q_snprintf(tempstr, sizeof(tempstr), "delta ang (dot) : %.2f (%f)", RAD2DEG(acos(flDot)), flDot); + EntityText( nOffset, tempstr, 0); + nOffset++; + + Q_snprintf(tempstr, sizeof(tempstr), "tolerance ang (dot): %.2f (%f)", RAD2DEG(acos(m_flDotTolerance)), m_flDotTolerance); + EntityText( nOffset, tempstr, 0); + nOffset++; + + Q_snprintf(tempstr, sizeof(tempstr), "facing: %s", bFacing ? "yes" : "no"); + EntityText( nOffset, tempstr, 0); + nOffset++; + } + + return nOffset; +} + +// ==================================================================== +// Proximity sensor +// ==================================================================== + +#define SF_PROXIMITY_TEST_AGAINST_AXIS (1<<0) + +class CPointProximitySensor : public CPointEntity +{ + DECLARE_CLASS( CPointProximitySensor, CPointEntity ); + +public: + + virtual void Activate( void ); + +protected: + + void Think( void ); + void Enable( void ); + void Disable( void ); + + // Input handlers + void InputEnable(inputdata_t &inputdata); + void InputDisable(inputdata_t &inputdata); + void InputToggle(inputdata_t &inputdata); + void InputSetTargetEntity(inputdata_t &inputdata); + +private: + + bool m_bDisabled; // When disabled, we do not think or fire outputs. + EHANDLE m_hTargetEntity; // Entity whose angles are being monitored. + + COutputFloat m_Distance; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( point_proximity_sensor, CPointProximitySensor ); + +BEGIN_DATADESC( CPointProximitySensor ) + + // Keys + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + DEFINE_FIELD( m_hTargetEntity, FIELD_EHANDLE ), + + // Outputs + DEFINE_OUTPUT( m_Distance, "Distance"), + + // Inputs + DEFINE_INPUTFUNC(FIELD_VOID, "Enable", InputEnable), + DEFINE_INPUTFUNC(FIELD_VOID, "Disable", InputDisable), + DEFINE_INPUTFUNC(FIELD_VOID, "Toggle", InputToggle), + DEFINE_INPUTFUNC(FIELD_STRING, "SetTargetEntity", InputSetTargetEntity), + +END_DATADESC() + +//----------------------------------------------------------------------------- +// Purpose: Called after all entities have spawned on new map or savegame load. +//----------------------------------------------------------------------------- +void CPointProximitySensor::Activate( void ) +{ + BaseClass::Activate(); + + if ( m_hTargetEntity == NULL ) + { + m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target ); + } + + if ( m_bDisabled == false && m_hTargetEntity != NULL ) + { + SetNextThink( gpGlobals->curtime ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProximitySensor::InputSetTargetEntity(inputdata_t &inputdata) +{ + if ((inputdata.value.String() == NULL) || (inputdata.value.StringID() == NULL_STRING) || (inputdata.value.String()[0] == '\0')) + { + m_target = NULL_STRING; + m_hTargetEntity = NULL; + SetNextThink( TICK_NEVER_THINK ); + } + else + { + m_target = AllocPooledString(inputdata.value.String()); + m_hTargetEntity = gEntList.FindEntityByName( NULL, m_target, NULL, inputdata.pActivator, inputdata.pCaller ); + if (!m_bDisabled && m_hTargetEntity) + { + SetNextThink( gpGlobals->curtime ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProximitySensor::InputEnable( inputdata_t &inputdata ) +{ + Enable(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProximitySensor::InputDisable( inputdata_t &inputdata ) +{ + Disable(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProximitySensor::InputToggle( inputdata_t &inputdata ) +{ + if ( m_bDisabled ) + { + Enable(); + } + else + { + Disable(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProximitySensor::Enable( void ) +{ + m_bDisabled = false; + if ( m_hTargetEntity ) + { + SetNextThink( gpGlobals->curtime ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPointProximitySensor::Disable( void ) +{ + m_bDisabled = true; + SetNextThink( TICK_NEVER_THINK ); +} + +//----------------------------------------------------------------------------- +// Purpose: Called every frame +//----------------------------------------------------------------------------- +void CPointProximitySensor::Think( void ) +{ + if ( m_hTargetEntity != NULL ) + { + Vector vecTestDir = ( m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin() ); + float flDist = VectorNormalize( vecTestDir ); + + // If we're only interested in the distance along a vector, modify the length the accomodate that + if ( HasSpawnFlags( SF_PROXIMITY_TEST_AGAINST_AXIS ) ) + { + Vector vecDir; + GetVectors( &vecDir, NULL, NULL ); + + float flDot = DotProduct( vecTestDir, vecDir ); + flDist *= fabs( flDot ); + } + + m_Distance.Set( flDist, this, this ); + SetNextThink( gpGlobals->curtime ); + } +} -- cgit v1.2.3