Activity CAI_StandoffBehavior::GetCoverActivity() { CAI_Hint *pHintNode = GetOuter()->m_pHintNode; if ( pHintNode && pHintNode->HintType() == HINT_TACTICAL_COVER_LOW ) return GetOuter()->GetCoverActivity( pHintNode ); return ACT_INVALID; }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- CAI_Hint *CAI_FearBehavior::FindFearWithdrawalDest() { CAI_Hint *pHint; CHintCriteria hintCriteria; CAI_BaseNPC *pOuter = GetOuter(); Assert(pOuter != NULL); hintCriteria.AddHintType( HINT_PLAYER_ALLY_FEAR_DEST ); hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE_TO_PLAYER | bits_HINT_NOT_CLOSE_TO_ENEMY /*| bits_HINT_NODE_IN_VIEWCONE | bits_HINT_NPC_IN_NODE_FOV*/ ); hintCriteria.AddIncludePosition( AI_GetSinglePlayer()->GetAbsOrigin(), ( ai_fear_player_dist.GetFloat() ) ); pHint = CAI_HintManager::FindHint( pOuter, hintCriteria ); if( pHint ) { // Reserve this node while I try to get to it. When I get there I will lock it. // Otherwise, if I fail to get there, the node will come available again soon. pHint->DisableForSeconds( 4.0f ); } #if 0 else { Msg("DID NOT FIND HINT\n"); NDebugOverlay::Cross3D( GetOuter()->WorldSpaceCenter(), 32, 255, 255, 0, false, 10.0f ); } #endif return pHint; }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ CAI_Hint* CAI_HintManager::CreateHint( HintNodeData *pNodeData, const char *pMapData ) { // Reset last found hint if new node is added CAI_HintManager::ResetFoundHints(); CAI_Hint *pHint = (CAI_Hint*)CreateEntityByName("ai_hint"); if ( pHint ) { // First, parse the mapdata chunk we were passed if ( pMapData ) { CEntityMapData entData( (char*)pMapData ); pHint->ParseMapData( &entData ); // Restore the desired classname (parsing the mapdata stomps it) pHint->SetClassname( "ai_hint" ); } pHint->SetName( pNodeData->strEntityName ); pHint->SetAbsOrigin( pNodeData->vecPosition ); memcpy( &(pHint->m_NodeData), pNodeData, sizeof(HintNodeData) ); DispatchSpawn( pHint ); return pHint; } return NULL; }
//----------------------------------------------------------------------------- int CAI_HintManager::FindAllHints( CAI_BaseNPC *pNPC, const Vector &position, const CHintCriteria &hintCriteria, CUtlVector<CAI_Hint *> *pResult ) { // If we have no hints, bail int c = CAI_HintManager::gm_AllHints.Count(); if ( !c ) return NULL; // Remove the nearest flag. It makes now sense with random. bool hadNearest = hintCriteria.HasFlag( bits_HINT_NODE_NEAREST ); (const_cast<CHintCriteria &>(hintCriteria)).ClearFlag( bits_HINT_NODE_NEAREST ); // Now loop till we find a valid hint or return to the start CAI_Hint *pTestHint; for ( int i = 0; i < c; ++i ) { pTestHint = CAI_HintManager::gm_AllHints[ i ]; Assert( pTestHint ); if ( pTestHint->HintMatchesCriteria( pNPC, hintCriteria, position, NULL ) ) pResult->AddToTail( pTestHint ); } if ( hadNearest ) (const_cast<CHintCriteria &>(hintCriteria)).SetFlag( bits_HINT_NODE_NEAREST ); return pResult->Count(); }
Hint_e CAI_StandoffBehavior::GetHintType() { CAI_Hint *pHintNode = GetOuter()->m_pHintNode; if ( pHintNode ) return pHintNode->HintType(); return HINT_NONE; }
void CAI_StandoffBehavior::UnlockHintNode() { CAI_Hint *pHintNode = GetOuter()->m_pHintNode; if ( pHintNode ) { if ( pHintNode->IsLocked() && pHintNode->IsLockedBy( GetOuter() ) ) pHintNode->Unlock(); CAI_Node *pNode = pHintNode->GetNode(); if ( pNode && pNode->IsLocked() ) pNode->Unlock(); GetOuter()->m_pHintNode = NULL; } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CAI_HintManager::DumpHints() { AIHintIter_t iter; CAI_Hint *pCurHint = GetFirstHint( &iter ); while (pCurHint) { const Vector &v = pCurHint->GetAbsOrigin(); Msg( "(%.1f, %.1f, %.1f) -- Node ID: %d; WC id %d; type %d\n", v.x, v.y, v.z, pCurHint->GetNodeId(), pCurHint->GetWCId(), pCurHint->HintType() ); pCurHint = GetNextHint( &iter ); } }
//----------------------------------------------------------------------------- // Purpose: // Input : *pMapData - //----------------------------------------------------------------------------- int CNodeEnt::Spawn( const char *pMapData ) { m_NodeData.strEntityName = GetEntityName(); m_NodeData.vecPosition = GetAbsOrigin(); m_NodeData.nNodeID = NO_NODE; if ( m_NodeData.minState == NPC_STATE_NONE ) m_NodeData.minState = NPC_STATE_IDLE; if ( m_NodeData.maxState == NPC_STATE_NONE ) m_NodeData.maxState = NPC_STATE_COMBAT; // --------------------------------------------------------------------------------- // If just a hint node (not used for navigation) just create a hint and bail // --------------------------------------------------------------------------------- if (FClassnameIs( this, "info_hint" )) { if (m_NodeData.nHintType) { CAI_HintManager::CreateHint( &m_NodeData, pMapData ); } else { Warning("info_hint (HammerID: %d, position (%.2f, %.2f, %.2f)) with no hint type.\n", m_NodeData.nWCNodeID, m_NodeData.vecPosition.x, m_NodeData.vecPosition.y, m_NodeData.vecPosition.z ); } UTIL_RemoveImmediate( this ); return -1; } // --------------------------------------------------------------------------------- // First check if this node has a hint. If so create a hint entity // --------------------------------------------------------------------------------- CAI_Hint *pHint = NULL; if ( ClassMatches( "info_node_hint" ) || ClassMatches( "info_node_air_hint" ) ) { if ( m_NodeData.nHintType || m_NodeData.strGroup != NULL_STRING || m_NodeData.strEntityName != NULL_STRING ) { m_NodeData.nNodeID = m_nNodeCount; pHint = CAI_HintManager::CreateHint( &m_NodeData, pMapData ); pHint->AddSpawnFlags( GetSpawnFlags() ); } } // --------------------------------------------------------------------------------- // If we loaded from disk, we can discard all these node ents as soon as they spawn // unless we are in WC edited mode // --------------------------------------------------------------------------------- if ( g_pAINetworkManager->NetworksLoaded() && !engine->IsInEditMode()) { // If hint exists for this node, set it if (pHint) { CAI_Node *pNode = g_pBigAINet->GetNode(m_nNodeCount); if (pNode) pNode->SetHint( pHint ); else { DevMsg("AI node graph corrupt\n"); } } m_nNodeCount++; UTIL_RemoveImmediate( this ); return -1; } else { m_nNodeCount++; } // --------------------------------------------------------------------------------- // Add a new node to the network // --------------------------------------------------------------------------------- // For now just using one big AI network CAI_Node *new_node = g_pBigAINet->AddNode( GetAbsOrigin(), GetAbsAngles().y ); new_node->SetHint( pHint ); // ------------------------------------------------------------------------- // Update table of how each WC id relates to each engine ID // ------------------------------------------------------------------------- if (g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable) { g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[new_node->GetId()] = m_NodeData.nWCNodeID; } // Keep track of largest index used by WC if (g_pAINetworkManager->GetEditOps()->m_nNextWCIndex <= m_NodeData.nWCNodeID) { g_pAINetworkManager->GetEditOps()->m_nNextWCIndex = m_NodeData.nWCNodeID+1; } // ------------------------------------------------------------------------- // If in WC edit mode: // Remember the original positions of the nodes before // they drop so we can send the undropped positions to wc. // ------------------------------------------------------------------------- if (engine->IsInEditMode()) { if (g_pAINetworkManager->GetEditOps()->m_pWCPosition) { g_pAINetworkManager->GetEditOps()->m_pWCPosition[new_node->GetId()] = new_node->GetOrigin(); } } if (FClassnameIs( this, "info_node_air" ) || FClassnameIs( this, "info_node_air_hint" )) { new_node->SetType( NODE_AIR ); } else if (FClassnameIs( this, "info_node_climb" )) { new_node->SetType( NODE_CLIMB ); } else { new_node->SetType( NODE_GROUND ); } new_node->m_eNodeInfo = ( m_spawnflags << NODE_ENT_FLAGS_SHIFT ); // If changed as part of WC editing process note that network must be rebuilt if (m_debugOverlays & OVERLAY_WC_CHANGE_ENTITY) { g_pAINetworkManager->GetEditOps()->SetRebuildFlags(); new_node->m_eNodeInfo |= bits_NODE_WC_CHANGED; // Initialize the new nodes position. The graph may not be rebuild // right away but the node should at least be positioned correctly g_AINetworkBuilder.InitNodePosition( g_pBigAINet, new_node ); } UTIL_RemoveImmediate( this ); return -1; }
//--------------------------------------------------------- // Purpose: //--------------------------------------------------------- void CNPC_Assassin::StartTask( const Task_t *pTask ) { switch( pTask->iTask ) { case TASK_ASSASSIN_SET_EYE_STATE: { SetEyeState( (eyeState_t) ( (int) pTask->flTaskData ) ); TaskComplete(); } break; case TASK_ASSASSIN_EVADE: { Activity flipAct = ACT_INVALID; const Vector *avoidPos = ( GetEnemy() != NULL ) ? &(GetEnemy()->GetAbsOrigin()) : NULL; for ( int i = FLIP_LEFT; i < NUM_FLIP_TYPES; i++ ) { if ( CanFlip( i, flipAct, avoidPos ) ) { // Don't flip back to where we just were if ( ( ( i == FLIP_LEFT ) && ( m_nLastFlipType == FLIP_RIGHT ) ) || ( ( i == FLIP_RIGHT ) && ( m_nLastFlipType == FLIP_LEFT ) ) || ( ( i == FLIP_FORWARD ) && ( m_nLastFlipType == FLIP_BACKWARD ) ) || ( ( i == FLIP_BACKWARD ) && ( m_nLastFlipType == FLIP_FORWARD ) ) ) { flipAct = ACT_INVALID; continue; } m_nNumFlips--; ResetIdealActivity( flipAct ); m_flNextFlipTime = gpGlobals->curtime + 2.0f; m_nLastFlipType = i; break; } } if ( flipAct == ACT_INVALID ) { m_nNumFlips = 0; m_nLastFlipType = -1; m_flNextFlipTime = gpGlobals->curtime + 2.0f; TaskFail( "Unable to find flip evasion direction!\n" ); } } break; case TASK_ASSASSIN_GET_PATH_TO_VANTAGE_POINT: { assert( GetEnemy() != NULL ); if ( GetEnemy() == NULL ) break; Vector goalPos; CHintCriteria hint; // Find a disadvantage node near the player, but away from ourselves hint.SetHintType( HINT_TACTICAL_ENEMY_DISADVANTAGED ); hint.AddExcludePosition( GetAbsOrigin(), 256 ); hint.AddExcludePosition( GetEnemy()->GetAbsOrigin(), 256 ); if ( ( m_pSquad != NULL ) && ( m_pSquad->NumMembers() > 1 ) ) { AISquadIter_t iter; for ( CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) ) { if ( pSquadMember == NULL ) continue; hint.AddExcludePosition( pSquadMember->GetAbsOrigin(), 128 ); } } hint.SetFlag( bits_HINT_NODE_NEAREST ); CAI_Hint *pHint = CAI_HintManager::FindHint( this, GetEnemy()->GetAbsOrigin(), &hint ); if ( pHint == NULL ) { TaskFail( "Unable to find vantage point!\n" ); break; } pHint->GetPosition( this, &goalPos ); AI_NavGoal_t goal( goalPos ); //Try to run directly there if ( GetNavigator()->SetGoal( goal ) == false ) { TaskFail( "Unable to find path to vantage point!\n" ); break; } TaskComplete(); } break; default: BaseClass::StartTask( pTask ); break; } }
//----------------------------------------------------------------------------- // Purpose: // Input : *hintCriteria - // Output : CAI_Hint //----------------------------------------------------------------------------- CAI_Hint *CAI_HintManager::FindHint( CAI_BaseNPC *pNPC, const Vector &position, const CHintCriteria &hintCriteria ) { #if defined( HINT_PROFILING ) CFastTimer timer; timer.Start(); #endif bool singleType = hintCriteria.MatchesSingleHintType(); bool lookingForNearest = hintCriteria.HasFlag( bits_HINT_NODE_NEAREST ); bool bIgnoreHintType = true; CUtlVector< CAIHintVector * > lists; if ( singleType ) { int slot = CAI_HintManager::gm_TypedHints.Find( hintCriteria.GetFirstHintType() ); if ( slot != CAI_HintManager::gm_TypedHints.InvalidIndex() ) { lists.AddToTail( &CAI_HintManager::gm_TypedHints[ slot ] ); } } else { int typeCount = hintCriteria.NumHintTypes(); if ( typeCount > 0 ) { for ( int listType = 0; listType < typeCount; ++listType ) { int slot = CAI_HintManager::gm_TypedHints.Find( hintCriteria.GetHintType( listType ) ); if ( slot != CAI_HintManager::gm_TypedHints.InvalidIndex() ) { lists.AddToTail( &CAI_HintManager::gm_TypedHints[ slot ] ); } } } else { // Still need to check hint type in this case lists.AddToTail( &CAI_HintManager::gm_AllHints ); bIgnoreHintType = false; } } CAI_Hint *pBestHint = NULL; int visited = 0; int listCount = lists.Count(); if ( listCount == 0 ) return NULL; // Try the fast match path int i, count; // Start with hint after the last one used CAI_Hint *pTestHint = NULL; float flBestDistance = MAX_TRACE_LENGTH; if ( !lookingForNearest ) { // Fast check of previous results count = CAI_HintManager::GetFoundHintCount(); for ( i = 0; i < count; ++i ) { pTestHint = CAI_HintManager::GetFoundHint( i ); if ( pTestHint ) { Assert( dynamic_cast<CAI_Hint *>(pTestHint) != NULL ); ++visited; if ( pTestHint->HintMatchesCriteria( pNPC, hintCriteria, position, &flBestDistance ) ) { #if defined( HINT_PROFILING ) Msg( "fast result visited %d\n", visited ); #endif return pTestHint; } } } } // Longer search, reset best distance flBestDistance = MAX_TRACE_LENGTH; for ( int listNum = 0; listNum < listCount; ++listNum ) { CAIHintVector *list = lists[ listNum ]; count = list->Count(); // ------------------------------------------- // If we have no hints, bail // ------------------------------------------- if ( !count ) continue; // Now loop till we find a valid hint or return to the start for ( i = 0 ; i < count; ++i ) { pTestHint = list->Element( i ); Assert( pTestHint ); ++visited; Assert( dynamic_cast<CAI_Hint *>(pTestHint) != NULL ); if ( pTestHint->HintMatchesCriteria( pNPC, hintCriteria, position, &flBestDistance, false, bIgnoreHintType ) ) { // If we were searching for the nearest, just note that this is now the nearest node if ( lookingForNearest ) { pBestHint = pTestHint; } else { // If we're not looking for the nearest, we're done CAI_HintManager::AddFoundHint( pTestHint ); #if defined( HINT_PROFILING ) Msg( "visited %d\n", visited ); #endif return pTestHint; } } } } // Return the nearest node that we found if ( pBestHint ) { CAI_HintManager::AddFoundHint( pBestHint ); } #if defined( HINT_PROFILING ) timer.End(); Msg( "visited %d\n", visited ); if ( !pBestHint ) { Msg( "%i search failed for [%d] at pos %.3f %.3f %.3f [%.4f msec ~ %.4f msec per node]\n", gpGlobals->tickcount, pNPC ? pNPC->entindex() : -1, position.x, position.y, position.z, timer.GetDuration().GetMillisecondsF(), timer.GetDuration().GetMillisecondsF()/max( (float)visited, 1.0f ) ); } #endif return pBestHint; }
//----------------------------------------------------------------------------- // Purpose: // // // Output : //----------------------------------------------------------------------------- CBaseEntity *CNPC_RollerDozer::FindDebris( void ) { if( !m_pHintNode ) { // Detect rubbish near a hint node. CAI_Hint* pHintNode = CAI_Hint::FindHint( this, HINT_ROLLER_CLEANUP_POINT, 0, 1024 ); if( pHintNode) { // Search around the hint node for debris that should be cleared. Vector vecHintNodeOrigin; // Get hint node position vecHintNodeOrigin; pHintNode->GetPosition(this,&vecHintNodeOrigin); CBaseEntity *pList[ 16 ]; Vector vecDeltaUp( 200, 200, 64 ); Vector vecDeltaDown( 200, 200, 10 ); int i; IPhysicsObject *pPhysObj; int count = UTIL_EntitiesInBox( pList, 16, vecHintNodeOrigin - vecDeltaDown, vecHintNodeOrigin + vecDeltaUp, 0 ); float m_flHeaviestMass = 0; CBaseEntity *pHeaviest = NULL; for( i = 0 ; i < count ; i++ ) { pPhysObj = pList[ i ]->VPhysicsGetObject(); if( !pPhysObj || FClassnameIs( pList[ i ], "npc_rollerdozer" ) ) { // Only consider physics objects. Exclude rollers. continue; } if( pPhysObj->GetMass() <= 400 ) { if( pPhysObj->GetMass() > m_flHeaviestMass ) { m_flHeaviestMass = pPhysObj->GetMass(); pHeaviest = pList[ i ]; } /* // Report to the cleanup point and doze this piece of debris away. SetCondition( COND_ROLLERDOZER_FOUND_DEBRIS ); m_vecCleanupPoint = vecHintNodeOrigin; return pList[ i ]; */ } } if( pHeaviest ) { SetCondition( COND_ROLLERDOZER_FOUND_DEBRIS ); //NDebugOverlay::Line( GetLocalOrigin(), pHeaviest->GetLocalOrigin(), 255,255,0, true, 3 ); m_vecCleanupPoint = vecHintNodeOrigin; return pHeaviest; } } } return NULL; }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CAI_HintManager::DrawHintOverlays(float flDrawDuration) { int c = gm_AllHints.Count(); for ( int i = 0; i < c; ++i ) { CAI_Hint *pHint = gm_AllHints[ i ]; int r = 0; int g = 0; int b = 255; Vector vHintPos; if (pHint->m_NodeData.nNodeID != NO_NODE) { vHintPos = g_pBigAINet->GetNode(pHint->m_NodeData.nNodeID)->GetPosition(g_pAINetworkManager->GetEditOps()->m_iHullDrawNum); } else { vHintPos = pHint->GetAbsOrigin(); } if ( pHint->GetNodeId() != NO_NODE ) NDebugOverlay::Text( vHintPos + Vector(0,6,8), CFmtStr("(%d), (%d)", pHint->HintType(), pHint->GetNodeId()), true, flDrawDuration ); else NDebugOverlay::Text( vHintPos + Vector(0,6,8), CFmtStr("(%d)", pHint->HintType()), true, flDrawDuration ); // If node is currently locked if (pHint->m_NodeData.iDisabled) { r = 100; g = 100; b = 100; } else if (pHint->m_hHintOwner != NULL) { r = 255; g = 0; b = 0; CBaseEntity* pOwner = pHint->User(); if (pOwner) { char owner[255]; Q_strncpy(owner,pOwner->GetDebugName(),sizeof(owner)); Vector loc = vHintPos; loc.x+=6; loc.y+=6; loc.z+=6; NDebugOverlay::Text( loc, owner, true, flDrawDuration ); NDebugOverlay::Line( vHintPos, pOwner->WorldSpaceCenter(), 128, 128, 128, false, 0); } } else if (pHint->IsLocked()) { r = 200; g = 150; b = 10; } NDebugOverlay::Box(vHintPos, Vector(-3,-3,-3), Vector(3,3,3), r,g,b,0,flDrawDuration); // Draw line in facing direction Vector offsetDir = 12.0 * Vector(cos(DEG2RAD(pHint->Yaw())),sin(DEG2RAD(pHint->Yaw())),0); NDebugOverlay::Line(vHintPos, vHintPos+offsetDir, r,g,b,false,flDrawDuration); } }
void CAI_StandoffBehavior::SetReuseCurrentCover() { CAI_Hint *pHintNode = GetOuter()->m_pHintNode; if ( pHintNode && pHintNode->GetNode() && pHintNode->GetNode()->IsLocked() ) pHintNode->GetNode()->Unlock(); }