diff --git a/src/game/server/ai_scriptconditions.cpp b/src/game/server/ai_scriptconditions.cpp index 7e637ba5190..d1fdf4c22ce 100644 --- a/src/game/server/ai_scriptconditions.cpp +++ b/src/game/server/ai_scriptconditions.cpp @@ -14,10 +14,17 @@ #include "ai_scriptconditions.h" #include "saverestore_utlvector.h" +#ifdef MAPBASE_MP +#include "filters.h" +#endif + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define SF_ACTOR_AS_ACTIVATOR ( 1 << 0 ) +#ifdef MAPBASE +#define SF_PLAYER_AS_ACTIVATOR ( 1 << 1 ) // Mainly useful in MP +#endif ConVar debugscriptconditions( "ai_debugscriptconditions", "0" ); @@ -55,6 +62,9 @@ BEGIN_DATADESC( CAI_ScriptConditions ) #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_EHANDLE, "SatisfyConditions", InputSatisfyConditions ), #endif +#ifdef MAPBASE_MP + DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerFilter", InputSetPlayerFilter ), +#endif //--------------------------------- @@ -111,6 +121,11 @@ BEGIN_DATADESC( CAI_ScriptConditions ) DEFINE_UTLVECTOR( m_ElementList, FIELD_EMBEDDED ), DEFINE_FIELD( m_bLeaveAsleep, FIELD_BOOLEAN ), +#ifdef MAPBASE_MP + DEFINE_KEYFIELD( m_iszPlayerFilterName, FIELD_STRING, "PlayerFilter" ), + DEFINE_FIELD( m_hPlayerFilter, FIELD_EHANDLE ), +#endif + END_DATADESC() BEGIN_SIMPLE_DATADESC( CAI_ProxTester ) @@ -129,6 +144,42 @@ END_DATADESC() #define EVALUATOR( name ) { &CAI_ScriptConditions::Eval##name, #name } +#ifdef MAPBASE_MP + +CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_PlayerEvaluators[] = +{ + EVALUATOR( ActorSeePlayer ), + EVALUATOR( PlayerActorProximity ), + EVALUATOR( PlayerTargetProximity ), + EVALUATOR( PlayerBlockingActor ), + EVALUATOR( PlayerActorLook ), + EVALUATOR( PlayerTargetLook ), + EVALUATOR( PlayerActorLOS ), + EVALUATOR( PlayerTargetLOS ), + +#ifdef HL2_EPISODIC + EVALUATOR( PlayerInVehicle ), +#endif + +}; + +// !!! In MP, this is only used for evaluators that don't use players. +// Use gm_PlayerEvaluators for evaluators that require a player. +CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_Evaluators[] = +{ + EVALUATOR( State ), + EVALUATOR( ActorTargetProximity ), + EVALUATOR( ActorSeeTarget), + +#ifdef HL2_EPISODIC + EVALUATOR( ActorInPVS ), + EVALUATOR( ActorInVehicle ), +#endif + +}; + +#else + CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_Evaluators[] = { EVALUATOR( ActorSeePlayer ), @@ -151,6 +202,8 @@ CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_Evaluators[] = }; +#endif + void CAI_ScriptConditions::OnRestore( void ) { BaseClass::OnRestore(); @@ -537,7 +590,7 @@ void CAI_ScriptConditions::EvaluationThink() m_OnConditionsTimeout.FireOutput( pActivator, this ); continue; } - + bool result = true; const int nEvaluators = sizeof( gm_Evaluators ) / sizeof( gm_Evaluators[0] ); @@ -561,6 +614,47 @@ void CAI_ScriptConditions::EvaluationThink() } } +#ifdef MAPBASE_MP + if ( result ) + { + // Loop through all of the players until one of them passes. If we've been given a filter, use it to determine which players to evaluate. + // + // You may notice this ignores GetPlayer() and is implemented more primitively than how ai_script_conditions handles multiple actors. + // ai_script_conditions was designed to evaluate conditions on multiple actors simultaneously, but it was only designed with one player in mind. + // Having the existing element list account for multiple players is theoretically possible, but each element is designed to time out and fire outputs separately. + // Since existing instances of the entity were not designed for that functionality and it would make the entity much more complex to manage even under new cases, + // I've elected to go for a simpler implementation that just loops through all of the players every time an actor's condition needs to be evaluated. + const int nPlayerEvaluators = sizeof( gm_PlayerEvaluators ) / sizeof( gm_PlayerEvaluators[0] ); + for ( int playerIdx = 1; playerIdx <= gpGlobals->maxClients; playerIdx++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( playerIdx ); + if ( !pPlayer || ( m_hPlayerFilter && !m_hPlayerFilter->PassesFilter( this, pPlayer ) ) ) + continue; + + result = true; + args.pPlayer = pPlayer; + + ScrCondDbgMsg( ("%s evaluating player: %s [%i]\n", GetDebugName(), pPlayer->GetPlayerName(), playerIdx) ); + + for ( int i = 0; i < nPlayerEvaluators; ++i ) + { + if ( !(this->*gm_PlayerEvaluators[i].pfnEvaluator)( args ) ) + { + pConditionElement->GetTimer()->Reset(); + result = false; + + ScrCondDbgMsg( ( "\t%s failed on: %s\n", GetDebugName(), gm_PlayerEvaluators[ i ].pszName ) ); + + break; + } + } + + if ( result ) + break; + } + } +#endif + if ( result ) { ScrCondDbgMsg( ( "%s waiting... %f\n", GetDebugName(), pConditionElement->GetTimer()->GetRemaining() ) ); @@ -656,6 +750,13 @@ void CAI_ScriptConditions::Enable( void ) } } +#ifdef MAPBASE_MP + if ( m_iszPlayerFilterName != NULL_STRING ) + { + m_hPlayerFilter = dynamic_cast(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this )); + } +#endif + m_fDisabled = false; SetThink( &CAI_ScriptConditions::EvaluationThink ); @@ -706,6 +807,23 @@ void CAI_ScriptConditions::InputSatisfyConditions( inputdata_t &inputdata ) } #endif +#ifdef MAPBASE_MP +//----------------------------------------------------------------------------- + +void CAI_ScriptConditions::InputSetPlayerFilter( inputdata_t &inputdata ) +{ + m_iszPlayerFilterName = inputdata.value.StringID(); + if ( m_iszPlayerFilterName != NULL_STRING ) + { + m_hPlayerFilter = dynamic_cast(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this )); + } + else + { + m_hPlayerFilter = NULL; + } +} +#endif + //----------------------------------------------------------------------------- bool CAI_ScriptConditions::IsInFOV( CBaseEntity *pViewer, CBaseEntity *pViewed, float fov, bool bTrueCone ) diff --git a/src/game/server/ai_scriptconditions.h b/src/game/server/ai_scriptconditions.h index 10e5929bac2..25418c0d172 100644 --- a/src/game/server/ai_scriptconditions.h +++ b/src/game/server/ai_scriptconditions.h @@ -162,6 +162,9 @@ class CAI_ScriptConditions : public CBaseEntity, public IEntityListener #ifdef MAPBASE void InputSatisfyConditions( inputdata_t &inputdata ); #endif +#ifdef MAPBASE_MP + void InputSetPlayerFilter( inputdata_t &inputdata ); +#endif // Output handlers COutputEvent m_OnConditionsSatisfied; @@ -186,7 +189,10 @@ class CAI_ScriptConditions : public CBaseEntity, public IEntityListener EvaluationFunc_t pfnEvaluator; const char *pszName; }; - + +#ifdef MAPBASE_MP + static EvaluatorInfo_t gm_PlayerEvaluators[]; +#endif static EvaluatorInfo_t gm_Evaluators[]; //--------------------------------- @@ -247,6 +253,11 @@ class CAI_ScriptConditions : public CBaseEntity, public IEntityListener ThreeState_t m_fActorInVehicle; ThreeState_t m_fPlayerInVehicle; +#ifdef MAPBASE_MP + string_t m_iszPlayerFilterName; + CHandle m_hPlayerFilter; +#endif + CUtlVector< CAI_ScriptConditionsElement > m_ElementList; //--------------------------------- diff --git a/src/game/server/env_player_surface_trigger.cpp b/src/game/server/env_player_surface_trigger.cpp index e25038ec383..7b3a250c320 100644 --- a/src/game/server/env_player_surface_trigger.cpp +++ b/src/game/server/env_player_surface_trigger.cpp @@ -15,7 +15,13 @@ LINK_ENTITY_TO_CLASS( env_player_surface_trigger, CEnvPlayerSurfaceTrigger ); BEGIN_DATADESC( CEnvPlayerSurfaceTrigger ) DEFINE_KEYFIELD( m_iTargetGameMaterial, FIELD_INTEGER, "gamematerial" ), +#ifdef MAPBASE_MP + DEFINE_AUTO_ARRAY( m_iCurrentGameMaterial, FIELD_INTEGER ), + DEFINE_AUTO_ARRAY( m_iLastGameMaterial, FIELD_INTEGER ), + DEFINE_FIELD( m_nNumOnMaterial, FIELD_INTEGER ), +#else DEFINE_FIELD( m_iCurrentGameMaterial, FIELD_INTEGER ), +#endif DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ), DEFINE_THINKFUNC( UpdateMaterialThink ), @@ -27,6 +33,11 @@ BEGIN_DATADESC( CEnvPlayerSurfaceTrigger ) // Outputs DEFINE_OUTPUT(m_OnSurfaceChangedToTarget, "OnSurfaceChangedToTarget"), DEFINE_OUTPUT(m_OnSurfaceChangedFromTarget, "OnSurfaceChangedFromTarget"), +#ifdef MAPBASE + // Used in MP + DEFINE_OUTPUT( m_OnSurfaceChangedToTargetAll, "OnSurfaceChangedToTargetAll" ), + DEFINE_OUTPUT( m_OnSurfaceChangedFromTargetAll, "OnSurfaceChangedFromTargetAll" ), +#endif END_DATADESC() // Global list of surface triggers @@ -48,7 +59,15 @@ void CEnvPlayerSurfaceTrigger::Spawn( void ) SetSolid( SOLID_NONE ); SetMoveType( MOVETYPE_NONE ); +#ifdef MAPBASE_MP + for (int i = 0; i < MAX_PLAYERS; i++) + { + m_iCurrentGameMaterial[i] = 0; + m_iLastGameMaterial[i] = 0; + } +#else m_iCurrentGameMaterial = 0; +#endif m_bDisabled = false; g_PlayerSurfaceTriggers.AddToTail( this ); @@ -90,6 +109,19 @@ void CEnvPlayerSurfaceTrigger::PlayerSurfaceChanged( CBasePlayer *pPlayer, char return; // Fire the output if we've changed, but only if it involves the target material +#ifdef MAPBASE_MP + int idx = pPlayer->entindex(); + if ( gameMaterial != (char)(m_iCurrentGameMaterial[idx]) && + ( gameMaterial == m_iTargetGameMaterial || m_iCurrentGameMaterial[idx] == m_iTargetGameMaterial ) ) + { + DevMsg( 2, "Player changed material to %d (was %d)\n", gameMaterial, m_iCurrentGameMaterial[idx] ); + + m_iCurrentGameMaterial[idx] = (int)gameMaterial; + + SetThink( &CEnvPlayerSurfaceTrigger::UpdateMaterialThink ); + SetNextThink( gpGlobals->curtime ); + } +#else if ( gameMaterial != (char)m_iCurrentGameMaterial && ( gameMaterial == m_iTargetGameMaterial || m_iCurrentGameMaterial == m_iTargetGameMaterial ) ) { @@ -100,6 +132,7 @@ void CEnvPlayerSurfaceTrigger::PlayerSurfaceChanged( CBasePlayer *pPlayer, char SetThink( &CEnvPlayerSurfaceTrigger::UpdateMaterialThink ); SetNextThink( gpGlobals->curtime ); } +#endif } //----------------------------------------------------------------------------- @@ -108,14 +141,63 @@ void CEnvPlayerSurfaceTrigger::PlayerSurfaceChanged( CBasePlayer *pPlayer, char //----------------------------------------------------------------------------- void CEnvPlayerSurfaceTrigger::UpdateMaterialThink( void ) { +#ifdef MAPBASE_MP + CBasePlayer *pFirstPlayer = NULL; + int nNumOnMaterialLast = m_nNumOnMaterial; + + for (int i = 0; i < gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if (!pFirstPlayer) + pFirstPlayer = pPlayer; + + if ( m_iCurrentGameMaterial[i] != m_iLastGameMaterial[i] ) + { + if ( m_iCurrentGameMaterial[i] == m_iTargetGameMaterial ) + { + m_OnSurfaceChangedToTarget.FireOutput( pPlayer, this ); + m_nNumOnMaterial++; + } + else + { + m_OnSurfaceChangedFromTarget.FireOutput( pPlayer, this ); + + if (m_nNumOnMaterial > 0) + m_nNumOnMaterial--; + } + } + + m_iLastGameMaterial[i] = m_iCurrentGameMaterial[i]; + } + + if ( nNumOnMaterialLast == 0 && m_nNumOnMaterial > 0 ) + { + m_OnSurfaceChangedToTargetAll.FireOutput( pFirstPlayer, this ); + } + else if ( nNumOnMaterialLast > 0 && m_nNumOnMaterial == 0 ) + { + m_OnSurfaceChangedFromTargetAll.FireOutput( pFirstPlayer, this ); + } +#else if ( m_iCurrentGameMaterial == m_iTargetGameMaterial ) { m_OnSurfaceChangedToTarget.FireOutput( NULL, this ); + +#ifdef MAPBASE + // This is used in MP, but SP has only one player, so just fire it here + m_OnSurfaceChangedToTargetAll.FireOutput( NULL, this ); +#endif } else { m_OnSurfaceChangedFromTarget.FireOutput( NULL, this ); + +#ifdef MAPBASE + // This is used in MP, but SP has only one player, so just fire it here + m_OnSurfaceChangedFromTargetAll.FireOutput( NULL, this ); +#endif } +#endif } //----------------------------------------------------------------------------- @@ -124,6 +206,9 @@ void CEnvPlayerSurfaceTrigger::UpdateMaterialThink( void ) void CEnvPlayerSurfaceTrigger::InputDisable( inputdata_t &inputdata ) { m_bDisabled = true; +#ifdef MAPBASE_MP + m_nNumOnMaterial = 0; +#endif } //----------------------------------------------------------------------------- diff --git a/src/game/server/env_player_surface_trigger.h b/src/game/server/env_player_surface_trigger.h index 96eb0810d9c..0eae16d6534 100644 --- a/src/game/server/env_player_surface_trigger.h +++ b/src/game/server/env_player_surface_trigger.h @@ -38,12 +38,22 @@ class CEnvPlayerSurfaceTrigger : public CPointEntity private: int m_iTargetGameMaterial; +#ifdef MAPBASE_MP + int m_iCurrentGameMaterial[MAX_PLAYERS]; + int m_iLastGameMaterial[MAX_PLAYERS]; + int m_nNumOnMaterial; +#else int m_iCurrentGameMaterial; +#endif bool m_bDisabled; // Outputs COutputEvent m_OnSurfaceChangedToTarget; COutputEvent m_OnSurfaceChangedFromTarget; +#ifdef MAPBASE + COutputEvent m_OnSurfaceChangedToTargetAll; + COutputEvent m_OnSurfaceChangedFromTargetAll; +#endif }; #endif // ENV_PLAYER_SURFACE_TRIGGER_H