//========================================================= // 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; }
//========================================================= // 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; } }
void CASW_Alien_Jumper::LockJumpNode( void ) { if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) == false ) return; if ( GetNavigator()->GetPath() == NULL ) return; if ( asw_test_new_alien_jump.GetBool() == false ) return; AI_Waypoint_t *pWaypoint = GetNavigator()->GetPath()->GetCurWaypoint(); while ( pWaypoint ) { AI_Waypoint_t *pNextWaypoint = pWaypoint->GetNext(); if ( pNextWaypoint && pNextWaypoint->NavType() == NAV_JUMP && pWaypoint->iNodeID != NO_NODE ) { CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( pWaypoint->iNodeID ); if ( pNode ) { //NDebugOverlay::Box( pNode->GetOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 255, 0, 0, 0, 2 ); pNode->Lock( 0.5f ); break; } } else { pWaypoint = pWaypoint->GetNext(); } } }
//----------------------------------------------------------------------------- // Purpose: For destroying nodes in wc edit mode // Input : // Output : //----------------------------------------------------------------------------- void NWCEdit::DestroyAINode( CBasePlayer *pPlayer ) { // ------------------------------------------------------------- // Check that WC is running with the right map version // ------------------------------------------------------------- if (!IsWCVersionValid()) { return; } if (!pPlayer) { return; } NodeType_e nNodeType = NODE_GROUND; if (g_pAINetworkManager->GetEditOps()->m_bAirEditMode) { nNodeType = NODE_AIR; } CAI_Node* pAINode = FindPickerAINode(pPlayer, nNodeType); if (pAINode) { int status = Editor_DeleteNode(g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[pAINode->GetId()], false); if (status == Editor_BadCommand) { Msg( "Worldcraft failed on deletion...\n" ); } else if (status == Editor_OK) { // Mark this node as deleted and changed pAINode->SetType( NODE_DELETED ); pAINode->m_eNodeInfo |= bits_NODE_WC_CHANGED; // Note that network needs to be rebuild g_pAINetworkManager->GetEditOps()->SetRebuildFlags(); g_pAINetworkManager->GetEditOps()->m_pLastDeletedNode = pAINode; // Now go through at delete any dynamic links that were attached to this node for (int link = 0; link < pAINode->NumLinks(); link++) { int nSrcID = pAINode->GetLinkByIndex(link)->m_iSrcID; int nDstID = pAINode->GetLinkByIndex(link)->m_iDestID; if (CAI_DynamicLink::GetDynamicLink(nSrcID, nDstID)) { int nWCSrcID = g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[nSrcID]; int nWCDstID = g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[nDstID]; int status = Editor_DeleteNodeLink(nWCSrcID, nWCDstID); if (status == Editor_BadCommand) { Msg( "Worldcraft failed on node link deletion...\n" ); } } } } } }
//----------------------------------------------------------------------------- // Computes the link type //----------------------------------------------------------------------------- Navigation_t CAI_Pathfinder::ComputeWaypointType( CAI_Node **ppNodes, int parentID, int destID ) { Navigation_t navType = NAV_NONE; CAI_Node *pNode = ppNodes[parentID]; for (int link=0; link < pNode->NumLinks();link++) { if (pNode->GetLinkByIndex(link)->DestNodeID(parentID) == destID) { // BRJ 10/1/02 // FIXME: pNPC->CapabilitiesGet() is actually the mechanism by which fliers // filter out the bitfields in the waypoint type (most importantly, bits_MOVE_CAP_GROUND) // that would cause the waypoint distance to be computed in a 2D, as opposed to 3D fashion // This is a super-scary weak link if you ask me. int linkMoveTypeBits = pNode->GetLinkByIndex(link)->m_iAcceptedMoveTypes[GetHullType()]; int moveTypeBits = ( linkMoveTypeBits & CapabilitiesGet()); if ( !moveTypeBits && linkMoveTypeBits == bits_CAP_MOVE_JUMP ) { Assert( pNode->GetHint() && pNode->GetHint()->HintType() == HINT_JUMP_OVERRIDE ); ppNodes[destID]->Lock(0.3); moveTypeBits = linkMoveTypeBits; } Navigation_t linkType = MoveBitsToNavType( moveTypeBits ); // This will only trigger if the links disagree about their nav type Assert( (navType == NAV_NONE) || (navType == linkType) ); navType = linkType; break; } } // @TODO (toml 10-15-02): one would not expect to come out of the above logic // with NAV_NONE. However, if a graph is newly built, it can contain malformed // links that are referred to by the destination node, not the source node. // This has to be fixed if ( navType == NAV_NONE ) { pNode = ppNodes[destID]; for (int link=0; link < pNode->NumLinks();link++) { if (pNode->GetLinkByIndex(link)->DestNodeID(parentID) == destID) { int npcMoveBits = CapabilitiesGet(); int nodeMoveBits = pNode->GetLinkByIndex(link)->m_iAcceptedMoveTypes[GetHullType()]; int moveTypeBits = ( npcMoveBits & nodeMoveBits ); Navigation_t linkType = MoveBitsToNavType( moveTypeBits ); Assert( (navType == NAV_NONE) || (navType == linkType) ); navType = linkType; DevMsg( "Note: Strange link found between nodes in AI node graph\n" ); break; } } } AssertMsg( navType != NAV_NONE, "Pathfinder appears to have output a path with consecutive nodes thate are not actually connected\n" ); return navType; }
void CAI_DynamicLinkController::GenerateLinksFromVolume() { Assert( m_ControlledLinks.Count() == 0 ); int nNodes = g_pBigAINet->NumNodes(); CAI_Node **ppNodes = g_pBigAINet->AccessNodes(); const float MinDistCareSq = Square(MAX_NODE_LINK_DIST + 0.1); const Vector &origin = WorldSpaceCenter(); Vector vAbsMins, vAbsMaxs; CollisionProp()->WorldSpaceAABB( &vAbsMins, &vAbsMaxs ); vAbsMins -= Vector( 1, 1, 1 ); vAbsMaxs += Vector( 1, 1, 1 ); for ( int i = 0; i < nNodes; i++ ) { CAI_Node *pNode = ppNodes[i]; const Vector &nodeOrigin = pNode->GetOrigin(); if ( origin.DistToSqr(nodeOrigin) < MinDistCareSq ) { int nLinks = pNode->NumLinks(); for ( int j = 0; j < nLinks; j++ ) { CAI_Link *pLink = pNode->GetLinkByIndex( j ); int iLinkDest = pLink->DestNodeID( i ); if ( iLinkDest > i ) { const Vector &originOther = ppNodes[iLinkDest]->GetOrigin(); if ( origin.DistToSqr(originOther) < MinDistCareSq ) { if ( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, nodeOrigin, originOther - nodeOrigin ) ) { Assert( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, originOther, nodeOrigin - originOther ) ); CAI_DynamicLink *pLink = (CAI_DynamicLink *)CreateEntityByName( "info_node_link" ); pLink->m_nSrcID = i; pLink->m_nDestID = iLinkDest; pLink->m_nSrcEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nSrcID ); pLink->m_nDestEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nDestID ); pLink->m_nLinkState = m_nLinkState; pLink->m_strAllowUse = m_strAllowUse; pLink->m_bFixedUpIds = true; pLink->m_bNotSaved = true; pLink->Spawn(); m_ControlledLinks.AddToTail( pLink ); } } } } } } }
void CAI_RadialLinkController::ModifyNodeLinks( bool bMakeStale ) { int nNodes = g_pBigAINet->NumNodes(); CAI_Node **ppNodes = g_pBigAINet->AccessNodes(); VPROF_BUDGET("ModifyLinks", "ModifyLinks"); const float MinDistCareSq = Square( ai_radial_max_link_dist.GetFloat() + 0.1 ); for ( int i = 0; i < nNodes; i++ ) { CAI_Node *pNode = ppNodes[i]; const Vector &nodeOrigin = pNode->GetOrigin(); if ( m_vecAtRestOrigin.DistToSqr(nodeOrigin) < MinDistCareSq ) { int nLinks = pNode->NumLinks(); for ( int j = 0; j < nLinks; j++ ) { CAI_Link *pLink = pNode->GetLinkByIndex( j ); int iLinkDest = pLink->DestNodeID( i ); if ( iLinkDest > i ) { bool bQualify = true; if( ( (pLink->m_iAcceptedMoveTypes[HULL_HUMAN]||pLink->m_iAcceptedMoveTypes[HULL_WIDE_HUMAN]) & bits_CAP_MOVE_GROUND) == 0 ) { // Micro-optimization: Ignore any connection that's not a walking connection for humans.(sjb) bQualify = false; } const Vector &originOther = ppNodes[iLinkDest]->GetOrigin(); if ( bQualify && m_vecAtRestOrigin.DistToSqr(originOther) < MinDistCareSq ) { if ( IsRayIntersectingSphere(nodeOrigin, originOther - nodeOrigin, m_vecAtRestOrigin, m_flRadius) ) { if( bMakeStale ) { pLink->m_LinkInfo |= bits_LINK_STALE_SUGGESTED; pLink->m_timeStaleExpires = FLT_MAX; } else { pLink->m_LinkInfo &= ~bits_LINK_STALE_SUGGESTED; } } } } } } } }
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; } }
//========================================================= // 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; }
bool CASW_Alien_Jumper::IsUnusableNode(int iNodeID, CAI_Hint *pHint) { bool iBaseReturn = BaseClass::IsUnusableNode( iNodeID, pHint ); if ( asw_test_new_alien_jump.GetBool() == 0 ) return iBaseReturn; CAI_Node *pNode = GetNavigator()->GetNetwork()->GetNode( iNodeID ); if ( pNode ) { if ( pNode->IsLocked() ) return true; } return iBaseReturn; }
//========================================================= // 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; } }
void CAI_Hint::OnRestore() { BaseClass::OnRestore(); m_NodeData.nNodeID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( m_NodeData.nWCNodeID ); FixupTargetNode(); CAI_Node *pNode = GetNode(); if ( !pNode ) { if ( m_NodeData.nWCNodeID > 0 ) DevMsg("Warning: AI hint has incorrect or no AI node\n"); } else { m_NodeData.vecPosition = pNode->GetOrigin(); Teleport( &m_NodeData.vecPosition, NULL, NULL ); pNode->SetHint( this ); } }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CAI_Link *CAI_DynamicLink::FindLink() { CAI_Node * pSrcNode = g_pBigAINet->GetNode(m_nSrcID, false); if ( pSrcNode ) { int numLinks = pSrcNode->NumLinks(); for (int i=0;i<numLinks;i++) { CAI_Link* pLink = pSrcNode->GetLinkByIndex(i); if (((pLink->m_iSrcID == m_nSrcID )&& (pLink->m_iDestID == m_nDestID)) || ((pLink->m_iSrcID == m_nDestID)&& (pLink->m_iDestID == m_nSrcID )) ) { return pLink; } } } return NULL; }
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; } } } }
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; }
//========================================================= // 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::SpawnRandomParasitePack( int nParasites ) { int iNumNodes = g_pBigAINet->NumNodes(); if ( iNumNodes < 6 ) return false; int nHull = HULL_TINY; CUtlVector<CASW_Open_Area*> aAreas; for ( int i = 0; i < 6; i++ ) { CAI_Node *pNode = NULL; int nTries = 0; while ( nTries < 5 && ( !pNode || pNode->GetType() != NODE_GROUND ) ) { pNode = g_pBigAINet->GetNode( RandomInt( 0, iNumNodes ) ); nTries++; } if ( pNode ) { CASW_Open_Area *pArea = FindNearbyOpenArea( pNode->GetOrigin(), HULL_MEDIUMBIG ); if ( pArea && pArea->m_nTotalLinks > 30 ) { // test if there's room to spawn a shieldbug at that spot if ( ValidSpawnPoint( pArea->m_pNode->GetPosition( nHull ), NAI_Hull::Mins( nHull ), NAI_Hull::Maxs( nHull ), true, false ) ) { aAreas.AddToTail( pArea ); } else { delete pArea; } } } // stop searching once we have 3 acceptable candidates if ( aAreas.Count() >= 3 ) break; } // find area with the highest connectivity CASW_Open_Area *pBestArea = NULL; for ( int i = 0; i < aAreas.Count(); i++ ) { CASW_Open_Area *pArea = aAreas[i]; if ( !pBestArea || pArea->m_nTotalLinks > pBestArea->m_nTotalLinks ) { pBestArea = pArea; } } if ( pBestArea ) { for ( int i = 0; i < nParasites; i++ ) { // raise the position by 12 units, a workaround for parasites // falling through displacements CBaseEntity *pAlien = SpawnAlienAt( "asw_parasite", pBestArea->m_pNode->GetPosition( nHull ) + 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 parasite at %f %f %f\n", pAlien->GetAbsOrigin() ); NDebugOverlay::Cross3D( pAlien->GetAbsOrigin(), 8.0f, 255, 0, 0, true, 20.0f ); } } aAreas.PurgeAndDeleteElements(); return true; } aAreas.PurgeAndDeleteElements(); return false; }
bool CASW_Weapon_Blink::SetBlinkDestination() { CASW_Player *pPlayer = GetCommander(); if ( !pPlayer ) return false; CASW_Marine *pMarine = GetMarine(); if ( !pMarine ) return false; Vector vecStart = pPlayer->GetCrosshairTracePos() + Vector( 0, 0, 30 ); Vector vecEnd = pPlayer->GetCrosshairTracePos() - Vector( 0, 0, 30 ); trace_t tr; UTIL_TraceHull( vecStart, vecEnd, pMarine->WorldAlignMins(), pMarine->WorldAlignMaxs(), MASK_PLAYERSOLID_BRUSHONLY, pMarine, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); if ( tr.startsolid || tr.allsolid ) { m_vecInvalidDestination = vecStart; return false; } if ( pMarine->GetAbsOrigin().DistTo( tr.endpos ) > asw_blink_range.GetFloat() ) { m_vecInvalidDestination = tr.endpos; return false; } Vector vecDest = tr.endpos; // now see if we can build an AI path from the marine to this spot bool bValidRoute = false; if ( !pMarine->GetPathfinder() ) { m_vecInvalidDestination = vecDest; return false; } AI_Waypoint_t *pRoute = pMarine->GetPathfinder()->BuildRoute( pMarine->GetAbsOrigin(), vecDest, NULL, 30, NAV_GROUND, bits_BUILD_GROUND | bits_BUILD_IGNORE_NPCS ); if ( pRoute && !UTIL_ASW_DoorBlockingRoute( pRoute, true ) ) { if ( !UTIL_ASW_BrushBlockingRoute( pRoute, MASK_PLAYERSOLID_BRUSHONLY, COLLISION_GROUP_PLAYER_MOVEMENT ) ) { // if end node of the route is too Z different, then abort, to stop people jumping on top of walls AI_Waypoint_t *pLast = pRoute->GetLast(); if ( pLast ) { AI_Waypoint_t *pNode = pLast->GetPrev(); if ( !pNode || fabs( pNode->GetPos().z - pLast->GetPos().z ) < 80.0f ) { bValidRoute = true; } } } } if ( !bValidRoute ) { // find the closest node to the dest and try to path there instead CAI_Network *pNetwork = pMarine->GetNavigator() ? pMarine->GetNavigator()->GetNetwork() : NULL; if ( pNetwork ) { int nNode = pNetwork->NearestNodeToPoint( vecDest, false ); if ( nNode != NO_NODE ) { CAI_Node *pNode = pNetwork->GetNode( nNode ); if ( pNode && pNode->GetType() == NODE_GROUND ) { vecDest = pNode->GetOrigin(); if ( pRoute ) { ASWPathUtils()->DeleteRoute( pRoute ); pRoute = NULL; } pRoute = pMarine->GetPathfinder()->BuildRoute( pMarine->GetAbsOrigin(), vecDest, NULL, 30, NAV_GROUND, bits_BUILD_GROUND | bits_BUILD_IGNORE_NPCS ); if ( pRoute && !UTIL_ASW_DoorBlockingRoute( pRoute, true ) ) { if ( !UTIL_ASW_BrushBlockingRoute( pRoute, MASK_PLAYERSOLID_BRUSHONLY, COLLISION_GROUP_PLAYER_MOVEMENT ) ) { bValidRoute = true; } } if ( !bValidRoute ) { m_vecInvalidDestination = vecDest; } } } } } if ( !bValidRoute ) { if ( pRoute ) { ASWPathUtils()->DeleteRoute( pRoute ); pRoute = NULL; } return false; } if ( asw_blink_debug.GetBool() ) { ASWPathUtils()->DebugDrawRoute( pMarine->GetAbsOrigin(), pRoute ); } m_vecAbilityDestination = vecDest; if ( pRoute ) { ASWPathUtils()->DeleteRoute( pRoute ); pRoute = NULL; } return true; }
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; }
//========================================================= //========================================================= void CNodeEnt::Spawn( void ) { // --------------------------------------------------------------------------------- // If just a hint node (not used for navigation) just create a hint and bail // --------------------------------------------------------------------------------- if (FClassnameIs( this, "info_hint" )) { if (m_eHintType) { CAI_Hint::CreateHint(GetEntityName(),GetAbsOrigin(),(Hint_e)m_eHintType,NO_NODE,m_strGroup); } else { Msg("WARNING: Hint node with no hint type!\n"); } UTIL_RemoveImmediate( this ); return; } // --------------------------------------------------------------------------------- // First check if this node has a hint. If so create a hint entity // --------------------------------------------------------------------------------- CAI_Hint *pHint = NULL; if (m_eHintType || m_strGroup != NULL_STRING) { pHint = CAI_Hint::CreateHint(GetEntityName(),GetAbsOrigin(),(Hint_e)m_eHintType,m_nNodeCount,m_strGroup); } // --------------------------------------------------------------------------------- // 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 { Msg("AI node graph corrupt\n"); } } m_nNodeCount++; UTIL_RemoveImmediate( this ); return; } 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_nWCNodeID; } // Keep track of largest index used by WC if (g_pAINetworkManager->GetEditOps()->m_nNextWCIndex <= m_nWCNodeID) { g_pAINetworkManager->GetEditOps()->m_nNextWCIndex = m_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 ); } // 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 ); }
// heuristic to find reasonably open space - searches for areas with high node connectivity CASW_Open_Area* CASW_Spawn_Manager::FindNearbyOpenArea( const Vector &vecSearchOrigin, int nSearchHull ) { CBaseEntity *pStartEntity = gEntList.FindEntityByClassname( NULL, "info_player_start" ); int iNumNodes = g_pBigAINet->NumNodes(); CAI_Node *pHighestConnectivity = NULL; int nHighestLinks = 0; for ( int i=0 ; i<iNumNodes; i++ ) { CAI_Node *pNode = g_pBigAINet->GetNode( i ); if ( !pNode || pNode->GetType() != NODE_GROUND ) continue; Vector vecPos = pNode->GetOrigin(); float flDist = vecPos.DistTo( vecSearchOrigin ); if ( flDist > 400.0f ) continue; // discard if node is too near start location if ( pStartEntity && vecPos.DistTo( pStartEntity->GetAbsOrigin() ) < 1400.0f ) // NOTE: assumes all start points are clustered near one another continue; // discard if node is inside an escape area 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; // count links that drones could follow int nLinks = pNode->NumLinks(); int nValidLinks = 0; for ( int k = 0; k < nLinks; k++ ) { CAI_Link *pLink = pNode->GetLinkByIndex( k ); if ( !pLink ) continue; if ( !( pLink->m_iAcceptedMoveTypes[nSearchHull] & bits_CAP_MOVE_GROUND ) ) continue; nValidLinks++; } if ( nValidLinks > nHighestLinks ) { nHighestLinks = nValidLinks; pHighestConnectivity = pNode; } if ( asw_director_debug.GetBool() ) { NDebugOverlay::Text( vecPos, UTIL_VarArgs( "%d", nValidLinks ), false, 10.0f ); } } if ( !pHighestConnectivity ) return NULL; // now, starting at the new node, find all nearby nodes with a minimum connectivity CASW_Open_Area *pArea = new CASW_Open_Area(); pArea->m_vecOrigin = pHighestConnectivity->GetOrigin(); pArea->m_pNode = pHighestConnectivity; int nMinLinks = nHighestLinks * 0.3f; nMinLinks = MAX( nMinLinks, 4 ); pArea->m_aAreaNodes.AddToTail( pHighestConnectivity ); if ( asw_director_debug.GetBool() ) { Msg( "minLinks = %d\n", nMinLinks ); } pArea->m_nTotalLinks = 0; for ( int i=0 ; i<iNumNodes; i++ ) { CAI_Node *pNode = g_pBigAINet->GetNode( i ); if ( !pNode || pNode->GetType() != NODE_GROUND ) continue; Vector vecPos = pNode->GetOrigin(); float flDist = vecPos.DistTo( pArea->m_vecOrigin ); if ( flDist > 400.0f ) continue; // discard if node is inside an escape area 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; // count links that drones could follow int nLinks = pNode->NumLinks(); int nValidLinks = 0; for ( int k = 0; k < nLinks; k++ ) { CAI_Link *pLink = pNode->GetLinkByIndex( k ); if ( !pLink ) continue; if ( !( pLink->m_iAcceptedMoveTypes[nSearchHull] & bits_CAP_MOVE_GROUND ) ) continue; nValidLinks++; } if ( nValidLinks >= nMinLinks ) { pArea->m_aAreaNodes.AddToTail( pNode ); pArea->m_nTotalLinks += nValidLinks; } } // highlight and measure bounds Vector vecAreaMins = Vector( FLT_MAX, FLT_MAX, FLT_MAX ); Vector vecAreaMaxs = Vector( -FLT_MAX, -FLT_MAX, -FLT_MAX ); for ( int i = 0; i < pArea->m_aAreaNodes.Count(); i++ ) { vecAreaMins = VectorMin( vecAreaMins, pArea->m_aAreaNodes[i]->GetOrigin() ); vecAreaMaxs = VectorMax( vecAreaMaxs, pArea->m_aAreaNodes[i]->GetOrigin() ); if ( asw_director_debug.GetBool() ) { if ( i == 0 ) { NDebugOverlay::Cross3D( pArea->m_aAreaNodes[i]->GetOrigin(), 20.0f, 255, 255, 64, true, 10.0f ); } else { NDebugOverlay::Cross3D( pArea->m_aAreaNodes[i]->GetOrigin(), 10.0f, 255, 128, 0, true, 10.0f ); } } } Vector vecArea = ( vecAreaMaxs - vecAreaMins ); float flArea = vecArea.x * vecArea.y; if ( asw_director_debug.GetBool() ) { Msg( "area mins = %f %f %f\n", VectorExpand( vecAreaMins ) ); Msg( "area maxs = %f %f %f\n", VectorExpand( vecAreaMaxs ) ); NDebugOverlay::Box( vec3_origin, vecAreaMins, vecAreaMaxs, 255, 128, 128, 10, 10.0f ); Msg( "Total links = %d Area = %f\n", pArea->m_nTotalLinks, flArea ); } return pArea; }
//----------------------------------------------------------------------------- // 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 : Returns false if the dynamic link doesn't have a corresponding // node link // Input : // Output : //------------------------------------------------------------------------------ bool CAI_DynamicLink::IsLinkValid( void ) { CAI_Node *pNode = g_pBigAINet->GetNode(m_nSrcID); return ( pNode->GetLink( m_nDestID ) != NULL ); }
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; }
int CAI_TacticalServices::FindLosNode(const Vector &vThreatPos, const Vector &vThreatEyePos, float flMinThreatDist, float flMaxThreatDist, float flBlockTime, FlankType_t eFlankType, const Vector &vecFlankRefPos, float flFlankParam ) { if ( !CAI_NetworkManager::NetworksLoaded() ) return NO_NODE; AI_PROFILE_SCOPE( CAI_TacticalServices_FindLosNode ); MARK_TASK_EXPENSIVE(); int iMyNode = GetPathfinder()->NearestNodeToNPC(); 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; } // ------------------------------------------------------------------------------------ // We're going to search for a shoot node by expanding to our current node's neighbors // and then their neighbors, until a shooting position 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 wasVisited.Set( iMyNode ); list.Insert( AI_NearNode_t(iMyNode, 0) ); static int nSearchRandomizer = 0; // tries to ensure the links are searched in a different order each time; while ( list.Count() ) { int nodeIndex = list.ElementAtHead().nodeIndex; // remove this item from the list list.RemoveAtHead(); const Vector &nodeOrigin = GetNetwork()->GetNode(nodeIndex)->GetPosition(GetHullType()); // HACKHACK: Can't we rework this loop and get rid of this? // skip the starting node, or we probably wouldn't have called this function. if ( nodeIndex != iMyNode ) { bool skip = false; // See if the node satisfies the flanking criteria. switch ( eFlankType ) { case FLANKTYPE_NONE: break; case FLANKTYPE_RADIUS: { Vector vecDist = nodeOrigin - vecFlankRefPos; if ( vecDist.Length() < flFlankParam ) { skip = true; } break; } case FLANKTYPE_ARC: { Vector vecEnemyToRef = vecFlankRefPos - vThreatPos; VectorNormalize( vecEnemyToRef ); Vector vecEnemyToNode = nodeOrigin - vThreatPos; VectorNormalize( vecEnemyToNode ); float flDot = DotProduct( vecEnemyToRef, vecEnemyToNode ); if ( RAD2DEG( acos( flDot ) ) < flFlankParam ) { skip = true; } break; } } // Don't accept climb nodes, and assume my nearest node isn't valid because // we decided to make this check in the first place. Keep moving if ( !skip && !GetNetwork()->GetNode(nodeIndex)->IsLocked() && GetNetwork()->GetNode(nodeIndex)->GetType() != NODE_CLIMB ) { // Now check its distance and only accept if in range float flThreatDist = ( nodeOrigin - vThreatPos ).Length(); if ( flThreatDist < flMaxThreatDist && flThreatDist > flMinThreatDist ) { CAI_Node *pNode = GetNetwork()->GetNode(nodeIndex); if ( GetOuter()->IsValidShootPosition( nodeOrigin, pNode, pNode->GetHint() ) ) { if (GetOuter()->TestShootPosition(nodeOrigin,vThreatEyePos)) { // Note when this node was used, so we don't try // to use it again right away. GetNetwork()->GetNode(nodeIndex)->Lock( flBlockTime ); #if 0 if ( GetOuter()->GetHintNode() ) { GetOuter()->GetHintNode()->Unlock(GetOuter()->GetHintDelay(GetOuter()->GetHintNode()->HintType())); GetOuter()->SetHintNode( NULL ); } // This used to not be set, why? (kenb) // @Note (toml 05-19-04): I think because stomping the hint can lead to // unintended side effects. The hint node is primarily a high level // tool, and certain NPCs break if it gets slammed here. If we need // this, we should propagate it out and let the schedule selector // or task decide to set the hint node GetOuter()->SetHintNode( GetNetwork()->GetNode(nodeIndex)->GetHint() ); #endif if ( ShouldDebugLos( nodeIndex ) ) { NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:los!", nodeIndex), false, 1 ); } // The next NPC who searches should use a slight different pattern nSearchRandomizer = nodeIndex; return nodeIndex; } else { if ( ShouldDebugLos( nodeIndex ) ) { NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:!shoot", nodeIndex), false, 1 ); } } } else { if ( ShouldDebugLos( nodeIndex ) ) { NDebugOverlay::Text( nodeOrigin, CFmtStr( "%d:!valid", nodeIndex), false, 1 ); } } } else { if ( ShouldDebugLos( nodeIndex ) ) { CFmtStr msg( "%d:%s", nodeIndex, ( flThreatDist < flMaxThreatDist ) ? "too close" : "too far" ); NDebugOverlay::Text( nodeOrigin, msg, false, 1 ); } } } } // Go through each link and add connected nodes to the list 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 visited, add to the list if (!wasVisited.IsBitSet(newID)) { float dist = (GetLocalOrigin() - GetNetwork()->GetNode(newID)->GetPosition(GetHullType())).LengthSqr(); list.Insert( AI_NearNode_t(newID, dist) ); wasVisited.Set( newID ); } } } // We failed. No range attack node node was found return NO_NODE; }
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::SpawnRandomShieldbug() { int iNumNodes = g_pBigAINet->NumNodes(); if ( iNumNodes < 6 ) return false; int nHull = HULL_WIDE_SHORT; CUtlVector<CASW_Open_Area*> aAreas; for ( int i = 0; i < 6; i++ ) { CAI_Node *pNode = NULL; int nTries = 0; while ( nTries < 5 && ( !pNode || pNode->GetType() != NODE_GROUND ) ) { pNode = g_pBigAINet->GetNode( RandomInt( 0, iNumNodes ) ); nTries++; } if ( pNode ) { CASW_Open_Area *pArea = FindNearbyOpenArea( pNode->GetOrigin(), HULL_MEDIUMBIG ); if ( pArea && pArea->m_nTotalLinks > 30 ) { // test if there's room to spawn a shieldbug at that spot if ( ValidSpawnPoint( pArea->m_pNode->GetPosition( nHull ), NAI_Hull::Mins( nHull ), NAI_Hull::Maxs( nHull ), true, false ) ) { aAreas.AddToTail( pArea ); } else { delete pArea; } } } // stop searching once we have 3 acceptable candidates if ( aAreas.Count() >= 3 ) break; } // find area with the highest connectivity CASW_Open_Area *pBestArea = NULL; for ( int i = 0; i < aAreas.Count(); i++ ) { CASW_Open_Area *pArea = aAreas[i]; if ( !pBestArea || pArea->m_nTotalLinks > pBestArea->m_nTotalLinks ) { pBestArea = pArea; } } if ( pBestArea ) { CBaseEntity *pAlien = SpawnAlienAt( "asw_shieldbug", pBestArea->m_pNode->GetPosition( nHull ), RandomAngle( 0, 360 ) ); IASW_Spawnable_NPC *pSpawnable = dynamic_cast<IASW_Spawnable_NPC*>( pAlien ); if ( pSpawnable ) { pSpawnable->SetAlienOrders(AOT_SpreadThenHibernate, vec3_origin, NULL); } aAreas.PurgeAndDeleteElements(); return true; } aAreas.PurgeAndDeleteElements(); return false; }
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; }
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 ); } } } }