diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/filters.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/filters.cpp')
| -rw-r--r-- | game/server/filters.cpp | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/game/server/filters.cpp b/game/server/filters.cpp new file mode 100644 index 0000000..6179254 --- /dev/null +++ b/game/server/filters.cpp @@ -0,0 +1,631 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "filters.h" +#include "entitylist.h" +#include "ai_squad.h" +#include "ai_basenpc.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// ################################################################### +// > BaseFilter +// ################################################################### +LINK_ENTITY_TO_CLASS(filter_base, CBaseFilter); + +BEGIN_DATADESC( CBaseFilter ) + + DEFINE_KEYFIELD(m_bNegated, FIELD_BOOLEAN, "Negated"), + + // Inputs + DEFINE_INPUTFUNC( FIELD_INPUT, "TestActivator", InputTestActivator ), + + // Outputs + DEFINE_OUTPUT( m_OnPass, "OnPass"), + DEFINE_OUTPUT( m_OnFail, "OnFail"), + +END_DATADESC() + +//----------------------------------------------------------------------------- + +bool CBaseFilter::PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) +{ + return true; +} + + +bool CBaseFilter::PassesFilter( CBaseEntity *pCaller, CBaseEntity *pEntity ) +{ + bool baseResult = PassesFilterImpl( pCaller, pEntity ); + return (m_bNegated) ? !baseResult : baseResult; +} + + +bool CBaseFilter::PassesDamageFilter(const CTakeDamageInfo &info) +{ + bool baseResult = PassesDamageFilterImpl(info); + return (m_bNegated) ? !baseResult : baseResult; +} + + +bool CBaseFilter::PassesDamageFilterImpl( const CTakeDamageInfo &info ) +{ + return PassesFilterImpl( NULL, info.GetAttacker() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Input handler for testing the activator. If the activator passes the +// filter test, the OnPass output is fired. If not, the OnFail output is fired. +//----------------------------------------------------------------------------- +void CBaseFilter::InputTestActivator( inputdata_t &inputdata ) +{ + if ( PassesFilter( inputdata.pCaller, inputdata.pActivator ) ) + { + m_OnPass.FireOutput( inputdata.pActivator, this ); + } + else + { + m_OnFail.FireOutput( inputdata.pActivator, this ); + } +} + + +// ################################################################### +// > FilterMultiple +// +// Allows one to filter through mutiple filters +// ################################################################### +#define MAX_FILTERS 5 +enum filter_t +{ + FILTER_AND, + FILTER_OR, +}; + +class CFilterMultiple : public CBaseFilter +{ + DECLARE_CLASS( CFilterMultiple, CBaseFilter ); + DECLARE_DATADESC(); + + filter_t m_nFilterType; + string_t m_iFilterName[MAX_FILTERS]; + EHANDLE m_hFilter[MAX_FILTERS]; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ); + bool PassesDamageFilterImpl(const CTakeDamageInfo &info); + void Activate(void); +}; + +LINK_ENTITY_TO_CLASS(filter_multi, CFilterMultiple); + +BEGIN_DATADESC( CFilterMultiple ) + + + // Keys + DEFINE_KEYFIELD(m_nFilterType, FIELD_INTEGER, "FilterType"), + + // Silence, Classcheck! +// DEFINE_ARRAY( m_iFilterName, FIELD_STRING, MAX_FILTERS ), + + DEFINE_KEYFIELD(m_iFilterName[0], FIELD_STRING, "Filter01"), + DEFINE_KEYFIELD(m_iFilterName[1], FIELD_STRING, "Filter02"), + DEFINE_KEYFIELD(m_iFilterName[2], FIELD_STRING, "Filter03"), + DEFINE_KEYFIELD(m_iFilterName[3], FIELD_STRING, "Filter04"), + DEFINE_KEYFIELD(m_iFilterName[4], FIELD_STRING, "Filter05"), + DEFINE_ARRAY( m_hFilter, FIELD_EHANDLE, MAX_FILTERS ), + +END_DATADESC() + + + +//------------------------------------------------------------------------------ +// Purpose : Called after all entities have been loaded +//------------------------------------------------------------------------------ +void CFilterMultiple::Activate( void ) +{ + BaseClass::Activate(); + + // We may reject an entity specified in the array of names, but we want the array of valid filters to be contiguous! + int nNextFilter = 0; + + // Get handles to my filter entities + for ( int i = 0; i < MAX_FILTERS; i++ ) + { + if ( m_iFilterName[i] != NULL_STRING ) + { + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, m_iFilterName[i] ); + CBaseFilter *pFilter = dynamic_cast<CBaseFilter *>(pEntity); + if ( pFilter == NULL ) + { + Warning("filter_multi: Tried to add entity (%s) which is not a filter entity!\n", STRING( m_iFilterName[i] ) ); + continue; + } + + // Take this entity and increment out array pointer + m_hFilter[nNextFilter] = pFilter; + nNextFilter++; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the entity passes our filter, false if not. +// Input : pEntity - Entity to test. +//----------------------------------------------------------------------------- +bool CFilterMultiple::PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) +{ + // Test against each filter + if (m_nFilterType == FILTER_AND) + { + for (int i=0;i<MAX_FILTERS;i++) + { + if (m_hFilter[i] != NULL) + { + CBaseFilter* pFilter = (CBaseFilter *)(m_hFilter[i].Get()); + if (!pFilter->PassesFilter( pCaller, pEntity ) ) + { + return false; + } + } + } + return true; + } + else // m_nFilterType == FILTER_OR + { + for (int i=0;i<MAX_FILTERS;i++) + { + if (m_hFilter[i] != NULL) + { + CBaseFilter* pFilter = (CBaseFilter *)(m_hFilter[i].Get()); + if (pFilter->PassesFilter( pCaller, pEntity ) ) + { + return true; + } + } + } + return false; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the entity passes our filter, false if not. +// Input : pEntity - Entity to test. +//----------------------------------------------------------------------------- +bool CFilterMultiple::PassesDamageFilterImpl(const CTakeDamageInfo &info) +{ + // Test against each filter + if (m_nFilterType == FILTER_AND) + { + for (int i=0;i<MAX_FILTERS;i++) + { + if (m_hFilter[i] != NULL) + { + CBaseFilter* pFilter = (CBaseFilter *)(m_hFilter[i].Get()); + if (!pFilter->PassesDamageFilter(info)) + { + return false; + } + } + } + return true; + } + else // m_nFilterType == FILTER_OR + { + for (int i=0;i<MAX_FILTERS;i++) + { + if (m_hFilter[i] != NULL) + { + CBaseFilter* pFilter = (CBaseFilter *)(m_hFilter[i].Get()); + if (pFilter->PassesDamageFilter(info)) + { + return true; + } + } + } + return false; + } +} + + +// ################################################################### +// > FilterName +// ################################################################### +class CFilterName : public CBaseFilter +{ + DECLARE_CLASS( CFilterName, CBaseFilter ); + DECLARE_DATADESC(); + +public: + string_t m_iFilterName; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + // special check for !player as GetEntityName for player won't return "!player" as a name + if (FStrEq(STRING(m_iFilterName), "!player")) + { + return pEntity->IsPlayer(); + } + else + { + return pEntity->NameMatches( STRING(m_iFilterName) ); + } + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_name, CFilterName ); + +BEGIN_DATADESC( CFilterName ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ), + +END_DATADESC() + + + +// ################################################################### +// > FilterClass +// ################################################################### +class CFilterClass : public CBaseFilter +{ + DECLARE_CLASS( CFilterClass, CBaseFilter ); + DECLARE_DATADESC(); + +public: + string_t m_iFilterClass; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + return pEntity->ClassMatches( STRING(m_iFilterClass) ); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_class, CFilterClass ); + +BEGIN_DATADESC( CFilterClass ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterClass, FIELD_STRING, "filterclass" ), + +END_DATADESC() + + +// ################################################################### +// > FilterTeam +// ################################################################### +class FilterTeam : public CBaseFilter +{ + DECLARE_CLASS( FilterTeam, CBaseFilter ); + DECLARE_DATADESC(); + +public: + int m_iFilterTeam; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + return ( pEntity->GetTeamNumber() == m_iFilterTeam ); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_team, FilterTeam ); + +BEGIN_DATADESC( FilterTeam ) + + // Keyfields + DEFINE_KEYFIELD( m_iFilterTeam, FIELD_INTEGER, "filterteam" ), + +END_DATADESC() + + +// ################################################################### +// > FilterMassGreater +// ################################################################### +class CFilterMassGreater : public CBaseFilter +{ + DECLARE_CLASS( CFilterMassGreater, CBaseFilter ); + DECLARE_DATADESC(); + +public: + float m_fFilterMass; + + bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + if ( pEntity->VPhysicsGetObject() == NULL ) + return false; + + return ( pEntity->VPhysicsGetObject()->GetMass() > m_fFilterMass ); + } +}; + +LINK_ENTITY_TO_CLASS( filter_activator_mass_greater, CFilterMassGreater ); + +BEGIN_DATADESC( CFilterMassGreater ) + +// Keyfields +DEFINE_KEYFIELD( m_fFilterMass, FIELD_FLOAT, "filtermass" ), + +END_DATADESC() + + +// ################################################################### +// > FilterDamageType +// ################################################################### +class FilterDamageType : public CBaseFilter +{ + DECLARE_CLASS( FilterDamageType, CBaseFilter ); + DECLARE_DATADESC(); + +protected: + + bool PassesFilterImpl(CBaseEntity *pCaller, CBaseEntity *pEntity ) + { + ASSERT( false ); + return true; + } + + bool PassesDamageFilterImpl(const CTakeDamageInfo &info) + { + return info.GetDamageType() == m_iDamageType; + } + + int m_iDamageType; +}; + +LINK_ENTITY_TO_CLASS( filter_damage_type, FilterDamageType ); + +BEGIN_DATADESC( FilterDamageType ) + + // Keyfields + DEFINE_KEYFIELD( m_iDamageType, FIELD_INTEGER, "damagetype" ), + +END_DATADESC() + +// ################################################################### +// > CFilterEnemy +// ################################################################### + +#define SF_FILTER_ENEMY_NO_LOSE_AQUIRED (1<<0) + +class CFilterEnemy : public CBaseFilter +{ + DECLARE_CLASS( CFilterEnemy, CBaseFilter ); + // NOT SAVED + // m_iszPlayerName + DECLARE_DATADESC(); + +public: + + virtual bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ); + virtual bool PassesDamageFilterImpl( const CTakeDamageInfo &info ); + +private: + + bool PassesNameFilter( CBaseEntity *pCaller ); + bool PassesProximityFilter( CBaseEntity *pCaller, CBaseEntity *pEnemy ); + bool PassesMobbedFilter( CBaseEntity *pCaller, CBaseEntity *pEnemy ); + + string_t m_iszEnemyName; // Name or classname + float m_flRadius; // Radius (enemies are acquired at this range) + float m_flOuterRadius; // Outer radius (enemies are LOST at this range) + int m_nMaxSquadmatesPerEnemy; // Maximum number of squadmates who may share the same enemy + string_t m_iszPlayerName; // "!player" +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CFilterEnemy::PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) +{ + if ( pCaller == NULL || pEntity == NULL ) + return false; + + // If asked to, we'll never fail to pass an already acquired enemy + // This allows us to use test criteria to initially pick an enemy, then disregard the test until a new enemy comes along + if ( HasSpawnFlags( SF_FILTER_ENEMY_NO_LOSE_AQUIRED ) && ( pEntity == pCaller->GetEnemy() ) ) + return true; + + // This is a little weird, but it's saying that if we're not the entity we're excluding the filter to, then just pass it throughZ + if ( PassesNameFilter( pEntity ) == false ) + return true; + + if ( PassesProximityFilter( pCaller, pEntity ) == false ) + return false; + + // NOTE: This can result in some weird NPC behavior if used improperly + if ( PassesMobbedFilter( pCaller, pEntity ) == false ) + return false; + + // The filter has been passed, meaning: + // - If we wanted all criteria to fail, they have + // - If we wanted all criteria to succeed, they have + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CFilterEnemy::PassesDamageFilterImpl( const CTakeDamageInfo &info ) +{ + // NOTE: This function has no meaning to this implementation of the filter class! + Assert( 0 ); + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Tests the enemy's name or classname +// Input : *pEnemy - Entity being assessed +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CFilterEnemy::PassesNameFilter( CBaseEntity *pEnemy ) +{ + // If there is no name specified, we're not using it + if ( m_iszEnemyName == NULL_STRING ) + return true; + + // Cache off the special case player name + if ( m_iszPlayerName == NULL_STRING ) + { + m_iszPlayerName = FindPooledString( "!player" ); + } + + if ( m_iszEnemyName == m_iszPlayerName ) + { + if ( pEnemy->IsPlayer() ) + { + if ( m_bNegated ) + return false; + + return true; + } + } + + // May be either a targetname or classname + bool bNameOrClassnameMatches = ( m_iszEnemyName == pEnemy->GetEntityName() || m_iszEnemyName == pEnemy->m_iClassname ); + + // We only leave this code block in a state meaning we've "succeeded" in any context + if ( m_bNegated ) + { + // We wanted the names to not match, but they did + if ( bNameOrClassnameMatches ) + return false; + } + else + { + // We wanted them to be the same, but they weren't + if ( bNameOrClassnameMatches == false ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Tests the enemy's proximity to the caller's position +// Input : *pCaller - Entity assessing the target +// *pEnemy - Entity being assessed +// Output : Returns true if potential enemy passes this filter stage +//----------------------------------------------------------------------------- +bool CFilterEnemy::PassesProximityFilter( CBaseEntity *pCaller, CBaseEntity *pEnemy ) +{ + // If there is no radius specified, we're not testing it + if ( m_flRadius <= 0.0f ) + return true; + + // We test the proximity differently when we've already picked up this enemy before + bool bAlreadyEnemy = ( pCaller->GetEnemy() == pEnemy ); + + // Get our squared length to the enemy from the caller + float flDistToEnemySqr = ( pCaller->GetAbsOrigin() - pEnemy->GetAbsOrigin() ).LengthSqr(); + + // Two radii are used to control oscillation between true/false cases + // The larger radius is either specified or defaulted to be double or half the size of the inner radius + float flLargerRadius = m_flOuterRadius; + if ( flLargerRadius == 0 ) + { + flLargerRadius = ( m_bNegated ) ? (m_flRadius*0.5f) : (m_flRadius*2.0f); + } + + float flSmallerRadius = m_flRadius; + if ( flSmallerRadius > flLargerRadius ) + { + ::V_swap( flLargerRadius, flSmallerRadius ); + } + + float flDist; + if ( bAlreadyEnemy ) + { + flDist = ( m_bNegated ) ? flSmallerRadius : flLargerRadius; + } + else + { + flDist = ( m_bNegated ) ? flLargerRadius : flSmallerRadius; + } + + // Test for success + if ( flDistToEnemySqr <= (flDist*flDist) ) + { + // We wanted to fail but didn't + if ( m_bNegated ) + return false; + + return true; + } + + // We wanted to succeed but didn't + if ( m_bNegated == false ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Attempt to govern how many squad members can target any given entity +// Input : *pCaller - Entity assessing the target +// *pEnemy - Entity being assessed +// Output : Returns true if potential enemy passes this filter stage +//----------------------------------------------------------------------------- +bool CFilterEnemy::PassesMobbedFilter( CBaseEntity *pCaller, CBaseEntity *pEnemy ) +{ + // Must be a valid candidate + CAI_BaseNPC *pNPC = pCaller->MyNPCPointer(); + if ( pNPC == NULL || pNPC->GetSquad() == NULL ) + return true; + + // Make sure we're checking for this + if ( m_nMaxSquadmatesPerEnemy <= 0 ) + return true; + + AISquadIter_t iter; + int nNumMatchingSquadmates = 0; + + // Look through our squad members to see how many of them are already mobbing this entity + for ( CAI_BaseNPC *pSquadMember = pNPC->GetSquad()->GetFirstMember( &iter ); pSquadMember != NULL; pSquadMember = pNPC->GetSquad()->GetNextMember( &iter ) ) + { + // Disregard ourself + if ( pSquadMember == pNPC ) + continue; + + // If the enemies match, count it + if ( pSquadMember->GetEnemy() == pEnemy ) + { + nNumMatchingSquadmates++; + + // If we're at or passed the max we stop + if ( nNumMatchingSquadmates >= m_nMaxSquadmatesPerEnemy ) + { + // We wanted to find more than allowed and we did + if ( m_bNegated ) + return true; + + // We wanted to be less but we're not + return false; + } + } + } + + // We wanted to find more than the allowed amount but we didn't + if ( m_bNegated ) + return false; + + return true; +} + +LINK_ENTITY_TO_CLASS( filter_enemy, CFilterEnemy ); + +BEGIN_DATADESC( CFilterEnemy ) + + DEFINE_KEYFIELD( m_iszEnemyName, FIELD_STRING, "filtername" ), + DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "filter_radius" ), + DEFINE_KEYFIELD( m_flOuterRadius, FIELD_FLOAT, "filter_outer_radius" ), + DEFINE_KEYFIELD( m_nMaxSquadmatesPerEnemy, FIELD_INTEGER, "filter_max_per_enemy" ), + DEFINE_FIELD( m_iszPlayerName, FIELD_STRING ), + +END_DATADESC() |