Skip to content

Commit 12dc0c9

Browse files
committed
Add MP implementation of env_player_surface_trigger and ai_script_conditions
1 parent 24f9063 commit 12dc0c9

4 files changed

Lines changed: 226 additions & 2 deletions

File tree

src/game/server/ai_scriptconditions.cpp

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,17 @@
1414
#include "ai_scriptconditions.h"
1515
#include "saverestore_utlvector.h"
1616

17+
#ifdef MAPBASE_MP
18+
#include "filters.h"
19+
#endif
20+
1721
// memdbgon must be the last include file in a .cpp file!!!
1822
#include "tier0/memdbgon.h"
1923

2024
#define SF_ACTOR_AS_ACTIVATOR ( 1 << 0 )
25+
#ifdef MAPBASE
26+
#define SF_PLAYER_AS_ACTIVATOR ( 1 << 1 ) // Mainly useful in MP
27+
#endif
2128

2229
ConVar debugscriptconditions( "ai_debugscriptconditions", "0" );
2330

@@ -55,6 +62,9 @@ BEGIN_DATADESC( CAI_ScriptConditions )
5562
#ifdef MAPBASE
5663
DEFINE_INPUTFUNC( FIELD_EHANDLE, "SatisfyConditions", InputSatisfyConditions ),
5764
#endif
65+
#ifdef MAPBASE_MP
66+
DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerFilter", InputSetPlayerFilter ),
67+
#endif
5868

5969
//---------------------------------
6070

@@ -111,6 +121,11 @@ BEGIN_DATADESC( CAI_ScriptConditions )
111121
DEFINE_UTLVECTOR( m_ElementList, FIELD_EMBEDDED ),
112122
DEFINE_FIELD( m_bLeaveAsleep, FIELD_BOOLEAN ),
113123

124+
#ifdef MAPBASE_MP
125+
DEFINE_KEYFIELD( m_iszPlayerFilterName, FIELD_STRING, "PlayerFilter" ),
126+
DEFINE_FIELD( m_hPlayerFilter, FIELD_EHANDLE ),
127+
#endif
128+
114129
END_DATADESC()
115130

116131
BEGIN_SIMPLE_DATADESC( CAI_ProxTester )
@@ -129,6 +144,42 @@ END_DATADESC()
129144

130145
#define EVALUATOR( name ) { &CAI_ScriptConditions::Eval##name, #name }
131146

147+
#ifdef MAPBASE_MP
148+
149+
CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_PlayerEvaluators[] =
150+
{
151+
EVALUATOR( ActorSeePlayer ),
152+
EVALUATOR( PlayerActorProximity ),
153+
EVALUATOR( PlayerTargetProximity ),
154+
EVALUATOR( PlayerBlockingActor ),
155+
EVALUATOR( PlayerActorLook ),
156+
EVALUATOR( PlayerTargetLook ),
157+
EVALUATOR( PlayerActorLOS ),
158+
EVALUATOR( PlayerTargetLOS ),
159+
160+
#ifdef HL2_EPISODIC
161+
EVALUATOR( PlayerInVehicle ),
162+
#endif
163+
164+
};
165+
166+
// !!! In MP, this is only used for evaluators that don't use players.
167+
// Use gm_PlayerEvaluators for evaluators that require a player.
168+
CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_Evaluators[] =
169+
{
170+
EVALUATOR( State ),
171+
EVALUATOR( ActorTargetProximity ),
172+
EVALUATOR( ActorSeeTarget),
173+
174+
#ifdef HL2_EPISODIC
175+
EVALUATOR( ActorInPVS ),
176+
EVALUATOR( ActorInVehicle ),
177+
#endif
178+
179+
};
180+
181+
#else
182+
132183
CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_Evaluators[] =
133184
{
134185
EVALUATOR( ActorSeePlayer ),
@@ -151,6 +202,8 @@ CAI_ScriptConditions::EvaluatorInfo_t CAI_ScriptConditions::gm_Evaluators[] =
151202

152203
};
153204

205+
#endif
206+
154207
void CAI_ScriptConditions::OnRestore( void )
155208
{
156209
BaseClass::OnRestore();
@@ -537,7 +590,7 @@ void CAI_ScriptConditions::EvaluationThink()
537590
m_OnConditionsTimeout.FireOutput( pActivator, this );
538591
continue;
539592
}
540-
593+
541594
bool result = true;
542595
const int nEvaluators = sizeof( gm_Evaluators ) / sizeof( gm_Evaluators[0] );
543596

