diff --git a/core/src/Dimension.cpp b/core/src/Dimension.cpp index 5f88a0e..4d5e0b9 100644 --- a/core/src/Dimension.cpp +++ b/core/src/Dimension.cpp @@ -150,3 +150,150 @@ int Dimension::highestSolidY(int gx, int gz) const { return cfg.floorY - 1; } +bool Dimension::standingSpot(int gx, int gz, int fromY, int clearance, int& outFeetY) const { + int start = std::max(fromY, cfg.floorY); + for (int y = start; y < cfg.highestPlaceableY() - clearance; ++y) { + if (!blocks.solidAt(gx, y, gz)) continue; + bool roomy = true; + for (int up = 1; up <= clearance && roomy; ++up) + roomy = blocks.blockAt(gx, y + up, gz) == Block::Air; + if (!roomy) continue; + outFeetY = y + 1; + return true; + } + return false; +} + +DimensionSet::DimensionSet(uint32_t worldSeed) : baseSeed(worldSeed) { + worlds.reserve(dimensionCount); + for (int index = 0; index < dimensionCount; ++index) { + DimensionId id = static_cast(index); + worlds.push_back(std::make_unique(DimensionConfig::forDimension(id), worldSeed)); + } +} + +void DimensionSet::tickAll(int steps) { + for (auto& dimension : worlds) dimension->tick(steps); +} + +std::size_t DimensionSet::totalChunks() const { + std::size_t total = 0; + for (const auto& dimension : worlds) total += dimension->world().chunkCount(); + return total; +} + +Vec3i DimensionTravel::scale(const Vec3i& pos, const DimensionConfig& from, + const DimensionConfig& to) { + double ratio = static_cast(from.coordinateScale) / + static_cast(to.coordinateScale); + return {static_cast(std::floor(pos.x * ratio)), pos.y, + static_cast(std::floor(pos.z * ratio))}; +} + +int DimensionTravel::searchRadiusFor(DimensionId to) { + return to == DimensionId::Nether ? 128 : 16; +} + +int DimensionTravel::buildRadiusFor(DimensionId to) { + return std::min(searchRadiusFor(to), 16); +} + +TravelResult DimensionTravel::plan(DimensionId from, DimensionId to, const Vec3i& pos) const { + return plan(from, to, pos, {}); +} + +TravelResult DimensionTravel::plan(DimensionId from, DimensionId to, const Vec3i& pos, + const std::vector& knownPortals) const { + TravelResult result; + result.from = from; + result.to = to; + result.source = pos; + + const DimensionConfig& origin = worlds.dimension(from).config(); + const DimensionConfig& destination = worlds.dimension(to).config(); + + result.scaledTarget = scale(pos, origin, destination); + result.target = result.scaledTarget; + if (result.target.y < destination.lowestPlaceableY()) { + result.target.y = destination.lowestPlaceableY(); + result.clampedToFloor = true; + } else if (result.target.y > destination.highestPlaceableY()) { + result.target.y = destination.highestPlaceableY(); + result.clampedToCeiling = true; + } + + result.searchRadius = searchRadiusFor(to); + long long best = -1; + for (std::size_t index = 0; index < knownPortals.size(); ++index) { + const Vec3i& candidate = knownPortals[index]; + long long dx = candidate.x - result.target.x; + long long dy = candidate.y - result.target.y; + long long dz = candidate.z - result.target.z; + if (std::llabs(dx) > result.searchRadius || std::llabs(dz) > result.searchRadius) continue; + long long distance = dx * dx + dy * dy + dz * dz; + if (best >= 0 && distance >= best) continue; + best = distance; + result.nearestIndex = static_cast(index); + } + + if (result.nearestIndex >= 0) { + result.needsPortal = false; + result.nearestDistanceSquared = static_cast(best); + } + return result; +} + +bool DimensionTravel::findSafeSpot(DimensionId to, const Vec3i& near, int radius, int clearance, + Vec3i& outSpot) const { + const Dimension& destination = worlds.dimension(to); + int floorY = destination.config().floorY; + + for (int ring = 0; ring <= radius; ++ring) { + int bestDrop = -1; + Vec3i best; + for (int dx = -ring; dx <= ring; ++dx) { + for (int dz = -ring; dz <= ring; ++dz) { + if (std::max(std::abs(dx), std::abs(dz)) != ring) continue; + + int gx = near.x + dx; + int gz = near.z + dz; + int scan = floorY; + int feetY = 0; + while (destination.standingSpot(gx, gz, scan, clearance, feetY)) { + int drop = std::abs(feetY - near.y); + if (drop <= safeSpotVerticalBand && (bestDrop < 0 || drop < bestDrop)) { + bestDrop = drop; + best = {gx, feetY, gz}; + } + scan = feetY + 1; + } + } + } + if (bestDrop >= 0) { + outSpot = best; + return true; + } + } + return false; +} + +void DimensionTravel::carvePlatform(DimensionId to, const Vec3i& spot, int halfWidth, + int clearance) { + Dimension& destination = worlds.dimension(to); + const DimensionConfig& cfg = destination.config(); + World& world = destination.world(); + + int deck = std::max(spot.y - 1, cfg.floorY); + for (int dx = -halfWidth; dx <= halfWidth; ++dx) { + for (int dz = -halfWidth; dz <= halfWidth; ++dz) { + world.setBlock(spot.x + dx, deck, spot.z + dz, Block::Obsidian); + for (int up = 1; up <= clearance; ++up) { + int y = deck + up; + if (y >= cfg.ceilingY - 1) break; + world.setBlock(spot.x + dx, y, spot.z + dz, Block::Air); + } + } + } +} + +} // namespace vf