Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 119 additions & 1 deletion src/game/server/ai_scriptconditions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" );

Expand Down Expand Up @@ -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

//---------------------------------

Expand Down Expand Up @@ -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 )
Expand All @@ -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 ),
Expand All @@ -151,6 +202,8 @@ CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_Evaluators[] =

};

#endif

void CAI_ScriptConditions::OnRestore( void )
{
BaseClass::OnRestore();
Expand Down Expand Up @@ -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] );

Expand All @@ -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() ) );
Expand Down Expand Up @@ -656,6 +750,13 @@ void CAI_ScriptConditions::Enable( void )
}
}

#ifdef MAPBASE_MP
if ( m_iszPlayerFilterName != NULL_STRING )
{
m_hPlayerFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this ));
}
#endif

m_fDisabled = false;

SetThink( &CAI_ScriptConditions::EvaluationThink );
Expand Down Expand Up @@ -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<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this ));
}
else
{
m_hPlayerFilter = NULL;
}
}
#endif

//-----------------------------------------------------------------------------

bool CAI_ScriptConditions::IsInFOV( CBaseEntity *pViewer, CBaseEntity *pViewed, float fov, bool bTrueCone )
Expand Down
13 changes: 12 additions & 1 deletion src/game/server/ai_scriptconditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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[];

//---------------------------------
Expand Down Expand Up @@ -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<CBaseFilter> m_hPlayerFilter;
#endif

CUtlVector< CAI_ScriptConditionsElement > m_ElementList;

//---------------------------------
Expand Down
85 changes: 85 additions & 0 deletions src/game/server/env_player_surface_trigger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ),
Expand All @@ -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
Expand All @@ -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 );
Expand Down Expand Up @@ -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 ) )
{
Expand All @@ -100,6 +132,7 @@ void CEnvPlayerSurfaceTrigger::PlayerSurfaceChanged( CBasePlayer *pPlayer, char
SetThink( &CEnvPlayerSurfaceTrigger::UpdateMaterialThink );
SetNextThink( gpGlobals->curtime );
}
#endif
}

//-----------------------------------------------------------------------------
Expand All @@ -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
}

//-----------------------------------------------------------------------------
Expand All @@ -124,6 +206,9 @@ void CEnvPlayerSurfaceTrigger::UpdateMaterialThink( void )
void CEnvPlayerSurfaceTrigger::InputDisable( inputdata_t &inputdata )
{
m_bDisabled = true;
#ifdef MAPBASE_MP
m_nNumOnMaterial = 0;
#endif
}

//-----------------------------------------------------------------------------
Expand Down
10 changes: 10 additions & 0 deletions src/game/server/env_player_surface_trigger.h
Original file line number Diff line number Diff line change
Expand Up @@ -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