From a919a0bfe6d02b8f88790c243af1e33991deb697 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 8 Sep 2026 19:23:16 +0200 Subject: [PATCH] feat: extend Portal --- core/src/Portal.cpp | 161 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/core/src/Portal.cpp b/core/src/Portal.cpp index 3c366ca..6b361c4 100644 --- a/core/src/Portal.cpp +++ b/core/src/Portal.cpp @@ -154,3 +154,164 @@ int PortalScan::light(World& world, const PortalFrame& frame) { return lit; } +int PortalScan::douse(World& world, const PortalFrame& frame) { + int cleared = 0; + for (int across = 0; across < frame.width; ++across) { + for (int up = 0; up < frame.height; ++up) { + Vec3i cell = frame.interiorBlock(across, up); + if (world.blockAt(cell.x, cell.y, cell.z) != Block::NetherPortal) continue; + world.setBlock(cell.x, cell.y, cell.z, Block::Air); + ++cleared; + } + } + return cleared; +} + +bool PortalScan::siteUsable(const World& world, const PortalFrame& frame) { + for (int across = -1; across <= frame.width; ++across) { + for (int up = -1; up <= frame.height; ++up) { + Vec3i cell = frame.interiorBlock(across, up); + if (world.blockAt(cell.x, cell.y, cell.z) == Block::Bedrock) return false; + } + } + return true; +} + +void PortalScan::buildFrame(World& world, const PortalFrame& frame) { + for (int across = -1; across <= frame.width; ++across) { + for (int up = -1; up <= frame.height; ++up) { + Vec3i cell = frame.interiorBlock(across, up); + bool ring = across == -1 || across == frame.width || up == -1 || up == frame.height; + world.setBlock(cell.x, cell.y, cell.z, ring ? Block::Obsidian : Block::Air); + } + } + + int stepX = frame.alongX ? 0 : 1; + int stepZ = frame.alongX ? 1 : 0; + for (int side = -1; side <= 1; side += 2) { + for (int across = 0; across < frame.width; ++across) { + Vec3i deck = frame.interiorBlock(across, -1); + world.setBlock(deck.x + stepX * side, deck.y, deck.z + stepZ * side, Block::Obsidian); + for (int up = 0; up < frame.height; ++up) { + Vec3i cell = frame.interiorBlock(across, up); + world.setBlock(cell.x + stepX * side, cell.y, cell.z + stepZ * side, Block::Air); + } + } + } +} + +PortalNetwork::PortalNetwork(DimensionSet& set) : transit(set) { + eyes.resize(static_cast(DimensionSet::dimensionCount)); +} + +int PortalNetwork::registerPortal(DimensionId dimension, const PortalFrame& frame) { + PortalLink link; + link.id = static_cast(portals.size()); + link.dimension = dimension; + link.frame = frame; + link.frame.dimension = dimension; + portals.push_back(link); + return link.id; +} + +int PortalNetwork::ignite(DimensionId dimension, const Vec3i& probe) { + World& world = dimensions().dimension(dimension).world(); + PortalScan scan = PortalScan::detect(world, probe, dimension); + if (!scan.found) return -1; + + PortalScan::light(world, scan.frame); + for (PortalLink& link : portals) { + if (link.dimension != dimension || link.frame != scan.frame) continue; + link.active = true; + return link.id; + } + return registerPortal(dimension, scan.frame); +} + +const PortalLink* PortalNetwork::portal(int id) const { + if (id < 0 || id >= static_cast(portals.size())) return nullptr; + return &portals[static_cast(id)]; +} + +int PortalNetwork::activeCountIn(DimensionId dimension) const { + int total = 0; + for (const PortalLink& link : portals) + if (link.active && link.dimension == dimension) ++total; + return total; +} + +void PortalNetwork::unlink(int id) { + const PortalLink* link = portal(id); + if (!link || link->linkedId < 0) return; + + int partner = link->linkedId; + portals[static_cast(id)].linkedId = -1; + if (partner >= 0 && partner < static_cast(portals.size())) + portals[static_cast(partner)].linkedId = -1; +} + +void PortalNetwork::linkPortals(int firstId, int secondId) { + if (!portal(firstId) || !portal(secondId)) return; + unlink(firstId); + unlink(secondId); + portals[static_cast(firstId)].linkedId = secondId; + portals[static_cast(secondId)].linkedId = firstId; +} + +int PortalNetwork::validate(DimensionId dimension) { + World& world = dimensions().dimension(dimension).world(); + int destroyed = 0; + + for (std::size_t index = 0; index < portals.size(); ++index) { + PortalLink& link = portals[index]; + if (!link.active || link.dimension != dimension) continue; + if (PortalScan::intact(world, link.frame)) continue; + + PortalScan::douse(world, link.frame); + link.active = false; + unlink(link.id); + ++destroyed; + } + return destroyed; +} + +PortalSearch PortalNetwork::findNearest(DimensionId dimension, const Vec3i& position, + int radius) const { + PortalSearch search; + long long best = -1; + + for (const PortalLink& link : portals) { + if (!link.active || link.dimension != dimension) continue; + Vec3i entry = link.frame.entry(); + long long dx = entry.x - position.x; + long long dy = entry.y - position.y; + long long dz = entry.z - position.z; + if (std::llabs(dx) > radius || std::llabs(dz) > radius) continue; + + long long distance = dx * dx + dy * dy + dz * dz; + if (best >= 0 && distance >= best) continue; + best = distance; + search.found = true; + search.portalId = link.id; + search.position = entry; + } + if (search.found) search.distanceSquared = static_cast(best); + return search; +} + +int PortalNetwork::buildPortalNear(DimensionId dimension, const Vec3i& target) { + Dimension& destination = dimensions().dimension(dimension); + destination.generateArea(floorDiv(target.x, chunkSize), floorDiv(target.z, chunkSize), 1); + + PortalFrame frame; + frame.dimension = dimension; + frame.alongX = true; + frame.width = 2; + frame.height = 3; + + const DimensionConfig& cfg = destination.config(); + int radius = DimensionTravel::buildRadiusFor(dimension); + Vec3i spot; + bool natural = transit.findSafeSpot(dimension, target, radius, frame.height + 1, spot); + if (!natural) spot = {target.x, cfg.clampY(target.y), target.z}; +