@@ -561,6 +614,47 @@ void CAI_ScriptConditions::EvaluationThink()
561614
}
562615
}
563616

617+
#ifdef MAPBASE_MP
618+
if ( result )
619+
{
620+
// 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.
621+
//
622+
// You may notice this ignores GetPlayer() and is implemented more primitively than how ai_script_conditions handles multiple actors.
623+
// ai_script_conditions was designed to evaluate conditions on multiple actors simultaneously, but it was only designed with one player in mind.
624+
// Having the existing element list account for multiple players is theoretically possible, but each element is designed to time out and fire outputs separately.
625+
// 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,
626+
// 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.
627+
const int nPlayerEvaluators = sizeof( gm_PlayerEvaluators ) / sizeof( gm_PlayerEvaluators[0] );
628+
for ( int playerIdx = 1; playerIdx <= gpGlobals->maxClients; playerIdx++ )
629+
{
630+
CBasePlayer *pPlayer = UTIL_PlayerByIndex( playerIdx );
631+
if ( !pPlayer || ( m_hPlayerFilter && !m_hPlayerFilter->PassesFilter( this, pPlayer ) ) )
632+
continue;
633+
634+
result = true;
635+
args.pPlayer = pPlayer;
636+
637+
ScrCondDbgMsg( ("%s evaluating player: %s [%i]\n", GetDebugName(), pPlayer->GetPlayerName(), playerIdx) );
638+
639+
for ( int i = 0; i < nPlayerEvaluators; ++i )
640+
{
641+
if ( !(this->*gm_PlayerEvaluators[i].pfnEvaluator)( args ) )
642+
{
643+
pConditionElement->GetTimer()->Reset();
644+
result = false;
645+
646+
ScrCondDbgMsg( ( "\t%s failed on: %s\n", GetDebugName(), gm_PlayerEvaluators[ i ].pszName ) );
647+
648+
break;
649+
}
650+
}
651+
652+
if ( result )
653+
break;
654+
}
655+
}
656+
#endif
657+
564658
if ( result )
565659
{
566660
ScrCondDbgMsg( ( "%s waiting... %f\n", GetDebugName(), pConditionElement->GetTimer()->GetRemaining() ) );
@@ -656,6 +750,13 @@ void CAI_ScriptConditions::Enable( void )
656750
}
657751
}
658752

753+
#ifdef MAPBASE_MP
754+
if ( m_iszPlayerFilterName != NULL_STRING )
755+
{
756+
m_hPlayerFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this ));
757+
}
758+
#endif
759+
659760
m_fDisabled = false;
660761

661762
SetThink( &CAI_ScriptConditions::EvaluationThink );
@@ -706,6 +807,23 @@ void CAI_ScriptConditions::InputSatisfyConditions( inputdata_t &inputdata )
706807
}
707808
#endif
708809

810+
#ifdef MAPBASE_MP
811+
//-----------------------------------------------------------------------------
812+
813+
void CAI_ScriptConditions::InputSetPlayerFilter( inputdata_t &inputdata )
814+
{
815+
m_iszPlayerFilterName = inputdata.value.StringID();
816+
if ( m_iszPlayerFilterName != NULL_STRING )
817+
{
818+
m_hPlayerFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this ));
819+
}
820+
else
821+
{
822+
m_hPlayerFilter = NULL;
823+
}
824+
}
825+
#endif
826+
709827
//-----------------------------------------------------------------------------
710828

711829
bool CAI_ScriptConditions::IsInFOV( CBaseEntity *pViewer, CBaseEntity *pViewed, float fov, bool bTrueCone )

src/game/server/ai_scriptconditions.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ class CAI_ScriptConditions : public CBaseEntity, public IEntityListener
162162
#ifdef MAPBASE
163163
void InputSatisfyConditions( inputdata_t &inputdata );
164164
#endif
165+
#ifdef MAPBASE_MP
166+
void InputSetPlayerFilter( inputdata_t &inputdata );
167+
#endif
165168

166169
// Output handlers
167170
COutputEvent m_OnConditionsSatisfied;
@@ -186,7 +189,10 @@ class CAI_ScriptConditions : public CBaseEntity, public IEntityListener
186189
EvaluationFunc_t pfnEvaluator;
187190
const char *pszName;
188191
};
189-
192+
193+
#ifdef MAPBASE_MP
194+
static EvaluatorInfo_t gm_PlayerEvaluators[];
195+
#endif
190196
static EvaluatorInfo_t gm_Evaluators[];
191197

