示例#1
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
boost::python::list GetNavAreasAtBB( const Vector &mins, const Vector &maxs )
{
	boost::python::list l;
	CNavArea *area;
	Extent extent;
	Vector vAbsMins2, vAbsMaxs2;

	// TODO: Use ForAllAreasOverlappingExtent?
	FOR_EACH_VEC( TheNavAreas, it )
	{
		area = TheNavAreas[ it ];

		area->GetExtent( &extent );

		vAbsMins2 = extent.lo;
		vAbsMaxs2 = extent.hi;

		if( vAbsMins2[2] > vAbsMaxs2[2] )
		{
			float z = vAbsMaxs2[2];
			vAbsMaxs2[2] = vAbsMins2[2];
			vAbsMins2[2] = z;
		}
		else if( vAbsMins2[2] == vAbsMaxs2[2] )
		{
			vAbsMaxs2[2] += 1.0f;
		}

		// Does it intersects with our bounding box?
		if( IsBoxIntersectingBox( mins, maxs, 
			vAbsMins2, vAbsMaxs2 ) )
		{
			l.append( area->GetID() );
		}
	}
/**
 * Escape from the bomb.
 */
void EscapeFromBombState::OnUpdate( CCSBot *me )
{
	const Vector *bombPos = me->GetGameState()->GetBombPosition();

	// if we don't know where the bomb is, we shouldn't be in this state
	if (bombPos == NULL)
	{
		me->Idle();
		return;
	}

	// grab our knife to move quickly
	me->EquipKnife();

	// look around
	me->UpdateLookAround();

	if (me->UpdatePathMovement() != CCSBot::PROGRESSING)
	{
		// we have no path, or reached the end of one - create a new path far away from the bomb
		FarAwayFromPositionFunctor func( *bombPos );
		CNavArea *goalArea = FindMinimumCostArea( me->GetLastKnownArea(), func );

		// if this fails, we'll try again next time
		me->ComputePath( goalArea->GetCenter(), FASTEST_ROUTE );
	}
}
示例#3
0
/**
 * Build trivial path when start and goal are in the same nav area
 */
bool CNavPath::BuildTrivialPath( const Vector *start, const Vector *goal )
{
	m_segmentCount = 0;

	CNavArea *startArea = TheNavAreaGrid.GetNearestNavArea( start );
	if (startArea == NULL)
		return false;

	CNavArea *goalArea = TheNavAreaGrid.GetNearestNavArea( goal );
	if (goalArea == NULL)
		return false;

	m_segmentCount = 2;

	m_path[0].area = startArea;
	m_path[0].pos.x = start->x;
	m_path[0].pos.y = start->y;
	m_path[0].pos.z = startArea->GetZ( start );
	m_path[0].ladder = NULL;
	m_path[0].how = NUM_TRAVERSE_TYPES;

	m_path[1].area = goalArea;
	m_path[1].pos.x = goal->x;
	m_path[1].pos.y = goal->y;
	m_path[1].pos.z = goalArea->GetZ( goal );
	m_path[1].ladder = NULL;
	m_path[1].how = NUM_TRAVERSE_TYPES;

	return true;
}
示例#4
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int GetActiveNavMesh()
{
	CNavArea *pArea = TheNavMesh->GetSelectedArea();
	if( !pArea )
		return -1;

	return pArea->GetID();
}
示例#5
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int GetNavAreaAt( const Vector &pos, float beneathlimit )
{
	CNavArea *pArea = TheNavMesh->GetNavArea( pos, beneathlimit );
	if( !pArea )
		return -1;

	return pArea->GetID();
}
示例#6
0
/* <4f169d> ../game_shared/bot/nav_file.cpp:811 */
void LoadLocationFile(const char *filename)
{
	char locFilename[256];
	Q_strcpy(locFilename, filename);

	char *dot = Q_strchr(locFilename, '.');
	if (dot)
	{
		Q_strcpy(dot, ".loc");

		int locDataLength;
		char *locDataFile = (char *)LOAD_FILE_FOR_ME(const_cast<char *>(locFilename), &locDataLength);
		char *locData = locDataFile;

		if (locData)
		{
			CONSOLE_ECHO("Loading legacy 'location file' '%s'\n", locFilename);

			// read directory
			locData = MP_COM_Parse(locData);
			int dirSize = Q_atoi(MP_COM_GetToken());

			if (dirSize)
			{
				std::vector<unsigned int> directory;
				directory.reserve(dirSize);

				for (int i = 0; i < dirSize; ++i)
				{
					locData = MP_COM_Parse(locData);
					directory.push_back(TheBotPhrases->NameToID(MP_COM_GetToken()));
				}

				// read places for each nav area
				unsigned int areaID, locDirIndex;
				while (true)
				{
					locData = MP_COM_Parse(locData);
					if (locData == NULL)
						break;

					areaID = Q_atoi(MP_COM_GetToken());

					locData = MP_COM_Parse(locData);
					locDirIndex = Q_atoi(MP_COM_GetToken());

					CNavArea *area = TheNavAreaGrid.GetNavAreaByID(areaID);
					unsigned int place = (locDirIndex > 0) ? directory[locDirIndex - 1] : UNDEFINED_PLACE;

					if (area)
						area->SetPlace(place);
				}
			}

			FREE_FILE(locDataFile);
		}
	}
}
示例#7
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Vector NavMeshGetPositionNearestNavArea( const Vector &pos, float beneathlimit, bool bCheckBlocked )
{
	CNavArea *pArea;
	//pArea = TheNavMesh->GetNearestNavArea(pos, false, 64.0f);
	pArea = TheNavMesh->GetNavArea(pos, beneathlimit, bCheckBlocked);
	if( pArea ) {
		Vector vAreaPos(pos);
		vAreaPos.z = pArea->GetZ(pos);
		return vAreaPos;
	}
	return vec3_origin;
}
示例#8
0
//--------------------------------------------------------------------------------------------------------
void CFuncNavBlocker::UpdateBlocked()
{
	NavAreaCollector collector( true );
	Extent extent;
	extent.Init( this );
	TheNavMesh->ForAllAreasOverlappingExtent( collector, extent );

	for ( int i=0; i<collector.m_area.Count(); ++i )
	{
		CNavArea *area = collector.m_area[i];
		area->UpdateBlocked( true );
	}

}
示例#9
0
// If we are not on the navigation mesh (m_currentArea == nullptr),
// move towards last known area.
// Return false if off mesh.
bool CCSBot::StayOnNavMesh()
{
	if (m_currentArea)
		return true;

	// move back onto the area map

	// if we have no lastKnownArea, we probably started off
	// of the nav mesh - find the closest nav area and use it
	CNavArea *goalArea;
	if (!m_currentArea && !m_lastKnownArea)
	{
		goalArea = TheNavAreaGrid.GetNearestNavArea(&pev->origin);
		PrintIfWatched("Started off the nav mesh - moving to closest nav area...\n");
	}
	else
	{
		goalArea = m_lastKnownArea;
		PrintIfWatched("Getting out of NULL area...\n");
	}

	if (goalArea)
	{
		Vector pos;
		goalArea->GetClosestPointOnArea(&pev->origin, &pos);

		// move point into area
		Vector to = pos - pev->origin;
		to.NormalizeInPlace();

		// how far to "step into" an area - must be less than min area size
		const float stepInDist = 5.0f;
		pos = pos + (stepInDist * to);

		MoveTowardsPosition(&pos);
	}

	// if we're stuck, try to get un-stuck
	// do stuck movements last, so they override normal movement
	if (m_isStuck)
	{
		Wiggle();
	}

	return false;
}
示例#10
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Vector RandomNavAreaPositionWithin( const Vector &mins, const Vector &maxs, float minimumarea, int maxtries )
{
	if( maxtries < 0 )
		maxtries = 1;

	Vector random;
	CNavArea *pArea = NULL;
	Extent extent;

	for( int i = 0; i < maxtries; i++ )
	{
		random.Init( 
			mins.x + ((float)rand() / RAND_MAX) * (maxs.x - mins.x),
			mins.y + ((float)rand() / RAND_MAX) * (maxs.y - mins.y),
			maxs.z
		);
		pArea = TheNavMesh->GetNearestNavArea( random, false, 10000.0f, false, false );
		if( pArea )
		{
			pArea->GetExtent( &extent );
			if( extent.Area() >= minimumarea )
				break;
		}

		// Reset
		pArea = NULL;
	}


	if( !pArea )
	{
		if( g_pynavmesh_debug.GetBool() )
			DevMsg("RandomNavAreaPosition: No area found within Mins: %f %f %f, Maxs: %f %f %f, Random: %f %f %f\n", 
					mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z, random.x, random.y, random.z);
		return vec3_origin;
	}
	Vector vRandomPoint = pArea->GetRandomPoint();
	vRandomPoint.z += 32.0f;

	if( g_pynavmesh_debug.GetBool() )
		DevMsg("RandomNavAreaPosition: Found position %f %f %f\n", vRandomPoint.x, vRandomPoint.y, vRandomPoint.z);

	return vRandomPoint;
}
示例#11
0
//-----------------------------------------------------------------------------------------------------
int CFuncNavBlocker::DrawDebugTextOverlays( void )
{
	int offset = BaseClass::DrawDebugTextOverlays();

	if (m_debugOverlays & OVERLAY_TEXT_BIT) 
	{
		CFmtStr str;

		// FIRST_GAME_TEAM skips TEAM_SPECTATOR and TEAM_UNASSIGNED, so we can print
		// useful team names in a non-game-specific fashion.
		for ( int i=FIRST_GAME_TEAM; i<FIRST_GAME_TEAM + MAX_NAV_TEAMS; ++i )
		{
			if ( IsBlockingNav( i ) )
			{
				CTeam *team = GetGlobalTeam( i );
				if ( team )
				{
					EntityText( offset++, str.sprintf( "blocking team %s", team->GetName() ), 0 );
				}
				else
				{
					EntityText( offset++, str.sprintf( "blocking team %d", i ), 0 );
				}
			}
		}

		NavAreaCollector collector( true );
		Extent extent;
		extent.Init( this );
		TheNavMesh->ForAllAreasOverlappingExtent( collector, extent );

		for ( int i=0; i<collector.m_area.Count(); ++i )
		{
			CNavArea *area = collector.m_area[i];
			Extent areaExtent;
			area->GetExtent( &areaExtent );
			debugoverlay->AddBoxOverlay( vec3_origin, areaExtent.lo, areaExtent.hi, vec3_angle, 0, 255, 0, 10, NDEBUG_PERSIST_TILL_NEXT_SERVER );
		}
	}

	return offset;
}
示例#12
0
void CEventLog::FormatPlayer( CBaseEntity *ent, char *str, int len ) const
{
	if ( !str || len <= 0 )
	{
		return;
	}

	CBasePlayer *player = ToBasePlayer( ent );

	const char *playerName = "Unknown";
	int userID = 0;
	const char *networkIDString = "";
	const char *teamName = "";
	int areaID = 0;
	if ( player )
	{
		playerName = player->GetPlayerName();
		userID = player->GetUserID();
		networkIDString = player->GetNetworkIDString();
		CTeam *team = player->GetTeam();
		if ( team )
		{
			teamName = team->GetName();
		}
	}

#ifdef USE_NAV_MESH
	if ( ent && ent->MyCombatCharacterPointer() )
	{
		CNavArea *area = ent->MyCombatCharacterPointer()->GetLastKnownArea();
		if ( area )
		{
			areaID = area->GetID();
		}
	}
#endif // USE_NAV_MESH

	V_snprintf( str, len, "\"%s<%i><%s><%s><Area %d>\"", playerName, userID, networkIDString, teamName, areaID );
}
示例#13
0
	// tell the other areas we are going away
	FOR_EACH_VEC( TheNavAreas, it )
	{
		CNavArea *area = TheNavAreas[ it ];

		area->OnDestroyNotify( this );
	}
示例#14
0
/* <4f19c7> ../game_shared/bot/nav_file.cpp:947 */
NavErrorType LoadNavigationMap()
{
	// since the navigation map is destroyed on map change,
	// if it exists it has already been loaded for this map
	if (!TheNavAreaList.empty())
		return NAV_OK;

	// nav filename is derived from map filename
	char filename[256];
	Q_sprintf(filename, "maps\\%s.nav", STRING(gpGlobals->mapname));

	// free previous navigation map data
	DestroyNavigationMap();
	placeDirectory.Reset();

	IMPL_CLASS(CNavArea, m_nextID) = 1;

	SteamFile navFile(filename);

	if (!navFile.IsValid())
		return NAV_CANT_ACCESS_FILE;

	// check magic number
	bool result;
	unsigned int magic;
	result = navFile.Read(&magic, sizeof(unsigned int));
	if (!result || magic != NAV_MAGIC_NUMBER)
	{
		CONSOLE_ECHO("ERROR: Invalid navigation file '%s'.\n", filename);
		return NAV_INVALID_FILE;
	}

	// read file version number
	unsigned int version;
	result = navFile.Read(&version, sizeof(unsigned int));
	if (!result || version > 5)
	{
		CONSOLE_ECHO("ERROR: Unknown navigation file version.\n");
		return NAV_BAD_FILE_VERSION;
	}

	if (version >= 4)
	{
		// get size of source bsp file and verify that the bsp hasn't changed
		unsigned int saveBspSize;
		navFile.Read(&saveBspSize, sizeof(unsigned int));

		// verify size
		char *bspFilename = GetBspFilename(filename);
		if (bspFilename == NULL)
			return NAV_INVALID_FILE;

		unsigned int bspSize = (unsigned int)GET_FILE_SIZE(bspFilename);

		if (bspSize != saveBspSize)
		{
			// this nav file is out of date for this bsp file
			char *msg = "*** WARNING ***\nThe AI navigation data is from a different version of this map.\nThe CPU players will likely not perform well.\n";
			HintMessageToAllPlayers(msg);
			CONSOLE_ECHO("\n-----------------\n");
			CONSOLE_ECHO(msg);
			CONSOLE_ECHO("-----------------\n\n");
		}
	}

	// load Place directory
	if (version >= 5)
	{
		placeDirectory.Load(&navFile);
	}

	// get number of areas
	unsigned int count;
	result = navFile.Read(&count, sizeof(unsigned int));

	Extent extent;
	extent.lo.x = 9999999999.9f;
	extent.lo.y = 9999999999.9f;
	extent.hi.x = -9999999999.9f;
	extent.hi.y = -9999999999.9f;

	// load the areas and compute total extent
	for (unsigned int i = 0; i < count; ++i)
	{
		CNavArea *area = new CNavArea;
		area->Load(&navFile, version);
		TheNavAreaList.push_back(area);

		const Extent *areaExtent = area->GetExtent();

		// check validity of nav area
		if (areaExtent->lo.x >= areaExtent->hi.x || areaExtent->lo.y >= areaExtent->hi.y)
			CONSOLE_ECHO("WARNING: Degenerate Navigation Area #%d at ( %g, %g, %g )\n",
				area->GetID(), area->m_center.x, area->m_center.y, area->m_center.z);

		if (areaExtent->lo.x < extent.lo.x)
			extent.lo.x = areaExtent->lo.x;

		if (areaExtent->lo.y < extent.lo.y)
			extent.lo.y = areaExtent->lo.y;

		if (areaExtent->hi.x > extent.hi.x)
			extent.hi.x = areaExtent->hi.x;

		if (areaExtent->hi.y > extent.hi.y)
			extent.hi.y = areaExtent->hi.y;
	}

	// add the areas to the grid
	TheNavAreaGrid.Initialize(extent.lo.x, extent.hi.x, extent.lo.y, extent.hi.y);

	NavAreaList::iterator iter;
	for (iter = TheNavAreaList.begin(); iter != TheNavAreaList.end(); ++iter)
		TheNavAreaGrid.AddNavArea(*iter);

	// allow areas to connect to each other, etc
	for (iter = TheNavAreaList.begin(); iter != TheNavAreaList.end(); ++iter)
	{
		CNavArea *area = *iter;
		area->PostLoad();
	}

	// load legacy location file (Places)
	if (version < 5)
	{
		LoadLocationFile(filename);
	}

	// Set up all the ladders
	BuildLadders();

	return NAV_OK;
}
示例#15
0
/* <4f3e47> ../game_shared/bot/nav_file.cpp:702 */
bool SaveNavigationMap(const char *filename)
{
	if (filename == NULL)
		return false;

	// Store the NAV file
	COM_FixSlashes(const_cast<char *>(filename));

#ifdef WIN32
	int fd = _open(filename, _O_BINARY | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE);
#else
	int fd = creat(filename, S_IRUSR | S_IWUSR | S_IRGRP);
#endif // WIN32

	if (fd < 0)
		return false;

	// store "magic number" to help identify this kind of file
	unsigned int magic = NAV_MAGIC_NUMBER;
	Q_write(fd, &magic, sizeof(unsigned int));

	// store version number of file
	// 1 = hiding spots as plain vector array
	// 2 = hiding spots as HidingSpot objects
	// 3 = Encounter spots use HidingSpot ID's instead of storing vector again
	// 4 = Includes size of source bsp file to verify nav data correlation
	// ---- Beta Release at V4 -----
	// 5 = Added Place info
	unsigned int version = 5;
	Q_write(fd, &version, sizeof(unsigned int));

	// get size of source bsp file and store it in the nav file
	// so we can test if the bsp changed since the nav file was made
	char *bspFilename = GetBspFilename(filename);
	if (bspFilename == NULL)
		return false;

	unsigned int bspSize = (unsigned int)GET_FILE_SIZE(bspFilename);
	CONSOLE_ECHO("Size of bsp file '%s' is %u bytes.\n", bspFilename, bspSize);

	Q_write(fd, &bspSize, sizeof(unsigned int));

	// Build a directory of the Places in this map
	placeDirectory.Reset();

	NavAreaList::iterator it;
	for (it = TheNavAreaList.begin(); it != TheNavAreaList.end(); ++it)
	{
		CNavArea *area = *it;
		Place place = area->GetPlace();

		if (place)
		{
			placeDirectory.AddPlace(place);
		}
	}

	placeDirectory.Save(fd);

	// Store navigation areas
	// store number of areas
	unsigned int count = TheNavAreaList.size();
	Q_write(fd, &count, sizeof(unsigned int));

	// store each area
	for (it = TheNavAreaList.begin(); it != TheNavAreaList.end(); ++it)
	{
		CNavArea *area = *it;
		area->Save(fd, version);
	}

	Q_close(fd);

#ifdef _WIN32
	// output a simple Wavefront file to visualize the generated areas in 3DSMax
	FILE *fp = fopen("c:\\tmp\\nav.obj", "w");
	if (fp)
	{
		for (NavAreaList::iterator iter = TheNavAreaList.begin(); iter != TheNavAreaList.end(); ++iter)
		{
			(*iter)->Save(fp);
		}

		fclose(fp);
	}
#endif // _WIN32

	return true;
}
		virtual void FrameUpdatePostEntityThink() override
		{
			static long frame = 0;
			++frame;
			
			if (frame % 22 == 0) {
				Enhancement_NotifyDefendersAboutBombs();
			}
			
			UpdateTrackers();
			
#if 0
			/* remove me */
			if (frame % 7 == 0) {
				CCaptureFlag *flag = GetClosestFlagToHatch();
				if (flag != nullptr) {
					CNavArea *area = TheNavMesh->GetNavArea(flag->GetAbsOrigin());
					if (area != nullptr) {
						float flag_dist = reinterpret_cast<CTFNavArea *>(area)->GetIncursionDistance(TF_TEAM_BLUE);
						
						constexpr float D_LOW  =   0.0f;
						constexpr float D_HIGH = 300.0f;
						
						for (auto area : (CUtlVector<CTFNavArea *>&)TheNavAreas) {
							float dist = area->GetIncursionDistance(TF_TEAM_BLUE);
							
							if (dist < flag_dist - D_LOW)  continue;
							if (dist > flag_dist + D_HIGH) continue;
							
							area->DrawFilled(0xff, 0xff, 0xff, 0x00, gpGlobals->interval_per_tick * 7, true, 0.0f);
							area->DrawFilled(0xff, 0xff, 0xff, 0x40, gpGlobals->interval_per_tick * 7, true, 0.0f);
						}
					}
				}
			}
#endif
			
#if 0
			if (frame % 22 == 0) {
				float inc_min = FLT_MAX;
				float inc_max = FLT_MIN;
				
				for (auto area : (CUtlVector<CTFNavArea *>&)TheNavAreas) {
					float inc = area->GetIncursionDistance(TF_TEAM_BLUE);
					if (inc < 0.0f) continue;
					
					if (inc < inc_min) inc_min = inc;
					if (inc > inc_max) inc_max = inc;
				}
				
				for (auto area : (CUtlVector<CTFNavArea *>&)TheNavAreas) {
					float val = area->GetIncursionDistance(TF_TEAM_BLUE);
					float rat = RemapValClamped(val, 0.0f, inc_max, 0.0f, 1.0f);
					
					int r;
					int g;
					int b;
					
					if (rat < 0.25f) {
						r = 0xff;
						g = RemapValClamped(rat, 0.00f, 0.25f, 0.0f, 255.0f);
						b = 0x00;
					} else if (rat < 0.50f) {
						r = RemapValClamped(rat, 0.25f, 0.50f, 255.0f, 0.0f);
						g = 0xff;
						b = 0x00;
					} else if (rat < 0.75f) {
						r = 0x00;
						g = 0xff;
						b = RemapValClamped(rat, 0.50f, 0.75f, 0.0f, 255.0f);
					} else {
						r = 0x00;
						g = RemapValClamped(rat, 0.75f, 1.00f, 255.0f, 0.0f);
						b = 0xff;
					}
					
					area->DrawFilled(r, g, b, 0x00, 0.33f, true, 0.0f);
					area->DrawFilled(r, g, b, 0x80, 0.33f, true, 0.0f);
					
					NDebugOverlay::EntityTextAtPosition(area->GetCenter(), 0,
						CFmtStrN<64>("%.1f%%", rat * 100.0f),
						0.33f, 0xff, 0xff, 0xff, 0xff);
				//	NDebugOverlay::EntityTextAtPosition(area->GetCenter(), 1,
				//		CFmtStrN<64>("%.0f HU", val),
				//		0.33f, 0xff, 0xff, 0xff, 0xff);
				}
			}
#endif
			
			DrawOverlay_Ownership(frame);
		}
示例#17
0
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCSPlayerResource::UpdatePlayerData( void )
{
	int i;

	m_iPlayerC4 = 0;
	m_iPlayerVIP = 0;

	for ( i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
		
		if ( pPlayer && pPlayer->IsConnected() )
		{
			if ( pPlayer->IsVIP() )
			{
				// we should only have one VIP
				Assert( m_iPlayerVIP == 0 );
				m_iPlayerVIP = i;
			}

			if ( pPlayer->HasC4() )
			{
				// we should only have one bomb
				m_iPlayerC4 = i;
			}
		}
	}

	CBaseEntity *c4 = NULL;
	if ( m_iPlayerC4 == 0 )
	{
		// no player has C4, update C4 position
		if ( g_C4s.Count() > 0 )
		{
			c4 = g_C4s[0];
			m_vecC4 = c4->GetAbsOrigin();
		}
		else
		{
			m_vecC4.Init();
		}
	}

	//int numHostages = g_Hostages.Count();

	for ( i = 0; i < MAX_HOSTAGES; i++ )
	{
		/*if ( i >= numHostages )
		{
//			engine->Con_NPrintf( i, "Dead" );
			m_bHostageAlive.Set( i, false );
			m_isHostageFollowingSomeone.Set( i, false );
			continue;
		}

//		CHostage* pHostage = g_Hostages[i];

		//m_bHostageAlive.Set( i, pHostage->IsRescuable() );

		/*if ( pHostage->IsValid() )
		{
			m_iHostageX.Set( i, (int) pHostage->GetAbsOrigin().x );	
			m_iHostageY.Set( i, (int) pHostage->GetAbsOrigin().y );	
			m_iHostageZ.Set( i, (int) pHostage->GetAbsOrigin().z );	
			m_iHostageEntityIDs.Set( i, pHostage->entindex() );
			//m_isHostageFollowingSomeone.Set( i, pHostage->IsFollowingSomeone() );
//			engine->Con_NPrintf( i, "ID:%d Pos:(%.0f,%.0f,%.0f)", pHostage->entindex(), pHostage->GetAbsOrigin().x, pHostage->GetAbsOrigin().y, pHostage->GetAbsOrigin().z );
		}
		else
		{
//			engine->Con_NPrintf( i, "Invalid" );
		}*/
	}

	if( !m_foundGoalPositions )
	{
		// We only need to update these once a map, but we need the client to know about them.
		CBaseEntity* ent = NULL;
		while ( ( ent = gEntList.FindEntityByClassname( ent, "func_bomb_target" ) ) != NULL )
		{
			const Vector &pos = ent->WorldSpaceCenter();
			CNavArea *area = TheNavMesh->GetNearestNavArea( pos, true );
			const char *placeName = (area) ? TheNavMesh->PlaceToName( area->GetPlace() ) : NULL;
			if ( placeName == NULL )
			{
				// The bomb site has no area or place name, so just choose A then B
				if ( m_bombsiteCenterA.Get().IsZero() )
				{
					m_bombsiteCenterA = pos;
				}
				else
				{
					m_bombsiteCenterB = pos;
				}
			}
			else
			{
				// The bomb site has a place name, so choose accordingly
				if( FStrEq( placeName, "BombsiteA" ) )
				{
					m_bombsiteCenterA = pos;
				}
				else
				{
					m_bombsiteCenterB = pos;
				}
			}
			m_foundGoalPositions = true;
		}

		int hostageRescue = 0;
		while ( (( ent = gEntList.FindEntityByClassname( ent, "func_hostage_rescue" ) ) != NULL)  &&  (hostageRescue < MAX_HOSTAGE_RESCUES) )
		{
			const Vector &pos = ent->WorldSpaceCenter();
			m_hostageRescueX.Set( hostageRescue, (int) pos.x );	
			m_hostageRescueY.Set( hostageRescue, (int) pos.y );	
			m_hostageRescueZ.Set( hostageRescue, (int) pos.z );	

			hostageRescue++;
			m_foundGoalPositions = true;
		}
	}

	bool bombSpotted = false;
	if ( c4 )
	{
		Spotter spotter( c4, m_vecC4, TEAM_CT );
		ForEachPlayer( spotter );
		if ( spotter.Spotted() )
		{
			bombSpotted = true;
		}
	}

	for ( int i=0; i < MAX_PLAYERS+1; i++ )
	{
		CCSPlayer *target = ToCSPlayer( UTIL_PlayerByIndex( i ) );
		if ( !target || !target->IsAlive() )
		{
			m_bPlayerSpotted.Set( i, 0 );
			continue;
		}

		Spotter spotter( target, target->EyePosition(), (target->GetTeamNumber()==TEAM_CT) ? TEAM_TERRORIST : TEAM_CT );
		ForEachPlayer( spotter );
		if ( spotter.Spotted() )
		{
			if ( target->HasC4() )
			{
				bombSpotted = true;
			}
			m_bPlayerSpotted.Set( i, 1 );
		}
		else
		{
			m_bPlayerSpotted.Set( i, 0 );
		}
	}

	if ( bombSpotted )
	{
		m_bBombSpotted = true;
	}
	else
	{
		m_bBombSpotted = false;
	}

	BaseClass::UpdatePlayerData();
}
示例#18
0
/* <36b780> ../cstrike/dlls/bot/cs_bot_manager.cpp:1109 */
void CCSBotManager::ValidateMapData(void)
{
	if (IMPLEMENT_ARRAY(m_isMapDataLoaded) || !UTIL_IsGame("czero"))
	{
		return;
	}

	IMPLEMENT_ARRAY(m_isMapDataLoaded) = true;

	// TODO: Reverse me
	if (LoadNavigationMap())
	{
		CONSOLE_ECHO("Failed to load navigation map.\n");
		return;
	}

	CONSOLE_ECHO("Navigation map loaded.\n");

	m_zoneCount = 0;
	m_gameScenario = SCENARIO_DEATHMATCH;

	// Search all entities in the map and set the game type and
	// store all zones (bomb target, etc).

	CBaseEntity *entity = NULL;
	int i;

	for (i = 1; i < gpGlobals->maxEntities; i++)
	{
		entity = CBaseEntity::Instance(INDEXENT(i));

		if (entity == NULL)
			continue;

		bool found = false;
		bool isLegacy = false;

		if (FClassnameIs(entity->pev, "func_bomb_target"))
		{
			found = true;
			isLegacy = false;

			m_gameScenario = SCENARIO_DEFUSE_BOMB;
		}
		else if (FClassnameIs(entity->pev, "info_bomb_target"))
		{
			found = true;
			isLegacy = true;

			m_gameScenario = SCENARIO_DEFUSE_BOMB;
		}
		else if (FClassnameIs(entity->pev, "func_hostage_rescue"))
		{
			found = true;
			isLegacy = false;

			m_gameScenario = SCENARIO_RESCUE_HOSTAGES;
		}
		else if (FClassnameIs(entity->pev, "info_hostage_rescue"))
		{
			found = true;
			isLegacy = true;

			m_gameScenario = SCENARIO_RESCUE_HOSTAGES;
		}
		else if (FClassnameIs(entity->pev, "hostage_entity"))
		{
			// some very old maps (ie: cs_assault) use info_player_start
			// as rescue zones, so set the scenario if there are hostages
			// in the map
			m_gameScenario = SCENARIO_RESCUE_HOSTAGES;
		}
		else if (FClassnameIs(entity->pev, "func_vip_safetyzone"))
		{
			found = true;
			isLegacy = false;

			m_gameScenario = SCENARIO_ESCORT_VIP;
		}

		if (found)
		{
			if (m_zoneCount < MAX_ZONES)
			{
				if (isLegacy)
					m_zone[ m_zoneCount ].m_center = entity->pev->origin;
				else
					m_zone[ m_zoneCount ].m_center = (entity->pev->absmax + entity->pev->absmin) / 2.0f;

				m_zone[ m_zoneCount ].m_isLegacy = isLegacy;
				m_zone[ m_zoneCount ].m_index = m_zoneCount;
				m_zone[ m_zoneCount ].m_entity = entity;

				++m_zoneCount;
			}
			else
				CONSOLE_ECHO("Warning: Too many zones, some will be ignored.\n");
		}
	}

	// If there are no zones and the scenario is hostage rescue,
	// use the info_player_start entities as rescue zones.
	if (m_zoneCount == 0 && m_gameScenario == SCENARIO_RESCUE_HOSTAGES)
	{
		entity = NULL;

		while ((entity = UTIL_FindEntityByClassname(entity, "info_player_start")) != NULL)
		{
			if (FNullEnt(entity->edict()))
				break;

			if (m_zoneCount < MAX_ZONES)
			{
				m_zone[ m_zoneCount ].m_center = entity->pev->origin;
				m_zone[ m_zoneCount ].m_isLegacy = true;
				m_zone[ m_zoneCount ].m_index = m_zoneCount;
				m_zone[ m_zoneCount ].m_entity = entity;

				++m_zoneCount;
			}
			else
				CONSOLE_ECHO("Warning: Too many zones, some will be ignored.\n");
		}
	}

	// Collect nav areas that overlap each zone
	for (i = 0; i < m_zoneCount; i++)
	{
		Zone *zone = &m_zone[i];

		if (zone->m_isLegacy)
		{
			const float legacyRange = 256.0f;

			zone->m_extent.lo.x = zone->m_center.x - legacyRange;
			zone->m_extent.lo.y = zone->m_center.y - legacyRange;
			zone->m_extent.lo.z = zone->m_center.z - legacyRange;

			zone->m_extent.hi.x = zone->m_center.x + legacyRange;
			zone->m_extent.hi.y = zone->m_center.y + legacyRange;
			zone->m_extent.hi.z = zone->m_center.z + legacyRange;
		}
		else
		{
			zone->m_extent.lo = zone->m_entity->pev->absmin;
			zone->m_extent.hi = zone->m_entity->pev->absmax;
		}

		// ensure Z overlap
		const float zFudge = 50.0f;

		zone->m_areaCount = 0;
		zone->m_extent.lo.z -= zFudge;
		zone->m_extent.hi.z += zFudge;

		// build a list of nav areas that overlap this zone
		for (NavAreaList::iterator iter = TheNavAreaList.begin(); iter != TheNavAreaList.end(); ++iter)
		{
			CNavArea *area = (*iter);
			const Extent *areaExtent = area->GetExtent();

			if (areaExtent->hi.x >= zone->m_extent.lo.x && areaExtent->lo.x <= zone->m_extent.hi.x
				&& areaExtent->hi.y >= zone->m_extent.lo.y && areaExtent->lo.y <= zone->m_extent.hi.y
				&& areaExtent->hi.z >= zone->m_extent.lo.z && areaExtent->lo.z <= zone->m_extent.hi.z)
			{
				// area overlaps zone
				zone->m_area[ zone->m_areaCount++ ] = area;

				if (zone->m_areaCount == MAX_ZONE_NAV_AREAS)
				{
					break;
				}
			}
		}
	}
}
示例#19
0
//--------------------------------------------------------------------------------------------------------
static bool ReduceToComponentAreas( CNavArea *area, bool addToSelectedSet )
{
	if ( !area )
		return false;

	bool splitAlongX;
	float splitEdge;

	const float minSplitSize = 2.0f; // ensure the first split is larger than this

	float sizeX = area->GetSizeX();
	float sizeY = area->GetSizeY();

	CNavArea *first = NULL;
	CNavArea *second = NULL;
	CNavArea *third = NULL;
	CNavArea *fourth = NULL;

	bool didSplit = false;

	if ( sizeX > GenerationStepSize )
	{
		splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).x, GenerationStepSize );
		if ( splitEdge < area->GetCorner( NORTH_WEST ).x + minSplitSize )
			splitEdge += GenerationStepSize;
		splitAlongX = false;

		didSplit = area->SplitEdit( splitAlongX, splitEdge, &first, &second );
	}

	if ( sizeY > GenerationStepSize )
	{
		splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).y, GenerationStepSize );
		if ( splitEdge < area->GetCorner( NORTH_WEST ).y + minSplitSize )
			splitEdge += GenerationStepSize;
		splitAlongX = true;

		if ( didSplit )
		{
			didSplit = first->SplitEdit( splitAlongX, splitEdge, &third, &fourth );
			didSplit = second->SplitEdit( splitAlongX, splitEdge, &first, &second );
		}
		else
		{
			didSplit = area->SplitEdit( splitAlongX, splitEdge, &first, &second );
		}
	}

	if ( !didSplit )
		return false;

	if ( addToSelectedSet )
	{
		TheNavMesh->AddToSelectedSet( first );
		TheNavMesh->AddToSelectedSet( second );
		TheNavMesh->AddToSelectedSet( third );
		TheNavMesh->AddToSelectedSet( fourth );
	}

	ReduceToComponentAreas( first, addToSelectedSet );
	ReduceToComponentAreas( second, addToSelectedSet );
	ReduceToComponentAreas( third, addToSelectedSet );
	ReduceToComponentAreas( fourth, addToSelectedSet );

	return true;
}
示例#20
0
/**
 * Follow our leader
 * @todo Clean up this nasty mess
 */
