Added AI hint radius from the Alien Swarm SDK as well as an all-new "hint weight" keyvalue

This commit is contained in:
Blixibon 2021-10-28 00:01:29 -05:00
parent ece1a612ce
commit 847db9c3e3
5 changed files with 192 additions and 3 deletions

View File

@ -35,6 +35,10 @@ CHintCriteria::CHintCriteria( void )
m_strGroup = NULL_STRING;
m_iFlags = 0;
m_HintTypes.Purge();
#ifdef MAPBASE // From Alien Swarm SDK
m_pfnFilter = NULL;
m_pFilterContext = NULL;
#endif
}
//-----------------------------------------------------------------------------
@ -1117,10 +1121,10 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing )
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
facing = bDefaultFacing ? HIF_YES : HIF_NO;
facing = bDefaultFacing ? HIF_NO : HIF_YES;
if (facing == HIF_YES)
pNPC->GetNavigator()->SetArrivalDirection(GetDirection());
if (facing == HIF_NO)
pNPC->GetNavigator()->SetArrivalDirection( GetDirection() );
if (HintActivityName() != NULL_STRING)
{
@ -1139,6 +1143,51 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing )
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if this hint should override a NPC's yaw even during regular AI.
//-----------------------------------------------------------------------------
bool CAI_Hint::OverridesNPCYaw( CAI_BaseNPC *pNPC )
{
switch (HintType())
{
case HINT_TACTICAL_COVER_CUSTOM:
case HINT_TACTICAL_COVER_MED:
case HINT_TACTICAL_COVER_LOW:
{
if (pNPC->HasMemory( bits_MEMORY_INCOVER ))
{
// By default, don't override yaw on cover nodes unless they use custom activities.
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
return ( HintActivityName() != NULL_STRING );
return facing == HIF_NO;
}
break;
}
case HINT_PLAYER_ALLY_MOVE_AWAY_DEST:
{
Vector vHintPos;
GetPosition( pNPC, &vHintPos );
if (VectorsAreEqual( vHintPos, pNPC->GetAbsOrigin(), 0.1f ))
{
// By default, don't override yaw on move away destinations unless they use custom activities.
HintIgnoreFacing_t facing = GetIgnoreFacing();
if (facing == HIF_DEFAULT)
return ( HintActivityName() != NULL_STRING );
return facing == HIF_NO;
}
break;
}
}
return false;
}
#endif
//-----------------------------------------------------------------------------
@ -1258,6 +1307,30 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
return false;
}
#ifdef MAPBASE
// Test against generic filter
// (From Alien Swarm SDK)
if ( !hintCriteria.PassesFilter( this ) )
{
REPORTFAILURE( "Failed filter test" );
return false;
}
// (From Alien Swarm SDK)
int nRadius = GetRadius();
if ( nRadius != 0 )
{
// Calculate our distance
float distance = (GetAbsOrigin() - position).LengthSqr();
if ( distance > nRadius * nRadius )
{
REPORTFAILURE( "NPC is not within the node's radius." );
return false;
}
}
#endif
if ( hintCriteria.HasFlag(bits_HINT_NPC_IN_NODE_FOV) )
{
if ( pNPC == NULL )
@ -1377,10 +1450,18 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
{
trace_t tr;
// Can my bounding box fit there?
#ifdef MAPBASE // From Alien Swarm SDK
Vector vStep( 0, 0, pNPC->StepHeight() );
AI_TraceHull ( GetAbsOrigin() + vStep, GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs() - vStep,
MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction < 0.95 )
#else
AI_TraceHull ( GetAbsOrigin(), GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs(),
MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction != 1.0 )
#endif
{
REPORTFAILURE( "Node isn't clear." );
return false;
@ -1396,6 +1477,15 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint
// Calculate our distance
float distance = (GetAbsOrigin() - position).Length();
#ifdef MAPBASE
// Divide by hint weight
float flWeight = GetHintWeight();
if ( flWeight != 1.0f )
{
distance *= GetHintWeightInverse();
}
#endif
// Must be closer than the current best
if ( distance > *flNearestDistance )
{
@ -1576,6 +1666,14 @@ void CAI_Hint::OnRestore()
m_NodeData.nNodeID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( m_NodeData.nWCNodeID );
FixupTargetNode();
#ifdef MAPBASE
if (m_NodeData.flWeight != 0.0f && m_NodeData.flWeight != 1.0f)
{
// Re-invert the weight
m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight;
}
#endif
CAI_Node *pNode = GetNode();
if ( !pNode )
@ -1751,6 +1849,11 @@ void CC_ai_drop_hint( const CCommand &args )
nodeData.fIgnoreFacing = HIF_DEFAULT;
nodeData.minState = NPC_STATE_IDLE;
nodeData.maxState = NPC_STATE_COMBAT;
#ifdef MAPBASE
nodeData.nRadius = 0; // From Alien Swarm SDK
nodeData.flWeight = 1.0f;
nodeData.flWeightInverse = 1.0f;
#endif
CAI_Hint *pHint = CAI_HintManager::CreateHint( &nodeData, NULL );
if ( pHint )
{

View File

@ -112,6 +112,13 @@ enum Hint_e
// CS port hints
HINT_CSTRIKE_HOSTAGE_ESCAPE = 1100,
#ifdef MAPBASE
// Mapbase hints
// (these start at a high number to avoid potential conflicts with mod hints)
HINT_TACTICAL_COVER_CUSTOM = 10000, // Cover node with a custom hint activity (NPCs can take cover and reload here while playing said activity)
#endif
};
const char *GetHintTypeDescription( Hint_e iHintType );
const char *GetHintTypeDescription( CAI_Hint *pHint );
@ -120,6 +127,10 @@ const char *GetHintTypeDescription( CAI_Hint *pHint );
// CHintCriteria
//-----------------------------------------------------------------------------
#ifdef MAPBASE // From Alien Swarm SDK
typedef bool (*HintSearchFilterFunc_t)( void *pContext, CAI_Hint *pCandidate );
#endif
class CHintCriteria
{
public:
@ -134,6 +145,11 @@ public:
void SetGroup( string_t group );
string_t GetGroup( void ) const { return m_strGroup; }
#ifdef MAPBASE // From Alien Swarm SDK
void SetFilterFunc( HintSearchFilterFunc_t pfnFilter, void *pContext = NULL ) { m_pfnFilter = pfnFilter; m_pFilterContext = pContext; }
bool PassesFilter( CAI_Hint *pCandidate ) const { return (m_pfnFilter) ? (*m_pfnFilter)(m_pFilterContext, pCandidate) : true; }
#endif
int GetFirstHintType( void ) const { return m_iFirstHintType; }
int GetLastHintType( void ) const { return m_iLastHintType; }
bool MatchesHintType( int hintType ) const;
@ -176,6 +192,11 @@ private:
zoneList_t m_zoneInclude;
zoneList_t m_zoneExclude;
#ifdef MAPBASE
HintSearchFilterFunc_t m_pfnFilter;
void * m_pFilterContext;
#endif
};
class CAI_Node;
@ -310,11 +331,21 @@ public:
int GetNodeId() { return m_NodeData.nNodeID; }
int GetWCId() { return m_NodeData.nWCNodeID; }
#ifdef MAPBASE
int GetRadius() const { return m_NodeData.nRadius; } // From Alien Swarm SDK
float GetHintWeight() const { return m_NodeData.flWeight; }
float GetHintWeightInverse() const { return m_NodeData.flWeightInverse; } // Used to multiply distances
#endif
bool HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria, const Vector &position, float *flNearestDistance, bool bIgnoreLock = false, bool bIgnoreHintType = false );
bool IsInNodeFOV( CBaseEntity *pOther );
#ifdef MAPBASE
void NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing );
// Returns true if this hint should override a NPC's yaw even during regular AI.
bool OverridesNPCYaw( CAI_BaseNPC *pNPC );
#endif
#ifdef MAPBASE_VSCRIPT

View File

@ -173,6 +173,10 @@ BEGIN_SIMPLE_DATADESC( HintNodeData )
DEFINE_KEYFIELD( fIgnoreFacing, FIELD_INTEGER, "IgnoreFacing" ),
DEFINE_KEYFIELD( minState, FIELD_INTEGER, "MinimumState" ),
DEFINE_KEYFIELD( maxState, FIELD_INTEGER, "MaximumState" ),
#ifdef MAPBASE
DEFINE_KEYFIELD( nRadius, FIELD_INTEGER, "radius" ), // From Alien Swarm SDK
DEFINE_KEYFIELD( flWeight, FIELD_FLOAT, "hintweight" ),
#endif
END_DATADESC()
@ -205,6 +209,17 @@ int CNodeEnt::Spawn( const char *pMapData )
m_NodeData.minState = NPC_STATE_IDLE;
if ( m_NodeData.maxState == NPC_STATE_NONE )
m_NodeData.maxState = NPC_STATE_COMBAT;
#ifdef MAPBASE
if (m_NodeData.flWeight == 0.0f)
{
m_NodeData.flWeight = 1.0f;
}
else if (m_NodeData.flWeight != 1.0f)
{
// Invert the weight so that it could be used as a direct multiplier for distances, etc.
m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight;
}
#endif
// ---------------------------------------------------------------------------------
// If just a hint node (not used for navigation) just create a hint and bail
// ---------------------------------------------------------------------------------

View File

@ -42,6 +42,11 @@ struct HintNodeData
HintIgnoreFacing_t fIgnoreFacing;
NPC_STATE minState;
NPC_STATE maxState;
#ifdef MAPBASE
int nRadius; // From Alien Swarm SDK
float flWeight;
float flWeightInverse; // Not saved
#endif
int nWCNodeID; // Node ID assigned by worldcraft (not same as engine!)

View File

@ -144,6 +144,31 @@ public:
int m_capabilities; // cache this
};
#ifdef MAPBASE
//-------------------------------------
// Purpose: A version of CNodeFilter which allows hints to influence the result.
//-------------------------------------
class CNodeHintFilter : public CNodeFilter
{
public:
CNodeHintFilter( CAI_BaseNPC *pNPC, const Vector &pos ) : CNodeFilter( pNPC, pos ) {}
CNodeHintFilter( const Vector &pos ) : CNodeFilter( pos ) {}
virtual float NodeDistanceSqr( CAI_Node &node )
{
// Heavier hints are considered closer
if (node.GetHint() && node.GetHint()->GetHintWeight() != 1.0f)
{
return CNodeFilter::NodeDistanceSqr( node ) * node.GetHint()->GetHintWeightInverse();
}
else
{
return CNodeFilter::NodeDistanceSqr( node );
}
}
};
#endif
//-----------------------------------------------------------------------------
// CAI_Network
//-----------------------------------------------------------------------------
@ -351,7 +376,12 @@ int CAI_Network::NearestNodeToPoint( CAI_BaseNPC *pNPC, const Vector &vecOrigin,
// First get nodes distances and eliminate those that are beyond
// the maximum allowed distance for local movements
// ---------------------------------------------------------------
#ifdef MAPBASE
// Allow hint weight to influence supposed distance
CNodeHintFilter filter( pNPC, vecOrigin );
#else
CNodeFilter filter( pNPC, vecOrigin );
#endif
#ifdef AI_PERF_MON
m_nPerfStatNN++;
@ -605,8 +635,13 @@ CAI_Node *CAI_Network::AddNode( const Vector &origin, float yaw )
CAI_Link *CAI_Network::CreateLink( int srcID, int destID, CAI_DynamicLink *pDynamicLink )
{
#ifdef MAPBASE // From Alien Swarm SDK
CAI_Node *pSrcNode = GetNode( srcID );
CAI_Node *pDestNode = GetNode( destID );
#else
CAI_Node *pSrcNode = g_pBigAINet->GetNode( srcID );
CAI_Node *pDestNode = g_pBigAINet->GetNode( destID );
#endif
Assert( pSrcNode && pDestNode && pSrcNode != pDestNode );