192198
//---------------------------------
@@ -247,6 +253,11 @@ class CAI_ScriptConditions : public CBaseEntity, public IEntityListener
247253
ThreeState_t m_fActorInVehicle;
248254
ThreeState_t m_fPlayerInVehicle;
249255

256+
#ifdef MAPBASE_MP
257+
string_t m_iszPlayerFilterName;
258+
CHandle<CBaseFilter> m_hPlayerFilter;
259+
#endif
260+
250261
CUtlVector< CAI_ScriptConditionsElement > m_ElementList;
251262

252263
//---------------------------------

src/game/server/env_player_surface_trigger.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ LINK_ENTITY_TO_CLASS( env_player_surface_trigger, CEnvPlayerSurfaceTrigger );
1515

1616
BEGIN_DATADESC( CEnvPlayerSurfaceTrigger )
1717
DEFINE_KEYFIELD( m_iTargetGameMaterial, FIELD_INTEGER, "gamematerial" ),
18+
#ifdef MAPBASE_MP
19+
DEFINE_AUTO_ARRAY( m_iCurrentGameMaterial, FIELD_INTEGER ),
20+
DEFINE_AUTO_ARRAY( m_iLastGameMaterial, FIELD_INTEGER ),
21+
DEFINE_FIELD( m_nNumOnMaterial, FIELD_INTEGER ),
22+
#else
1823
DEFINE_FIELD( m_iCurrentGameMaterial, FIELD_INTEGER ),
24+
#endif
1925
DEFINE_FIELD( m_bDisabled, FIELD_BOOLEAN ),
2026

2127
DEFINE_THINKFUNC( UpdateMaterialThink ),
@@ -27,6 +33,11 @@ BEGIN_DATADESC( CEnvPlayerSurfaceTrigger )
2733
// Outputs
2834
DEFINE_OUTPUT(m_OnSurfaceChangedToTarget, "OnSurfaceChangedToTarget"),
2935
DEFINE_OUTPUT(m_OnSurfaceChangedFromTarget, "OnSurfaceChangedFromTarget"),
36+
#ifdef MAPBASE
37+
// Used in MP
38+
DEFINE_OUTPUT( m_OnSurfaceChangedToTargetAll, "OnSurfaceChangedToTargetAll" ),
39+
DEFINE_OUTPUT( m_OnSurfaceChangedFromTargetAll, "OnSurfaceChangedFromTargetAll" ),
40+
#endif
3041
END_DATADESC()
3142

3243
// Global list of surface triggers
@@ -48,7 +59,15 @@ void CEnvPlayerSurfaceTrigger::Spawn( void )
4859
SetSolid( SOLID_NONE );
4960
SetMoveType( MOVETYPE_NONE );
5061

62+
#ifdef MAPBASE_MP
63+
for (int i = 0; i < MAX_PLAYERS; i++)
64+
{
65+
m_iCurrentGameMaterial[i] = 0;
66+
m_iLastGameMaterial[i] = 0;
67+
}
68+
#else
5169
m_iCurrentGameMaterial = 0;
70+
#endif
5271
m_bDisabled = false;
5372