void FollowState::OnUpdate( CDABot *me )
{
	// if we lost our leader, give up
	if (m_leader == NULL || !m_leader->IsAlive())
	{
		me->Idle();
		return;
	}

	// look around
	me->UpdateLookAround();

	// if we are moving, we are not idle
	if (me->IsNotMoving() == false)
		m_idleTimer.Start( RandomFloat( 2.0f, 5.0f ) );

	// compute the leader's speed
	Vector leaderVel = m_leader->GetAbsVelocity();
	float leaderSpeed = Vector2D( leaderVel.x, leaderVel.y ).Length();

	// determine our leader's movement state
	ComputeLeaderMotionState( leaderSpeed );

	// track whether we can see the leader
	bool isLeaderVisible;
	Vector leaderOrigin = GetCentroid( m_leader );
	if (me->IsVisible( leaderOrigin ))
	{
		m_lastSawLeaderTime = gpGlobals->curtime;
		isLeaderVisible = true;
	}
	else
	{
		isLeaderVisible = false;
	}


	// determine whether we should sneak or not
	const float farAwayRange = 750.0f;
	Vector myOrigin = GetCentroid( me );
	if ((leaderOrigin - myOrigin).IsLengthGreaterThan( farAwayRange ))
	{
		// far away from leader - run to catch up
		m_isSneaking = false;
	}
	else if (isLeaderVisible)
	{
		// if we see leader walking and we are nearby, walk
		if (m_leaderMotionState == WALKING)
			m_isSneaking = true;

		// if we are sneaking and our leader starts running, stop sneaking
		if (m_isSneaking && m_leaderMotionState == RUNNING)
			m_isSneaking = false;
	}

	// if we haven't seen the leader for a long time, run
	const float longTime = 20.0f;
	if (gpGlobals->curtime - m_lastSawLeaderTime > longTime)
		m_isSneaking = false;

	if (m_isSneaking)
		me->Walk();
	else
		me->Run();


	bool repath = false;

	// if the leader has stopped, hide nearby
	const float nearLeaderRange = 250.0f;
	if (!me->HasPath() && m_leaderMotionState == STOPPED && m_leaderMotionStateTime.GetElapsedTime() > m_waitTime)
	{
		// throttle how often this check occurs
		m_waitTime += RandomFloat( 1.0f, 3.0f );

		// the leader has stopped - if we are close to him, take up a hiding spot
		if ((leaderOrigin - myOrigin).IsLengthLessThan( nearLeaderRange ))
		{
			const float hideRange = 250.0f;
			if (me->TryToHide( NULL, -1.0f, hideRange, false, USE_NEAREST ))
			{
				me->ResetStuckMonitor();
				return;
			}
		}
	}

	// if we have been idle for awhile, move
	if (m_idleTimer.IsElapsed())
	{
		repath = true;

		// always walk when we move such a short distance
		m_isSneaking = true;
	}

	// if our leader has moved, repath (don't repath if leading is stopping)
	if (leaderSpeed > 100.0f && m_leaderMotionState != STOPPED)
	{
		repath = true;
	}

	// move along our path
	if (me->UpdatePathMovement( NO_SPEED_CHANGE ) != CDABot::PROGRESSING)
	{
		me->DestroyPath();
	}

	// recompute our path if necessary	
	if (repath && m_repathInterval.IsElapsed() && !me->IsOnLadder())
	{
		// recompute our path to keep us near our leader
		m_lastLeaderPos = leaderOrigin;

		me->ResetStuckMonitor();

		const float runSpeed = 200.0f;

		const float collectRange = (leaderSpeed > runSpeed) ? 600.0f : 400.0f;		// 400, 200
		FollowTargetCollector collector( m_leader );
		SearchSurroundingAreas( TheNavMesh->GetNearestNavArea( m_lastLeaderPos ), m_lastLeaderPos, collector, collectRange );

		if (cv_bot_debug.GetBool())
		{
			for( int i=0; i<collector.m_targetAreaCount; ++i )
				collector.m_targetArea[i]->Draw( /*255, 0, 0, 2*/ );		
		}

		// move to one of the collected areas
		if (collector.m_targetAreaCount)
		{
			CNavArea *target = NULL;
			Vector targetPos;

			// if we are idle, pick a random area
			if (m_idleTimer.IsElapsed())
			{
				target = collector.m_targetArea[ RandomInt( 0, collector.m_targetAreaCount-1 ) ];				
				targetPos = target->GetCenter();
				me->PrintIfWatched( "%4.1f: Bored. Repathing to a new nearby area\n", gpGlobals->curtime );
			}
			else
			{
				me->PrintIfWatched( "%4.1f: Repathing to stay with leader.\n", gpGlobals->curtime );

				// find closest area to where we are
				CNavArea *area;
				float closeRangeSq = 9999999999.9f;
				Vector close;

				for( int a=0; a<collector.m_targetAreaCount; ++a )
				{
					area = collector.m_targetArea[a];

					area->GetClosestPointOnArea( myOrigin, &close );

					float rangeSq = (myOrigin - close).LengthSqr();
					if (rangeSq < closeRangeSq)
					{
						target = area;
						targetPos = close;
						closeRangeSq = rangeSq;
					}
				}
			}
						
			if (target == NULL || me->ComputePath( target->GetCenter(), FASTEST_ROUTE ) == NULL)
				me->PrintIfWatched( "Pathfind to leader failed.\n" );

			// throttle how often we repath
			m_repathInterval.Start( 0.5f );

			m_idleTimer.Reset();
		}
	}
}
示例#21
0
// Compute shortest path to goal position via A* algorithm
// If 'goalArea' is NULL, path will get as close as it can.
bool CCSBot::ComputePath(CNavArea *goalArea, const Vector *goal, RouteType route)
{
	// Throttle re-pathing
	if (!m_repathTimer.IsElapsed())
		return false;

	// randomize to distribute CPU load
	m_repathTimer.Start(RANDOM_FLOAT(0.4f, 0.6f));

	DestroyPath();

	CNavArea *startArea = m_lastKnownArea;
	if (!startArea)
		return false;

	// note final specific position
	Vector pathEndPosition;

	if (!goal && !goalArea)
		return false;

	if (!goal)
		pathEndPosition = *goalArea->GetCenter();
	else
		pathEndPosition = *goal;

	// make sure path end position is on the ground
	if (goalArea)
		pathEndPosition.z = goalArea->GetZ(&pathEndPosition);
	else
		GetGroundHeight(&pathEndPosition, &pathEndPosition.z);

	// if we are already in the goal area, build trivial path
	if (startArea == goalArea)
	{
		BuildTrivialPath(&pathEndPosition);
		return true;
	}

	// Compute shortest path to goal
	CNavArea *closestArea = nullptr;
	PathCost pathCost(this, route);
	bool pathToGoalExists = NavAreaBuildPath(startArea, goalArea, goal, pathCost, &closestArea);

	CNavArea *effectiveGoalArea = (pathToGoalExists) ? goalArea : closestArea;

	// Build path by following parent links
	// get count
	int count = 0;
	CNavArea *area;
	for (area = effectiveGoalArea; area; area = area->GetParent())
	{
		count++;
	}

	// save room for endpoint
	if (count > MAX_PATH_LENGTH - 1)
		count = MAX_PATH_LENGTH - 1;

	if (count == 0)
		return false;

	if (count == 1)
	{
		BuildTrivialPath(&pathEndPosition);
		return true;
	}

	// build path
	m_pathLength = count;
	for (area = effectiveGoalArea; count && area; area = area->GetParent())
	{
		count--;
		m_path[count].area = area;
		m_path[count].how = area->GetParentHow();
	}

	// compute path positions
	if (ComputePathPositions() == false)
	{
		PrintIfWatched("Error building path\n");
		DestroyPath();
		return false;
	}

	if (!goal)
	{
		switch (m_path[m_pathLength - 1].how)
		{
		case GO_NORTH:
		case GO_SOUTH:
			pathEndPosition.x = m_path[m_pathLength - 1].pos.x;
			pathEndPosition.y = effectiveGoalArea->GetCenter()->y;
			break;

		case GO_EAST:
		case GO_WEST:
			pathEndPosition.x = effectiveGoalArea->GetCenter()->x;
			pathEndPosition.y = m_path[m_pathLength - 1].pos.y;
			break;
		}

		GetGroundHeight(&pathEndPosition, &pathEndPosition.z);
	}

	// append path end position
	m_path[m_pathLength].area = effectiveGoalArea;
	m_path[m_pathLength].pos = pathEndPosition;
	m_path[m_pathLength].ladder = nullptr;
	m_path[m_pathLength].how = NUM_TRAVERSE_TYPES;
	m_pathLength++;

	// do movement setup
	m_pathIndex = 1;
	m_areaEnteredTimestamp = gpGlobals->time;
	m_spotEncounter = nullptr;
	m_goalPosition = m_path[1].pos;

	if (m_path[1].ladder)
		SetupLadderMovement();
	else
		m_pathLadder = nullptr;

	return true;
}
示例#22
0
CBasePlayer *CCSBot::FindMostDangerousThreat()
{
	// maximum number of simulataneously attendable threats
	enum { MAX_THREATS = 16 };
	struct CloseInfo
	{
		CBasePlayer *enemy;
		float range;
	}
	threat[ MAX_THREATS ];
	int threatCount = 0;

	m_bomber = NULL;

	m_closestVisibleFriend = NULL;
	float closeFriendRange = 99999999999.9f;

	m_closestVisibleHumanFriend = NULL;
	float closeHumanFriendRange = 99999999999.9f;

	int i;

	{
		for (i = 1; i <= gpGlobals->maxClients; ++i)
		{
			CBasePlayer *player = UTIL_PlayerByIndex(i);

			if (player == NULL)
				continue;

			if (FNullEnt(player->pev))
				continue;

			// is it a player?
			if (!player->IsPlayer())
				continue;

			// ignore self
			if (player->entindex() == entindex())
				continue;

			// is it alive?
			if (!player->IsAlive())
				continue;

			// is it an enemy?
			if (player->m_iTeam == m_iTeam)
			{
				TraceResult result;
				UTIL_TraceLine(GetEyePosition(), player->pev->origin, ignore_monsters, ignore_glass, edict(), &result);
				if (result.flFraction == 1.0f)
				{
					// update watch timestamp
					int idx = player->entindex() - 1;
					m_watchInfo[idx].timestamp = gpGlobals->time;
					m_watchInfo[idx].isEnemy = false;

					// keep track of our closest friend
					Vector to = pev->origin - player->pev->origin;
					float rangeSq = to.LengthSquared();
					if (rangeSq < closeFriendRange)
					{
						m_closestVisibleFriend = player;
						closeFriendRange = rangeSq;
					}

					// keep track of our closest human friend
					if (!player->IsBot() && rangeSq < closeHumanFriendRange)
					{
						m_closestVisibleHumanFriend = player;
						closeHumanFriendRange = rangeSq;
					}
				}

				continue;
			}

			// check if this enemy is fully
			if (!IsVisible(player, CHECK_FOV))
				continue;

			// update watch timestamp
			int idx = player->entindex() - 1;
			m_watchInfo[idx].timestamp = gpGlobals->time;
			m_watchInfo[idx].isEnemy = true;

			// note if we see the bomber
			if (player->IsBombGuy())
			{
				m_bomber = player;
			}

			// keep track of all visible threats
			Vector d = pev->origin - player->pev->origin;
			float distSq = d.LengthSquared();

			// maintain set of visible threats, sorted by increasing distance
			if (threatCount == 0)
			{
				threat[0].enemy = player;
				threat[0].range = distSq;
				threatCount = 1;
			}
			else
			{
				// find insertion point
				int j;
				for (j = 0; j < threatCount; ++j)
				{
					if (distSq < threat[j].range)
						break;
				}


				// shift lower half down a notch
				for (int k = threatCount - 1; k >= j; --k)
					threat[k + 1] = threat[k];

				// insert threat into sorted list
				threat[j].enemy = player;
				threat[j].range = distSq;

				if (threatCount < MAX_THREATS)
					++threatCount;
			}
		}
	}
	{
		// track the maximum enemy and friend counts we've seen recently
		int prevEnemies = m_nearbyEnemyCount;
		int prevFriends = m_nearbyFriendCount;
		m_nearbyEnemyCount = 0;
		m_nearbyFriendCount = 0;

		for (i = 0; i < MAX_CLIENTS; ++i)
		{
			if (m_watchInfo[i].timestamp <= 0.0f)
				continue;

			const float recentTime = 3.0f;
			if (gpGlobals->time - m_watchInfo[i].timestamp < recentTime)
			{
				if (m_watchInfo[i].isEnemy)
					++m_nearbyEnemyCount;
				else
					++m_nearbyFriendCount;
			}
		}

		// note when we saw this batch of enemies
		if (prevEnemies == 0 && m_nearbyEnemyCount > 0)
		{
			m_firstSawEnemyTimestamp = gpGlobals->time;
		}

		if (prevEnemies != m_nearbyEnemyCount || prevFriends != m_nearbyFriendCount)
		{
			PrintIfWatched("Nearby friends = %d, enemies = %d\n", m_nearbyFriendCount, m_nearbyEnemyCount);
		}
	}
	{
		// Track the place where we saw most of our enemies
		struct PlaceRank
		{
			unsigned int place;
			int count;
		};
		static PlaceRank placeRank[ MAX_PLACES_PER_MAP ];
		int locCount = 0;

		PlaceRank common;
		common.place = 0;
		common.count = 0;

		for (i = 0; i < threatCount; ++i)
		{
			// find the area the player/bot is standing on
			CNavArea *area;
			CCSBot *bot = dynamic_cast<CCSBot *>(threat[i].enemy);
			if (bot != NULL && bot->IsBot())
			{
				area = bot->GetLastKnownArea();
			}
			else
			{
				area = TheNavAreaGrid.GetNearestNavArea(&threat[i].enemy->pev->origin);
			}

			if (area == NULL)
				continue;

			unsigned int threatLoc = area->GetPlace();
			if (!threatLoc)
				continue;

			// if place is already in set, increment count
			int j;
			for (j = 0; j < locCount; ++j)
			{
				if (placeRank[j].place == threatLoc)
					break;
			}

			if (j == locCount)
			{
				// new place
				if (locCount < MAX_PLACES_PER_MAP)
				{
					placeRank[ locCount ].place = threatLoc;
					placeRank[ locCount ].count = 1;

					if (common.count == 0)
						common = placeRank[locCount];

					++locCount;
				}
			}
			else
			{
				// others are in that place, increment
				++placeRank[j].count;

				// keep track of the most common place
				if (placeRank[j].count > common.count)
					common = placeRank[j];
			}
		}

		// remember most common place
		m_enemyPlace = common.place;
	}

	{
		if (threatCount == 0)
			return NULL;

		// otherwise, find the closest threat that without using shield
		int t;
		for (t = 0; t < threatCount; ++t)
		{
			if (!threat[t].enemy->IsProtectedByShield())
			{
				return threat[t].enemy;
			}
		}
	}

	// return closest threat
	return threat[0].enemy;
}