Example #1
0
void CCmpPathfinder::UpdateGrid()
{
	CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
	if (!cmpTerrain)
		return; // error

	// If the terrain was resized then delete the old grid data
	if (m_Grid && m_MapSize != cmpTerrain->GetTilesPerSide())
	{
		SAFE_DELETE(m_Grid);
		SAFE_DELETE(m_ObstructionGrid);
		m_TerrainDirty = true;
	}

	// Initialise the terrain data when first needed
	if (!m_Grid)
	{
		m_MapSize = cmpTerrain->GetTilesPerSide();
		m_Grid = new Grid<TerrainTile>(m_MapSize, m_MapSize);
		m_ObstructionGrid = new Grid<u8>(m_MapSize, m_MapSize);
	}

	CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);

	bool obstructionsDirty = cmpObstructionManager->Rasterise(*m_ObstructionGrid);

	if (obstructionsDirty && !m_TerrainDirty)
	{
		PROFILE("UpdateGrid obstructions");

		// Obstructions changed - we need to recompute passability
		// Since terrain hasn't changed we only need to update the obstruction bits
		// and can skip the rest of the data

		// TODO: if ObstructionManager::SetPassabilityCircular was called at runtime
		// (which should probably never happen, but that's not guaranteed),
		// then TILE_OUTOFBOUNDS will change and we can't use this fast path, but
		// currently it'll just set obstructionsDirty and we won't notice

		for (u16 j = 0; j < m_MapSize; ++j)
		{
			for (u16 i = 0; i < m_MapSize; ++i)
			{
				TerrainTile& t = m_Grid->get(i, j);

				u8 obstruct = m_ObstructionGrid->get(i, j);

				if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_PATHFINDING)
					t |= 1;
				else
					t &= (TerrainTile)~1;

				if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_FOUNDATION)
					t |= 2;
				else
					t &= (TerrainTile)~2;
			}
		}

		++m_Grid->m_DirtyID;
	}
	else if (obstructionsDirty || m_TerrainDirty)
	{
		PROFILE("UpdateGrid full");

		// Obstructions or terrain changed - we need to recompute passability
		// TODO: only bother recomputing the region that has actually changed

		CmpPtr<ICmpWaterManager> cmpWaterManager(GetSimContext(), SYSTEM_ENTITY);

		// TOOD: these bits should come from ICmpTerrain
		CTerrain& terrain = GetSimContext().GetTerrain();

		// avoid integer overflow in intermediate calculation
		const u16 shoreMax = 32767;
		
		// First pass - find underwater tiles
		Grid<bool> waterGrid(m_MapSize, m_MapSize);
		for (u16 j = 0; j < m_MapSize; ++j)
		{
			for (u16 i = 0; i < m_MapSize; ++i)
			{
				fixed x, z;
				TileCenter(i, j, x, z);
				
				bool underWater = cmpWaterManager && (cmpWaterManager->GetWaterLevel(x, z) > terrain.GetExactGroundLevelFixed(x, z));
				waterGrid.set(i, j, underWater);
			}
		}
		// Second pass - find shore tiles
		Grid<u16> shoreGrid(m_MapSize, m_MapSize);
		for (u16 j = 0; j < m_MapSize; ++j)
		{
			for (u16 i = 0; i < m_MapSize; ++i)
			{
				// Find a land tile
				if (!waterGrid.get(i, j))
				{
					if ((i > 0 && waterGrid.get(i-1, j)) || (i > 0 && j < m_MapSize-1 && waterGrid.get(i-1, j+1)) || (i > 0 && j > 0 && waterGrid.get(i-1, j-1))
						|| (i < m_MapSize-1 && waterGrid.get(i+1, j)) || (i < m_MapSize-1 && j < m_MapSize-1 && waterGrid.get(i+1, j+1)) || (i < m_MapSize-1 && j > 0 && waterGrid.get(i+1, j-1))
						|| (j > 0 && waterGrid.get(i, j-1)) || (j < m_MapSize-1 && waterGrid.get(i, j+1))
						)
					{	// If it's bordered by water, it's a shore tile
						shoreGrid.set(i, j, 0);
					}
					else
					{
						shoreGrid.set(i, j, shoreMax);
					}
				}
			}
		}

		// Expand influences on land to find shore distance
		for (u16 y = 0; y < m_MapSize; ++y)
		{
			u16 min = shoreMax;
			for (u16 x = 0; x < m_MapSize; ++x)
			{
				if (!waterGrid.get(x, y))
				{
					u16 g = shoreGrid.get(x, y);
					if (g > min)
						shoreGrid.set(x, y, min);
					else if (g < min)
						min = g;

					++min;
				}
			}
			for (u16 x = m_MapSize; x > 0; --x)
			{
				if (!waterGrid.get(x-1, y))
				{
					u16 g = shoreGrid.get(x-1, y);
					if (g > min)
						shoreGrid.set(x-1, y, min);
					else if (g < min)
						min = g;

					++min;
				}
			}
		}
		for (u16 x = 0; x < m_MapSize; ++x)
		{
			u16 min = shoreMax;
			for (u16 y = 0; y < m_MapSize; ++y)
			{
				if (!waterGrid.get(x, y))
				{
					u16 g = shoreGrid.get(x, y);
					if (g > min)
						shoreGrid.set(x, y, min);
					else if (g < min)
						min = g;

					++min;
				}
			}
			for (u16 y = m_MapSize; y > 0; --y)
			{
				if (!waterGrid.get(x, y-1))
				{
					u16 g = shoreGrid.get(x, y-1);
					if (g > min)
						shoreGrid.set(x, y-1, min);
					else if (g < min)
						min = g;

					++min;
				}
			}
		}

		// Apply passability classes to terrain
		for (u16 j = 0; j < m_MapSize; ++j)
		{
			for (u16 i = 0; i < m_MapSize; ++i)
			{
				fixed x, z;
				TileCenter(i, j, x, z);

				TerrainTile t = 0;

				u8 obstruct = m_ObstructionGrid->get(i, j);

				fixed height = terrain.GetExactGroundLevelFixed(x, z);

				fixed water;
				if (cmpWaterManager)
					water = cmpWaterManager->GetWaterLevel(x, z);

				fixed depth = water - height;

				fixed slope = terrain.GetSlopeFixed(i, j);

				fixed shoredist = fixed::FromInt(shoreGrid.get(i, j));

				if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_PATHFINDING)
					t |= 1;

				if (obstruct & ICmpObstructionManager::TILE_OBSTRUCTED_FOUNDATION)
					t |= 2;

				if (obstruct & ICmpObstructionManager::TILE_OUTOFBOUNDS)
				{
					// If out of bounds, nobody is allowed to pass
					for (size_t n = 0; n < m_PassClasses.size(); ++n)
						t |= m_PassClasses[n].m_Mask;
				}
				else
				{
					for (size_t n = 0; n < m_PassClasses.size(); ++n)
					{
						if (!m_PassClasses[n].IsPassable(depth, slope, shoredist))
							t |= m_PassClasses[n].m_Mask;
					}
				}

				std::string moveClass = terrain.GetMovementClass(i, j);
				if (m_TerrainCostClassTags.find(moveClass) != m_TerrainCostClassTags.end())
					t |= COST_CLASS_MASK(m_TerrainCostClassTags[moveClass]);

				m_Grid->set(i, j, t);
			}
		}

		m_TerrainDirty = false;

		++m_Grid->m_DirtyID;
	}
}
Example #2
0
Grid<u16> CCmpPathfinder::ComputeShoreGrid(bool expandOnWater)
{
	PROFILE3("ComputeShoreGrid");

	CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity());

	// TODO: these bits should come from ICmpTerrain
	CTerrain& terrain = GetSimContext().GetTerrain();

	// avoid integer overflow in intermediate calculation
	const u16 shoreMax = 32767;

	// First pass - find underwater tiles
	Grid<u8> waterGrid(m_MapSize, m_MapSize);
	for (u16 j = 0; j < m_MapSize; ++j)
	{
		for (u16 i = 0; i < m_MapSize; ++i)
		{
			fixed x, z;
			Pathfinding::TileCenter(i, j, x, z);

			bool underWater = cmpWaterManager && (cmpWaterManager->GetWaterLevel(x, z) > terrain.GetExactGroundLevelFixed(x, z));
			waterGrid.set(i, j, underWater ? 1 : 0);
		}
	}

	// Second pass - find shore tiles
	Grid<u16> shoreGrid(m_MapSize, m_MapSize);
	for (u16 j = 0; j < m_MapSize; ++j)
	{
		for (u16 i = 0; i < m_MapSize; ++i)
		{
			// Find a land tile
			if (!waterGrid.get(i, j))
			{
				// If it's bordered by water, it's a shore tile
				if ((i > 0 && waterGrid.get(i-1, j)) || (i > 0 && j < m_MapSize-1 && waterGrid.get(i-1, j+1)) || (i > 0 && j > 0 && waterGrid.get(i-1, j-1))
					|| (i < m_MapSize-1 && waterGrid.get(i+1, j)) || (i < m_MapSize-1 && j < m_MapSize-1 && waterGrid.get(i+1, j+1)) || (i < m_MapSize-1 && j > 0 && waterGrid.get(i+1, j-1))
					|| (j > 0 && waterGrid.get(i, j-1)) || (j < m_MapSize-1 && waterGrid.get(i, j+1))
					)
					shoreGrid.set(i, j, 0);
				else
					shoreGrid.set(i, j, shoreMax);
			}
			// If we want to expand on water, we want water tiles not to be shore tiles
			else if (expandOnWater)
				shoreGrid.set(i, j, shoreMax);
		}
	}

	// Expand influences on land to find shore distance
	for (u16 y = 0; y < m_MapSize; ++y)
	{
		u16 min = shoreMax;
		for (u16 x = 0; x < m_MapSize; ++x)
		{
			if (!waterGrid.get(x, y) || expandOnWater)
			{
				u16 g = shoreGrid.get(x, y);
				if (g > min)
					shoreGrid.set(x, y, min);
				else if (g < min)
					min = g;

				++min;
			}
		}
		for (u16 x = m_MapSize; x > 0; --x)
		{
			if (!waterGrid.get(x-1, y) || expandOnWater)
			{
				u16 g = shoreGrid.get(x-1, y);
				if (g > min)
					shoreGrid.set(x-1, y, min);
				else if (g < min)
					min = g;

				++min;
			}
		}
	}
	for (u16 x = 0; x < m_MapSize; ++x)
	{
		u16 min = shoreMax;
		for (u16 y = 0; y < m_MapSize; ++y)
		{
			if (!waterGrid.get(x, y) || expandOnWater)
			{
				u16 g = shoreGrid.get(x, y);
				if (g > min)
					shoreGrid.set(x, y, min);
				else if (g < min)
					min = g;

				++min;
			}
		}
		for (u16 y = m_MapSize; y > 0; --y)
		{
			if (!waterGrid.get(x, y-1) || expandOnWater)
			{
				u16 g = shoreGrid.get(x, y-1);
				if (g > min)
					shoreGrid.set(x, y-1, min);
				else if (g < min)
					min = g;

				++min;
			}
		}
	}

	return shoreGrid;
}