5473
g_PlayerSurfaceTriggers.AddToTail( this );
@@ -90,6 +109,19 @@ void CEnvPlayerSurfaceTrigger::PlayerSurfaceChanged( CBasePlayer *pPlayer, char
90109
return;
91110

92111
// Fire the output if we've changed, but only if it involves the target material
112+
#ifdef MAPBASE_MP
113+
int idx = pPlayer->entindex();
114+
if ( gameMaterial != (char)(m_iCurrentGameMaterial[idx]) &&
115+
( gameMaterial == m_iTargetGameMaterial || m_iCurrentGameMaterial[idx] == m_iTargetGameMaterial ) )
116+
{
117+
DevMsg( 2, "Player changed material to %d (was %d)\n", gameMaterial, m_iCurrentGameMaterial[idx] );
118+
119+
m_iCurrentGameMaterial[idx] = (int)gameMaterial;
120+
121+
SetThink( &CEnvPlayerSurfaceTrigger::UpdateMaterialThink );
122+
SetNextThink( gpGlobals->curtime );
123+
}
124+
#else
93125
if ( gameMaterial != (char)m_iCurrentGameMaterial &&
94126
( gameMaterial == m_iTargetGameMaterial || m_iCurrentGameMaterial == m_iTargetGameMaterial ) )
95127
{
@@ -100,6 +132,7 @@ void CEnvPlayerSurfaceTrigger::PlayerSurfaceChanged( CBasePlayer *pPlayer, char
100132
SetThink( &CEnvPlayerSurfaceTrigger::UpdateMaterialThink );
101133
SetNextThink( gpGlobals->curtime );
102134
}
135+
#endif
103136
}
104137

105138
//-----------------------------------------------------------------------------
@@ -108,14 +141,63 @@ void CEnvPlayerSurfaceTrigger::PlayerSurfaceChanged( CBasePlayer *pPlayer, char
108141
//-----------------------------------------------------------------------------
109142
void CEnvPlayerSurfaceTrigger::UpdateMaterialThink( void )
110143
{
144+
#ifdef MAPBASE_MP
145+
CBasePlayer *pFirstPlayer = NULL;
146+
int nNumOnMaterialLast = m_nNumOnMaterial;
147+
148+
for (int i = 0; i < gpGlobals->maxClients; i++)
149+
{
150+
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
151+
if (!pFirstPlayer)
152+
pFirstPlayer = pPlayer;
153+
154+
if ( m_iCurrentGameMaterial[i] != m_iLastGameMaterial[i] )
155+
{
156+
if ( m_iCurrentGameMaterial[i] == m_iTargetGameMaterial )
157+
{
158+
m_OnSurfaceChangedToTarget.FireOutput( pPlayer, this );
159+
m_nNumOnMaterial++;
160+
}
161+
else
162+
{
163+
m_OnSurfaceChangedFromTarget.FireOutput( pPlayer, this );
164+
165+
if (m_nNumOnMaterial > 0)
166+
m_nNumOnMaterial--;
167+
}
168+
}
169+
170+
m_iLastGameMaterial[i] = m_iCurrentGameMaterial[i];
171+
}
172+
173+
if ( nNumOnMaterialLast == 0 && m_nNumOnMaterial > 0 )
174+
{
175+
m_OnSurfaceChangedToTargetAll.FireOutput( pFirstPlayer, this );
176+
}
177+
else if ( nNumOnMaterialLast > 0 && m_nNumOnMaterial == 0 )
178+
{
179+
m_OnSurfaceChangedFromTargetAll.FireOutput( pFirstPlayer, this );
180+
}
181+
#else
111182
if ( m_iCurrentGameMaterial == m_iTargetGameMaterial )
112183
{
113184
m_OnSurfaceChangedToTarget.FireOutput( NULL, this );
185+
186+
#ifdef MAPBASE
187+
// This is used in MP, but SP has only one player, so just fire it here
188+
m_OnSurfaceChangedToTargetAll.FireOutput( NULL, this );
189+
#endif
114190
}
115191
else
116192
{
117193
m_OnSurfaceChangedFromTarget.FireOutput( NULL, this );
194+
195+
#ifdef MAPBASE
196+
// This is used in MP, but SP has only one player, so just fire it here
197+
m_OnSurfaceChangedFromTargetAll.FireOutput( NULL, this );
198+
#endif
118199
}
200+
#endif
119201
}
120202

121203
//-----------------------------------------------------------------------------
@@ -124,6 +206,9 @@ void CEnvPlayerSurfaceTrigger::UpdateMaterialThink( void )
124206
void CEnvPlayerSurfaceTrigger::InputDisable( inputdata_t &inputdata )
125207
{
126208
m_bDisabled = true;
209+
#ifdef MAPBASE_MP
210+
m_nNumOnMaterial = 0;
211+
#endif
127212
}
128213

129214
//-----------------------------------------------------------------------------

src/game/server/env_player_surface_trigger.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,22 @@ class CEnvPlayerSurfaceTrigger : public CPointEntity
3838

3939
private:
4040
int m_iTargetGameMaterial;
41+
#ifdef MAPBASE_MP
42+
int m_iCurrentGameMaterial[MAX_PLAYERS];
43+
int m_iLastGameMaterial[MAX_PLAYERS];
44+
int m_nNumOnMaterial;
45+
#else
4146
int m_iCurrentGameMaterial;
47+
#endif
4248
bool m_bDisabled;
4349

4450
// Outputs
4551
COutputEvent m_OnSurfaceChangedToTarget;
4652
COutputEvent m_OnSurfaceChangedFromTarget;
53+
#ifdef MAPBASE
54+
COutputEvent m_OnSurfaceChangedToTargetAll;
55+
COutputEvent m_OnSurfaceChangedFromTargetAll;
56+
#endif
4757
};
4858

4959
#endif // ENV_PLAYER_SURFACE_TRIGGER_H

0 commit comments

Comments
 (0)