ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass, bool onlyCenterPoint) { // Check unit obstruction CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (!cmpObstructionManager) return ICmpObstruction::FOUNDATION_CHECK_FAIL_ERROR; if (cmpObstructionManager->TestStaticShape(filter, x, z, a, w, h, NULL)) return ICmpObstruction::FOUNDATION_CHECK_FAIL_OBSTRUCTS_FOUNDATION; // Test against terrain: UpdateGrid(); ICmpObstructionManager::ObstructionSquare square; CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id); if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(square)) return ICmpObstruction::FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION; if (onlyCenterPoint) { u16 i, j; NearestTile(x, z, i, j); if (IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass)) return ICmpObstruction::FOUNDATION_CHECK_SUCCESS; return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS; } // Expand bounds by 1/sqrt(2) tile (multiply by TERRAIN_TILE_SIZE since we want world coordinates) entity_pos_t expand = entity_pos_t::FromInt(2).Sqrt().Multiply(entity_pos_t::FromInt(TERRAIN_TILE_SIZE / 2)); CFixedVector2D halfSize(square.hw + expand, square.hh + expand); CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(square.u, square.v, halfSize); u16 i0, j0, i1, j1; NearestTile(square.x - halfBound.X, square.z - halfBound.Y, i0, j0); NearestTile(square.x + halfBound.X, square.z + halfBound.Y, i1, j1); for (u16 j = j0; j <= j1; ++j) { for (u16 i = i0; i <= i1; ++i) { entity_pos_t x, z; TileCenter(i, j, x, z); if (Geometry::PointIsInSquare(CFixedVector2D(x - square.x, z - square.z), square.u, square.v, halfSize) && !IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass)) { return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS; } } } return ICmpObstruction::FOUNDATION_CHECK_SUCCESS; }
bool CCmpPathfinder::CheckUnitPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass) { // Check unit obstruction CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (cmpObstructionManager.null()) return false; if (cmpObstructionManager->TestUnitShape(filter, x, z, r, NULL)) return false; // Test against terrain: UpdateGrid(); u16 i0, j0, i1, j1; NearestTile(x - r, z - r, i0, j0); NearestTile(x + r, z + r, i1, j1); for (u16 j = j0; j <= j1; ++j) { for (u16 i = i0; i <= i1; ++i) { if (!IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass)) { return false; } } } return true; }
ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckUnitPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass, bool onlyCenterPoint) { // Check unit obstruction CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (!cmpObstructionManager) return ICmpObstruction::FOUNDATION_CHECK_FAIL_ERROR; if (cmpObstructionManager->TestUnitShape(filter, x, z, r, NULL)) return ICmpObstruction::FOUNDATION_CHECK_FAIL_OBSTRUCTS_FOUNDATION; // Test against terrain: UpdateGrid(); if (onlyCenterPoint) { u16 i, j; NearestTile(x , z, i, j); if (IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass)) return ICmpObstruction::FOUNDATION_CHECK_SUCCESS; return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS; } u16 i0, j0, i1, j1; NearestTile(x - r, z - r, i0, j0); NearestTile(x + r, z + r, i1, j1); for (u16 j = j0; j <= j1; ++j) { for (u16 i = i0; i <= i1; ++i) { if (!IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass)) { return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS; } } } return ICmpObstruction::FOUNDATION_CHECK_SUCCESS; }
static void AddTerrainEdges(std::vector<Edge>& edgesAA, std::vector<Vertex>& vertexes, u16 i0, u16 j0, u16 i1, u16 j1, fixed r, ICmpPathfinder::pass_class_t passClass, const Grid<TerrainTile>& terrain) { PROFILE("AddTerrainEdges"); std::vector<TileEdge> tileEdges; // Find all edges between tiles of differently passability statuses for (u16 j = j0; j <= j1; ++j) { for (u16 i = i0; i <= i1; ++i) { if (!IS_TERRAIN_PASSABLE(terrain.get(i, j), passClass)) { bool any = false; // whether we're adding any edges of this tile if (j > 0 && IS_TERRAIN_PASSABLE(terrain.get(i, j-1), passClass)) { TileEdge e = { i, j, TileEdge::BOTTOM }; tileEdges.push_back(e); any = true; } if (j < terrain.m_H-1 && IS_TERRAIN_PASSABLE(terrain.get(i, j+1), passClass)) { TileEdge e = { i, j, TileEdge::TOP }; tileEdges.push_back(e); any = true; } if (i > 0 && IS_TERRAIN_PASSABLE(terrain.get(i-1, j), passClass)) { TileEdge e = { i, j, TileEdge::LEFT }; tileEdges.push_back(e); any = true; } if (i < terrain.m_W-1 && IS_TERRAIN_PASSABLE(terrain.get(i+1, j), passClass)) { TileEdge e = { i, j, TileEdge::RIGHT }; tileEdges.push_back(e); any = true; } // If we want to add any edge, then add the whole square to the axis-aligned-edges list. // (The inner edges are redundant but it's easier than trying to split the squares apart.) if (any) { CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i * (int)CELL_SIZE) - r, fixed::FromInt(j * (int)CELL_SIZE) - r); CFixedVector2D v1 = CFixedVector2D(fixed::FromInt((i+1) * (int)CELL_SIZE) + r, fixed::FromInt((j+1) * (int)CELL_SIZE) + r); Edge e = { v0, v1 }; edgesAA.push_back(e); } } } } // TODO: maybe we should precompute these terrain edges since they'll rarely change? // TODO: for efficiency (minimising the A* search space), we should coalesce adjoining edges // Add all the tile outer edges to the search vertex lists for (size_t n = 0; n < tileEdges.size(); ++n) { u16 i = tileEdges[n].i; u16 j = tileEdges[n].j; CFixedVector2D v0, v1; Vertex vert; vert.status = Vertex::UNEXPLORED; vert.quadOutward = QUADRANT_ALL; switch (tileEdges[n].dir) { case TileEdge::BOTTOM: { v0 = CFixedVector2D(fixed::FromInt(i * (int)CELL_SIZE) - r, fixed::FromInt(j * (int)CELL_SIZE) - r); v1 = CFixedVector2D(fixed::FromInt((i+1) * (int)CELL_SIZE) + r, fixed::FromInt(j * (int)CELL_SIZE) - r); vert.p.X = v0.X - EDGE_EXPAND_DELTA; vert.p.Y = v0.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TR; vertexes.push_back(vert); vert.p.X = v1.X + EDGE_EXPAND_DELTA; vert.p.Y = v1.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TL; vertexes.push_back(vert); break; } case TileEdge::TOP: { v0 = CFixedVector2D(fixed::FromInt((i+1) * (int)CELL_SIZE) + r, fixed::FromInt((j+1) * (int)CELL_SIZE) + r); v1 = CFixedVector2D(fixed::FromInt(i * (int)CELL_SIZE) - r, fixed::FromInt((j+1) * (int)CELL_SIZE) + r); vert.p.X = v0.X + EDGE_EXPAND_DELTA; vert.p.Y = v0.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BL; vertexes.push_back(vert); vert.p.X = v1.X - EDGE_EXPAND_DELTA; vert.p.Y = v1.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BR; vertexes.push_back(vert); break; } case TileEdge::LEFT: { v0 = CFixedVector2D(fixed::FromInt(i * (int)CELL_SIZE) - r, fixed::FromInt((j+1) * (int)CELL_SIZE) + r); v1 = CFixedVector2D(fixed::FromInt(i * (int)CELL_SIZE) - r, fixed::FromInt(j * (int)CELL_SIZE) - r); vert.p.X = v0.X - EDGE_EXPAND_DELTA; vert.p.Y = v0.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BR; vertexes.push_back(vert); vert.p.X = v1.X - EDGE_EXPAND_DELTA; vert.p.Y = v1.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TR; vertexes.push_back(vert); break; } case TileEdge::RIGHT: { v0 = CFixedVector2D(fixed::FromInt((i+1) * (int)CELL_SIZE) + r, fixed::FromInt(j * (int)CELL_SIZE) - r); v1 = CFixedVector2D(fixed::FromInt((i+1) * (int)CELL_SIZE) + r, fixed::FromInt((j+1) * (int)CELL_SIZE) + r); vert.p.X = v0.X + EDGE_EXPAND_DELTA; vert.p.Y = v0.Y - EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_TL; vertexes.push_back(vert); vert.p.X = v1.X + EDGE_EXPAND_DELTA; vert.p.Y = v1.Y + EDGE_EXPAND_DELTA; vert.quadInward = QUADRANT_BL; vertexes.push_back(vert); break; } } } }