//========================================================= // Crea un hijo en un nodo candidato. //========================================================= void CDirector_Manager::AddChild() { // Actualizamos los nodos candidatos para la creación. UpdateNodes(); // ¡No hay nodos! if ( SpawnNodes.Count() <= 0 ) return; // Intentamos crear al hijo. for ( int i = 0; i < MAX_TRIES; ++i ) { // Seleccionamos un nodo candidato al azar. CAI_Node *pNode = GetRandomNode(); // El nodo no existe. if ( !pNode ) continue; // Obtenemos la ubicación del nodo e intentamos crear al hijo. Vector vecOrigin = pNode->GetPosition(HULL_HUMAN); bool bSpawn = AddChild(vecOrigin); // El hijo no se ha podido crear. if ( !bSpawn ) continue; // Un hijo menos en la cola. Director()->SpawnQueue = Director()->SpawnQueue - 1; break; } }
//========================================================= // Crea un jefe. //========================================================= void CDirector_Manager::SpawnBoss() { // Actualizamos los nodos candidatos para la creación. UpdateNodes(); // ¡No hay nodos! if ( SpawnNodes.Count() <= 0 ) return; for ( int i = 0; i < MAX_TRIES; ++i ) { // Seleccionamos un nodo candidato al azar. CAI_Node *pNode = GetRandomNode(); // El nodo no existe. if ( !pNode ) continue; // Obtenemos la ubicación del nodo. Vector vecOrigin = pNode->GetPosition(HULL_MEDIUM_TALL); bool bSpawn = AddBoss(vecOrigin); // El hijo no se ha podido crear. if ( !bSpawn ) continue; return; } // No se pudo crear al Jefe, intentarlo en otro momento. Director()->BossPendient = true; }
void CASW_Spawn_Manager::PrespawnAlienAtRandomNode(const char *szAlienClass, const int iNumAliens, const int iHull, const Vector &playerStartPos, const int iNumNodes) { for (int i = 0; i < iNumAliens; ++i) { CAI_Node *pNode = NULL; for (int k = 0; k < 30; ++k) { int node_id = RandomInt(0, iNumNodes - 1); pNode = g_pBigAINet->GetNode(node_id); if (!pNode || pNode->GetType() != NODE_GROUND) continue; else if (pNode->GetOrigin().DistToSqr(playerStartPos) < 1000 * 1000) { continue; } if (ValidSpawnPoint(pNode->GetPosition(iHull), NAI_Hull::Mins(iHull), NAI_Hull::Maxs(iHull), true, false)) { // Raise the end position a little up off the floor, place the npc and drop him down CBaseEntity *pAlien = SpawnAlienAt(szAlienClass, pNode->GetPosition(iHull) + Vector(0.f, 0.f, 12.f), RandomAngle(0, 360)); IASW_Spawnable_NPC *pSpawnable = dynamic_cast<IASW_Spawnable_NPC*>(pAlien); if (pSpawnable) { pSpawnable->SetAlienOrders(AOT_SpreadThenHibernate, vec3_origin, NULL); } if (asw_director_debug.GetBool() && pAlien) { Msg("Spawned alien at %f %f %f\n", pAlien->GetAbsOrigin()); NDebugOverlay::Cross3D(pAlien->GetAbsOrigin(), 8.0f, 255, 0, 0, true, 20.0f); } if (pAlien) break; } } } }
//========================================================= // Verificaciones después de crear al NPC. //========================================================= bool CDirector_Manager::PostSpawn(CAI_BaseNPC *pNPC) { bool bStuck = true; while ( bStuck ) { trace_t tr; UTIL_TraceHull(pNPC->GetAbsOrigin(), pNPC->GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs(), MASK_NPCSOLID, pNPC, COLLISION_GROUP_NONE, &tr); if ( tr.fraction != 1.0 && tr.m_pEnt ) { // Nos hemos atorado en un objeto con fisicas. if ( FClassnameIs(tr.m_pEnt, "prop_physics") ) { // Lo ajustamos como "No solido" para que el bucle lo ignore. tr.m_pEnt->AddSolidFlags(FSOLID_NOT_SOLID); // Removemos el objeto. UTIL_RemoveImmediate(tr.m_pEnt); continue; } // Nos hemos atorado con una pared o algo del mundo. if ( tr.m_pEnt->IsWorld() ) { // No... no podemos eliminar una pared... // Removemos el NPC para evitar eventos sobrenaturales. UTIL_RemoveImmediate(pNPC); return false; } // No es un objeto con físicas ni del mundo. // Intentamos encontrar una nueva ubicación para el hijo. //DevMsg("[MANAGER] <%s> STUCK!!!! \r\n", tr.m_pEnt->GetClassname()); CAI_Node *pNode = GetRandomNode(); // Nada if ( !pNode ) UTIL_RemoveImmediate(pNPC); pNPC->SetAbsOrigin(pNode->GetPosition(HULL_HUMAN)); } bStuck = false; } return true; }
//========================================================= // Crea un bonche de hijos en un nodo candidato. //========================================================= void CDirector_Manager::AddHorde(int pMount) { // Actualizamos los nodos candidatos para la creación. UpdateNodes(); // ¡No hay nodos! if ( SpawnNodes.Count() <= 0 ) return; for ( int i = 0; i < MAX_TRIES; ++i ) { // Seleccionamos un nodo candidato al azar. CAI_Node *pNode = GetRandomNode(); // El nodo no existe. if ( !pNode ) continue; // Obtenemos la ubicación del nodo. Vector vecOrigin = pNode->GetPosition(HULL_HUMAN); ConVarRef director_debug("director_debug"); if ( director_debug.GetBool() ) NDebugOverlay::Cross3D(vecOrigin, 50.0f, 255, 128, 0, true, 20.0f); // Creamos varios hijos en este nodo. // Con las colisiones especiales (No colisionan con los otros hijos) for ( int h = 0; h < pMount; ++h ) { for ( int s = 0; s < MAX_TRIES; ++s ) { bool pSpawn = AddChild(vecOrigin, SPAWN_NO_COLLISION_AND_POWERFUL); if ( pSpawn ) break; } } break; } }
bool CAI_ASW_PrepareToEngageBehavior::GetPrepareToAttackPath(const Vector &vecThreat ) { if ( !CAI_NetworkManager::NetworksLoaded() ) { DevWarning( 2, "Graph not ready for GetPrepareToAttackPath!\n" ); return false; } Vector vecToThreat = vecThreat - GetAbsOrigin(); float flDistToThreat = vecToThreat.NormalizeInPlace(); /* int iMyNode = GetOuter()->GetPathfinder()->NearestNodeToNPC(); int iThreatNode = GetOuter()->GetPathfinder()->NearestNodeToPoint( vecThreat ); if ( iMyNode == NO_NODE ) { DevWarning( 2, "FindPrepareToAttackNode() - %s has no nearest node!\n", GetEntClassname()); return false; } if ( iThreatNode == NO_NODE ) { // DevWarning( 2, "FindBackAwayNode() - Threat has no nearest node!\n" ); iThreatNode = iMyNode; // return false; } // A vector pointing to the threat. Vector vecToThreat; vecToThreat = vecThreat - GetLocalOrigin(); // Get my current distance from the threat float flCurDist = VectorNormalize( vecToThreat ); // find all nodes within the radius of our target m_flPrepareRadius; */ int iNumNodes = g_pBigAINet->NumNodes(); CUtlVector<int> candidateNodes; for ( int i = 0; i < iNumNodes; i++ ) { CAI_Node *pNode = g_pBigAINet->GetNode( i ); if ( !pNode || pNode->GetType() != NODE_GROUND ) continue; Vector vecPos = pNode->GetPosition( GetOuter()->GetHullType() ); Vector vecDir = vecPos - vecThreat; float flDist = vecDir.NormalizeInPlace(); if ( flDist > m_flPrepareRadiusMax || flDist < m_flPrepareRadiusMin ) continue; // Make sure this node doesn't take me past the enemy's position. Vector vecToNode = vecPos - GetAbsOrigin(); float flDistToNode = vecToNode.NormalizeInPlace(); if( DotProduct( vecToNode, vecToThreat ) > 0.0 && flDistToNode > flDistToThreat ) continue; candidateNodes.AddToTail( i ); } if ( candidateNodes.Count() <= 0 ) return false; int iOffset = RandomInt( 0, candidateNodes.Count() - 1 ); int iNumCandidateNodes = candidateNodes.Count(); int iMaxTries = 4; for ( int i = 0; i < iNumCandidateNodes && iMaxTries > 0; i++ ) { CAI_Node *pNode = g_pBigAINet->GetNode( candidateNodes[ i + iOffset ] ); if ( !pNode || pNode->GetType() != NODE_GROUND ) continue; // see if we can reach it Vector vecPos = pNode->GetPosition( GetOuter()->GetHullType() ); AI_NavGoal_t goal( vecPos ); if ( GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET ) ) { return true; } iMaxTries--; } return false; }
AI_Waypoint_t *CAI_Pathfinder::FindBestPath(int startID, int endID) { if ( !GetNetwork()->NumNodes() ) return NULL; int nNodes = GetNetwork()->NumNodes(); CAI_Node **pAInode = GetNetwork()->AccessNodes(); CVarBitVec openBS(nNodes); CVarBitVec closeBS(nNodes); // ------------- INITIALIZE ------------------------ float* nodeG = (float *)stackalloc( nNodes * sizeof(float) ); float* nodeH = (float *)stackalloc( nNodes * sizeof(float) ); float* nodeF = (float *)stackalloc( nNodes * sizeof(float) ); int* nodeP = (int *)stackalloc( nNodes * sizeof(int) ); // Node parent for (int node=0;node<nNodes;node++) { nodeG[node] = FLT_MAX; nodeP[node] = -1; } nodeG[startID] = 0; nodeH[startID] = 0.1*(pAInode[startID]->GetPosition(GetHullType())-pAInode[endID]->GetPosition(GetHullType())).Length(); // Don't want to over estimate nodeF[startID] = nodeG[startID] + nodeH[startID]; openBS.Set(startID); closeBS.Set( startID ); // --------------- FIND BEST PATH ------------------ while (!openBS.IsAllClear()) { int smallestID = CAI_Network::FindBSSmallest(&openBS,nodeF,nNodes); openBS.Clear(smallestID); CAI_Node *pSmallestNode = pAInode[smallestID]; if (GetOuter()->IsUnusableNode(smallestID, pSmallestNode->m_pHint)) continue; if (smallestID == endID) { AI_Waypoint_t* route = MakeRouteFromParents(&nodeP[0], endID); return route; } // Check this if the node is immediately in the path after the startNode // that it isn't blocked for (int link=0; link < pSmallestNode->NumLinks();link++) { CAI_Link *nodeLink = pSmallestNode->GetLinkByIndex(link); if (!IsLinkUsable(nodeLink,smallestID)) continue; // FIXME: the cost function should take into account Node costs (danger, flanking, etc). int moveType = nodeLink->m_iAcceptedMoveTypes[GetHullType()] & CapabilitiesGet(); int testID = nodeLink->DestNodeID(smallestID); Vector r1 = pSmallestNode->GetPosition(GetHullType()); Vector r2 = pAInode[testID]->GetPosition(GetHullType()); float dist = GetOuter()->GetNavigator()->MovementCost( moveType, r1, r2 ); // MovementCost takes ref parameters!! if ( dist == FLT_MAX ) continue; float new_g = nodeG[smallestID] + dist; if ( !closeBS.IsBitSet(testID) || (new_g < nodeG[testID]) ) { nodeP[testID] = smallestID; nodeG[testID] = new_g; nodeH[testID] = (pAInode[testID]->GetPosition(GetHullType())-pAInode[endID]->GetPosition(GetHullType())).Length(); nodeF[testID] = nodeG[testID] + nodeH[testID]; closeBS.Set( testID ); openBS.Set( testID ); } } } return NULL; }
int CAI_TacticalServices::FindCoverNode(const Vector &vNearPos, const Vector &vThreatPos, const Vector &vThreatEyePos, float flMinDist, float flMaxDist ) { if ( !CAI_NetworkManager::NetworksLoaded() ) return NO_NODE; AI_PROFILE_SCOPE( CAI_TacticalServices_FindCoverNode ); MARK_TASK_EXPENSIVE(); DebugFindCover( NO_NODE, GetOuter()->EyePosition(), vThreatEyePos, 0, 255, 255 ); int iMyNode = GetPathfinder()->NearestNodeToPoint( vNearPos ); if ( iMyNode == NO_NODE ) { Vector pos = GetOuter()->GetAbsOrigin(); DevWarning( 2, "FindCover() - %s has no nearest node! (Check near %f %f %f)\n", GetEntClassname(), pos.x, pos.y, pos.z); return NO_NODE; } if ( !flMaxDist ) { // user didn't supply a MaxDist, so work up a crazy one. flMaxDist = 784; } if ( flMinDist > 0.5 * flMaxDist) { flMinDist = 0.5 * flMaxDist; } // ------------------------------------------------------------------------------------ // We're going to search for a cover node by expanding to our current node's neighbors // and then their neighbors, until cover is found, or all nodes are beyond MaxDist // ------------------------------------------------------------------------------------ AI_NearNode_t *pBuffer = (AI_NearNode_t *)stackalloc( sizeof(AI_NearNode_t) * GetNetwork()->NumNodes() ); CNodeList list( pBuffer, GetNetwork()->NumNodes() ); CVarBitVec wasVisited(GetNetwork()->NumNodes()); // Nodes visited // mark start as visited list.Insert( AI_NearNode_t(iMyNode, 0) ); wasVisited.Set( iMyNode ); float flMinDistSqr = flMinDist*flMinDist; float flMaxDistSqr = flMaxDist*flMaxDist; static int nSearchRandomizer = 0; // tries to ensure the links are searched in a different order each time; // Search until the list is empty while( list.Count() ) { // Get the node that is closest in the number of steps and remove from the list int nodeIndex = list.ElementAtHead().nodeIndex; list.RemoveAtHead(); CAI_Node *pNode = GetNetwork()->GetNode(nodeIndex); Vector nodeOrigin = pNode->GetPosition(GetHullType()); float dist = (vNearPos - nodeOrigin).LengthSqr(); if (dist >= flMinDistSqr && dist < flMaxDistSqr) { Activity nCoverActivity = GetOuter()->GetCoverActivity( pNode->GetHint() ); Vector vEyePos = nodeOrigin + GetOuter()->EyeOffset(nCoverActivity); if ( GetOuter()->IsValidCover( nodeOrigin, pNode->GetHint() ) ) { // Check if this location will block the threat's line of sight to me if (GetOuter()->IsCoverPosition(vThreatEyePos, vEyePos)) { // -------------------------------------------------------- // Don't let anyone else use this node for a while // -------------------------------------------------------- pNode->Lock( 1.0 ); if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW ) ) { if ( GetOuter()->GetHintNode() ) { GetOuter()->GetHintNode()->Unlock(GetOuter()->GetHintDelay(GetOuter()->GetHintNode()->HintType())); GetOuter()->SetHintNode( NULL ); } GetOuter()->SetHintNode( pNode->GetHint() ); } // The next NPC who searches should use a slight different pattern nSearchRandomizer = nodeIndex; DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 0, 255, 0 ); return nodeIndex; } else { DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 255, 0, 0 ); } } else { DebugFindCover( pNode->GetId(), vEyePos, vThreatEyePos, 0, 0, 255 ); } } // Add its children to the search list // Go through each link // UNDONE: Pass in a cost function to measure each link? for ( int link = 0; link < GetNetwork()->GetNode(nodeIndex)->NumLinks(); link++ ) { int index = (link + nSearchRandomizer) % GetNetwork()->GetNode(nodeIndex)->NumLinks(); CAI_Link *nodeLink = GetNetwork()->GetNode(nodeIndex)->GetLinkByIndex(index); if ( !m_pPathfinder->IsLinkUsable( nodeLink, iMyNode ) ) continue; int newID = nodeLink->DestNodeID(nodeIndex); // If not already on the closed list, add to it and set its distance if (!wasVisited.IsBitSet(newID)) { // Don't accept climb nodes or nodes that aren't ready to use yet if ( GetNetwork()->GetNode(newID)->GetType() != NODE_CLIMB && !GetNetwork()->GetNode(newID)->IsLocked() ) { // UNDONE: Shouldn't we really accumulate the distance by path rather than // absolute distance. After all, we are performing essentially an A* here. nodeOrigin = GetNetwork()->GetNode(newID)->GetPosition(GetHullType()); dist = (vNearPos - nodeOrigin).LengthSqr(); // use distance to threat as a heuristic to keep AIs from running toward // the threat in order to take cover from it. float threatDist = (vThreatPos - nodeOrigin).LengthSqr(); // Now check this node is not too close towards the threat if ( dist < threatDist * 1.5 ) { list.Insert( AI_NearNode_t(newID, dist) ); } } // mark visited wasVisited.Set(newID); } } } // We failed. Not cover node was found // Clear hint node used to set ducking GetOuter()->ClearHintNode(); return NO_NODE; }
bool CASW_Spawn_Manager::FindHordePosition() { // need to find a suitable place from which to spawn a horde // this place should: // - be far enough away from the marines so the whole horde can spawn before the marines get there // - should have a clear path to the marines UpdateCandidateNodes(); // decide if the horde is going to come from behind or in front bool bNorth = RandomFloat() < 0.7f; if ( m_northCandidateNodes.Count() <= 0 ) { bNorth = false; } else if ( m_southCandidateNodes.Count() <= 0 ) { bNorth = true; } CUtlVector<int> &candidateNodes = bNorth ? m_northCandidateNodes : m_southCandidateNodes; if ( candidateNodes.Count() <= 0 ) { if ( asw_director_debug.GetBool() ) { Msg( " Failed to find horde pos as there are no candidate nodes\n" ); } return false; } int iMaxTries = 3; // количество попыток найти место для спавна волны for ( int i=0 ; i<iMaxTries ; i++ ) { int iChosen = RandomInt( 0, candidateNodes.Count() - 1); CAI_Node *pNode = GetNetwork()->GetNode( candidateNodes[iChosen] ); if ( !pNode ) continue; float flDistance = 0; CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(UTIL_ASW_NearestMarine( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), flDistance )); if ( !pMarine ) { if ( asw_director_debug.GetBool() ) { Msg( " Failed to find horde pos as there is no nearest marine\n" ); } return false; } // check if there's a route from this node to the marine(s) AI_Waypoint_t *pRoute = ASWPathUtils()->BuildRoute( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), pMarine->GetAbsOrigin(), NULL, 100 ); if ( !pRoute ) { if ( asw_director_debug.GetInt() >= 2 ) { Msg( " Discarding horde node %d as there's no route.\n", iChosen ); } continue; } if ( bNorth && UTIL_ASW_DoorBlockingRoute( pRoute, true ) ) { if ( asw_director_debug.GetInt() >= 2 ) { Msg( " Discarding horde node %d as there's a door in the way.\n", iChosen ); } DeleteRoute( pRoute ); continue; } m_vecHordePosition = pNode->GetPosition( CANDIDATE_ALIEN_HULL ) + Vector( 0, 0, 32 ); // spawn facing the nearest marine Vector vecDir = pMarine->GetAbsOrigin() - m_vecHordePosition; vecDir.z = 0; vecDir.NormalizeInPlace(); VectorAngles( vecDir, m_angHordeAngle ); if ( asw_director_debug.GetInt() >= 2 ) { Msg( " Accepting horde node %d.\n", iChosen ); } DeleteRoute( pRoute ); return true; } if ( asw_director_debug.GetBool() ) { Msg( " Failed to find horde pos as we tried 3 times to build routes to possible locations, but failed\n" ); } return false; }
void CASW_Spawn_Manager::UpdateCandidateNodes() { // don't update too frequently if ( m_CandidateUpdateTimer.HasStarted() && !m_CandidateUpdateTimer.IsElapsed() ) return; m_CandidateUpdateTimer.Start( asw_candidate_interval.GetFloat() ); if ( !GetNetwork() || !GetNetwork()->NumNodes() ) { m_vecHordePosition = vec3_origin; if ( asw_director_debug.GetBool() ) Msg("Error: Can't spawn hordes as this map has no node network\n"); return; } CASW_Game_Resource *pGameResource = ASWGameResource(); if ( !pGameResource ) return; Vector vecSouthMarine = vec3_origin; Vector vecNorthMarine = vec3_origin; for ( int i=0; i<pGameResource->GetMaxMarineResources(); i++ ) { CASW_Marine_Resource *pMR = pGameResource->GetMarineResource(i); if ( !pMR ) continue; CASW_Marine *pMarine = pMR->GetMarineEntity(); if ( !pMarine || pMarine->GetHealth() <= 0 ) continue; if ( vecSouthMarine == vec3_origin || vecSouthMarine.y > pMarine->GetAbsOrigin().y ) { vecSouthMarine = pMarine->GetAbsOrigin(); } if ( vecNorthMarine == vec3_origin || vecNorthMarine.y < pMarine->GetAbsOrigin().y ) { vecNorthMarine = pMarine->GetAbsOrigin(); } } if ( vecSouthMarine == vec3_origin || vecNorthMarine == vec3_origin ) // no live marines return; int iNumNodes = GetNetwork()->NumNodes(); m_northCandidateNodes.Purge(); m_southCandidateNodes.Purge(); for ( int i=0 ; i<iNumNodes; i++ ) { CAI_Node *pNode = GetNetwork()->GetNode( i ); if ( !pNode || pNode->GetType() != NODE_GROUND ) continue; Vector vecPos = pNode->GetPosition( CANDIDATE_ALIEN_HULL ); // find the nearest marine to this node float flDistance = 0; CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(UTIL_ASW_NearestMarine( vecPos, flDistance )); if ( !pMarine ) return; if ( flDistance > asw_horde_max_distance.GetFloat() || flDistance < asw_horde_min_distance.GetFloat() ) continue; // check node isn't in an exit trigger bool bInsideEscapeArea = false; for ( int d=0; d<m_EscapeTriggers.Count(); d++ ) { if ( m_EscapeTriggers[d]->CollisionProp()->IsPointInBounds( vecPos ) ) { bInsideEscapeArea = true; break; } } if ( bInsideEscapeArea ) continue; if ( vecPos.y >= vecSouthMarine.y ) { if ( asw_director_debug.GetInt() == 3 ) { NDebugOverlay::Box( vecPos, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 32, 32, 128, 10, 60.0f ); } m_northCandidateNodes.AddToTail( i ); } if ( vecPos.y <= vecNorthMarine.y ) { m_southCandidateNodes.AddToTail( i ); if ( asw_director_debug.GetInt() == 3 ) { NDebugOverlay::Box( vecPos, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 128, 32, 32, 10, 60.0f ); } } } }
bool CASW_Spawn_Manager::SpawnAlientAtRandomNode() { if ( spawnRandomAlienTimer.HasStarted() && !spawnRandomAlienTimer.IsElapsed() ) return false; spawnRandomAlienTimer.Start( MINIMUM_SPAWN_DELAY ); UpdateCandidateNodes(); // decide if the alien is going to come from behind or in front bool bNorth = RandomFloat() < 0.7f; if ( m_northCandidateNodes.Count() <= 0 ) { bNorth = false; } else if ( m_southCandidateNodes.Count() <= 0 ) { bNorth = true; } CUtlVector<int> &candidateNodes = bNorth ? m_northCandidateNodes : m_southCandidateNodes; if ( candidateNodes.Count() <= 0 ) return false; const char *szAlienClass = wandererQueue[wandererQueueStart]; Vector vecMins, vecMaxs; GetAlienBounds( szAlienClass, vecMins, vecMaxs ); int alienHull = getAlienHull(szAlienClass); int iMaxTries = 3; for ( int i=0 ; i<iMaxTries ; i++ ) { int iChosen = RandomInt( 0, candidateNodes.Count() - 1); CAI_Node *pNode = GetNetwork()->GetNode( candidateNodes[iChosen] ); if ( !pNode ) continue; float flDistance = 0; CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(UTIL_ASW_NearestMarine( pNode->GetPosition( alienHull ), flDistance )); if ( !pMarine ) return false; // check if there's a route from this node to the marine(s) AI_Waypoint_t *pRoute = ASWPathUtils()->BuildRoute( pNode->GetPosition( alienHull ), pMarine->GetAbsOrigin(), NULL, 100 ); if ( !pRoute ) { if ( asw_director_debug.GetBool() ) { NDebugOverlay::Cross3D( pNode->GetOrigin(), 10.0f, 255, 128, 0, true, 20.0f ); } continue; } if ( bNorth && UTIL_ASW_DoorBlockingRoute( pRoute, true ) ) { DeleteRoute( pRoute ); continue; } Vector vecSpawnPos = pNode->GetPosition( alienHull ) + Vector( 0, 0, 32 ); bool needsGround = Q_stricmp(szAlienClass, "asw_buzzer") != 0; if ( ValidSpawnPoint( vecSpawnPos, vecMins, vecMaxs, needsGround, MARINE_NEAR_DISTANCE ) ) { float extraClearanceFactor = 1; //the boomer's hull seems to be too small for some reason if (Q_stricmp(szAlienClass, "asw_boomer") == 0) { extraClearanceFactor = 2; } Vector shiftSpawnBy = shiftSpawnPosition(vecSpawnPos, vecMins, vecMaxs, extraClearanceFactor); if (shiftSpawnBy.z == -1 ) { DeleteRoute( pRoute ); continue; } Vector shiftedSpawnPos = vecSpawnPos+shiftSpawnBy; Vector testShift = shiftSpawnPosition(shiftedSpawnPos, vecMins, vecMaxs, extraClearanceFactor); if (testShift.x != 0 || testShift.y != 0 || testShift.z != 0) { DeleteRoute( pRoute ); continue; } if ( SpawnAlienAt( szAlienClass, shiftedSpawnPos, vec3_angle ) ) { wandererQueueStart++; wandererQueueStart %= WANDERER_QUEUE_SIZE; if ( asw_director_debug.GetBool() ) { NDebugOverlay::Cross3D( vecSpawnPos, 25.0f, 255, 255, 255, true, 20.0f ); float flDist; CASW_Marine *pMarine = UTIL_ASW_NearestMarine( vecSpawnPos, flDist ); if ( pMarine ) { NDebugOverlay::Line( pMarine->GetAbsOrigin(), vecSpawnPos, 64, 64, 64, true, 60.0f ); } } DeleteRoute( pRoute ); return true; } } else { if ( asw_director_debug.GetBool() ) { NDebugOverlay::Cross3D( vecSpawnPos, 25.0f, 255, 0, 0, true, 20.0f ); } } DeleteRoute( pRoute ); } return false; }
//========================================================= // Actualiza los mejores nodos para la creación de hijos. //========================================================= void CDirector_Manager::UpdateNodes() { // Aún no toca actualizar. if ( CandidateUpdateTimer.HasStarted() && !CandidateUpdateTimer.IsElapsed() ) return; // Empezar el cronometro. CandidateUpdateTimer.Start(director_update_nodes.GetFloat()); // ¡Este mapa no tiene nodos! if ( !GetNetwork() || !GetNetwork()->NumNodes() ) { ClientPrint(UTIL_InPlayerByIndex(1), HUD_PRINTCENTER, "#NoNODES"); Warning("[NODOS] Este mapa no tiene nodos de movimiento.\r\n"); return; } int iNumNodes = GetNetwork()->NumNodes(); SpawnNodes.Purge(); // Limpiamos la lista de nodos. // Revisamos cada nodo. for ( int i = 0; i < iNumNodes; ++i ) { CAI_Node *pNode = GetNetwork()->GetNode(i); // El nodo ya no existe o no es de suelo. if ( !pNode || pNode->GetType() != NODE_GROUND ) continue; // Buscar al jugador más cercano. float flDistance = 0; Vector vecPos = pNode->GetPosition(HULL_HUMAN); CIN_Player *pPlayer = UTIL_GetNearestInPlayer(vecPos, flDistance); // ¡Ninguno! if ( !pPlayer ) return; ConVarRef director_debug("director_debug"); ConVarRef director_min_distance("director_min_distance"); ConVarRef director_max_distance("director_max_distance"); ConVarRef director_spawn_outview("director_spawn_outview"); CBaseEntity *pChild = gEntList.FindEntityByNameNearest(CHILD_NAME, vecPos, 20); CBaseEntity *pSpawn = gEntList.FindEntityByClassname(NULL, "info_player_start"); if ( pSpawn ) { float spawnDistance = vecPos.DistTo(pSpawn->GetAbsOrigin()); // Este nodo esta muy cerca del Spawn. if ( spawnDistance < SPAWN_OUT_DISTANCE ) continue; } // Hay un hijo aquí. if ( pChild ) continue; // Este nodo esta muy lejos o muy cerca. if ( flDistance > director_max_distance.GetFloat() || flDistance < director_min_distance.GetFloat() ) continue; // No usar nodos que esten a la vista de los jugadores. if ( director_spawn_outview.GetBool() ) { if ( UTIL_IsPlayersVisible(vecPos) ) continue; } // Marcamos al nodo afortunado. if ( director_debug.GetBool() ) NDebugOverlay::Box(vecPos, -Vector(5, 5, 5), Vector(5, 5, 5), 32, 32, 128, 10, 6.0f); // Lo agregamos a la lista. SpawnNodes.AddToTail(i); } }
bool CASW_Spawn_Manager::SpawnAlientAtRandomNode() { UpdateCandidateNodes(); // decide if the alien is going to come from behind or in front bool bNorth = RandomFloat() < 0.7f; if ( m_northCandidateNodes.Count() <= 0 ) { bNorth = false; } else if ( m_southCandidateNodes.Count() <= 0 ) { bNorth = true; } CUtlVector<int> &candidateNodes = bNorth ? m_northCandidateNodes : m_southCandidateNodes; if ( candidateNodes.Count() <= 0 ) return false; const char *szAlienClass = "asw_drone"; Vector vecMins, vecMaxs; GetAlienBounds( szAlienClass, vecMins, vecMaxs ); int iMaxTries = 1; for ( int i=0 ; i<iMaxTries ; i++ ) { int iChosen = RandomInt( 0, candidateNodes.Count() - 1); CAI_Node *pNode = GetNetwork()->GetNode( candidateNodes[iChosen] ); if ( !pNode ) continue; float flDistance = 0; CASW_Marine *pMarine = dynamic_cast<CASW_Marine*>(UTIL_ASW_NearestMarine( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), flDistance )); if ( !pMarine ) return false; // check if there's a route from this node to the marine(s) AI_Waypoint_t *pRoute = ASWPathUtils()->BuildRoute( pNode->GetPosition( CANDIDATE_ALIEN_HULL ), pMarine->GetAbsOrigin(), NULL, 100 ); if ( !pRoute ) { if ( asw_director_debug.GetBool() ) { NDebugOverlay::Cross3D( pNode->GetOrigin(), 10.0f, 255, 128, 0, true, 20.0f ); } continue; } if ( bNorth && UTIL_ASW_DoorBlockingRoute( pRoute, true ) ) { DeleteRoute( pRoute ); continue; } // riflemod: preventing wanderers from spawning behind closed airlocks if (UTIL_ASW_BrushBlockingRoute(pRoute, MASK_PLAYERSOLID_BRUSHONLY, COLLISION_GROUP_PLAYER_MOVEMENT)) { DeleteRoute(pRoute); continue; } Vector vecSpawnPos = pNode->GetPosition( CANDIDATE_ALIEN_HULL ) + Vector( 0, 0, 32 ); if ( ValidSpawnPoint( vecSpawnPos, vecMins, vecMaxs, true, MARINE_NEAR_DISTANCE ) ) { if ( SpawnAlienAt( szAlienClass, vecSpawnPos, vec3_angle ) ) { if ( asw_director_debug.GetBool() ) { NDebugOverlay::Cross3D( vecSpawnPos, 25.0f, 255, 255, 255, true, 20.0f ); float flDist; CASW_Marine *pMarine = UTIL_ASW_NearestMarine( vecSpawnPos, flDist ); if ( pMarine ) { NDebugOverlay::Line( pMarine->GetAbsOrigin(), vecSpawnPos, 64, 64, 64, true, 60.0f ); } } DeleteRoute( pRoute ); return true; } } else { if ( asw_director_debug.GetBool() ) { NDebugOverlay::Cross3D( vecSpawnPos, 25.0f, 255, 0, 0, true, 20.0f ); } } DeleteRoute( pRoute ); } return false; }