feat: extend Portal

This commit is contained in:
dualfroz committed 2026-09-08 19:23:16 +02:00
1 parent 81d5504932
commit a919a0bfe6
1 file changed
+161
+161
View File
@@ -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<std::size_t>(DimensionSet::dimensionCount));
}
int PortalNetwork::registerPortal(DimensionId dimension, const PortalFrame& frame) {
PortalLink link;
link.id = static_cast<int>(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<int>(portals.size())) return nullptr;
return &portals[static_cast<std::size_t>(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<std::size_t>(id)].linkedId = -1;
if (partner >= 0 && partner < static_cast<int>(portals.size()))
portals[static_cast<std::size_t>(partner)].linkedId = -1;
}
void PortalNetwork::linkPortals(int firstId, int secondId) {
if (!portal(firstId) || !portal(secondId)) return;
unlink(firstId);
unlink(secondId);
portals[static_cast<std::size_t>(firstId)].linkedId = secondId;
portals[static_cast<std::size_t>(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<int>(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};