feat: expand Portal

This commit is contained in:
dualfroz committed 2026-09-08 19:35:24 +02:00
1 parent a919a0bfe6
commit e2cf8b6dcc
1 file changed
+146
+146
View File
@@ -315,3 +315,149 @@ int PortalNetwork::buildPortalNear(DimensionId dimension, const Vec3i& target) {
bool natural = transit.findSafeSpot(dimension, target, radius, frame.height + 1, spot);
if (!natural) spot = {target.x, cfg.clampY(target.y), target.z};
if (spot.y - 1 < cfg.floorY || spot.y + frame.height >= cfg.ceilingY - 1) return -1;
frame.origin = spot;
if (!natural || !PortalScan::siteUsable(destination.world(), frame)) {
transit.carvePlatform(dimension, spot, 3, frame.height + 2);
if (!PortalScan::siteUsable(destination.world(), frame)) return -1;
}
PortalScan::buildFrame(destination.world(), frame);
if (PortalScan::light(destination.world(), frame) == 0) return -1;
return registerPortal(dimension, frame);
}
PortalSearch PortalNetwork::travel(int entityId, int sourcePortalId, DimensionId to) {
PortalSearch search;
const PortalLink* source = portal(sourcePortalId);
if (!source || !source->active) return search;
if (!readyToTravel(entityId)) {
search.blocked = true;
return search;
}
DimensionId from = source->dimension;
Vec3i position = source->frame.entry();
std::vector<Vec3i> candidates;
std::vector<int> candidateIds;
for (const PortalLink& link : portals) {
if (!link.active || link.dimension != to) continue;
candidates.push_back(link.frame.entry());
candidateIds.push_back(link.id);
}
search.plan = transit.plan(from, to, position, candidates);
if (!search.plan.needsPortal) {
std::size_t index = static_cast<std::size_t>(search.plan.nearestIndex);
search.found = true;
search.portalId = candidateIds[index];
search.position = candidates[index];
search.distanceSquared = search.plan.nearestDistanceSquared;
} else {
int raised = buildPortalNear(to, search.plan.target);
if (raised < 0) return search;
search.found = true;
search.built = true;
search.portalId = raised;
search.position = portals[static_cast<std::size_t>(raised)].frame.entry();
}
linkPortals(sourcePortalId, search.portalId);
startCooldown(entityId);
return search;
}
void PortalNetwork::tickEntity(int entityId, bool insidePortal) {
int& remaining = cooldown[entityId];
int& charged = standing[entityId];
if (remaining > 0) {
--remaining;
charged = 0;
return;
}
if (insidePortal) ++charged;
else charged = 0;
}
int PortalNetwork::standingTicks(int entityId) const {
auto it = standing.find(entityId);
return it == standing.end() ? 0 : it->second;
}
int PortalNetwork::cooldownTicks(int entityId) const {
auto it = cooldown.find(entityId);
return it == cooldown.end() ? 0 : it->second;
}
bool PortalNetwork::readyToTravel(int entityId) const {
return cooldownTicks(entityId) == 0 && standingTicks(entityId) >= standingThreshold;
}
void PortalNetwork::startCooldown(int entityId) {
cooldown[entityId] = cooldownAfterTravel;
standing[entityId] = 0;
}
int PortalNetwork::buildEndFrame(DimensionId dimension, const Vec3i& center) {
World& world = dimensions().dimension(dimension).world();
int placed = 0;
for (const Vec3i& offset : endRingOffsets) {
Vec3i slot = center + offset;
world.setBlock(slot.x, slot.y, slot.z, Block::EndPortalFrame);
++placed;
}
for (int dx = -1; dx <= 1; ++dx)
for (int dz = -1; dz <= 1; ++dz)
world.setBlock(center.x + dx, center.y, center.z + dz, Block::Air);
return placed;
}
bool PortalNetwork::hasEndEye(DimensionId dimension, const Vec3i& framePos) const {
const auto& sockets = eyes[dimensionIndex(dimension)];
return sockets.find(framePos) != sockets.end();
}
bool PortalNetwork::insertEndEye(DimensionId dimension, const Vec3i& framePos) {
World& world = dimensions().dimension(dimension).world();
if (world.blockAt(framePos.x, framePos.y, framePos.z) != Block::EndPortalFrame) return false;
if (!eyes[dimensionIndex(dimension)].insert(framePos).second) return false;
for (const Vec3i& offset : endRingOffsets) {
Vec3i center{framePos.x - offset.x, framePos.y - offset.y, framePos.z - offset.z};
activateEndPortal(dimension, center);
}
return true;
}
int PortalNetwork::endEyeCount(DimensionId dimension, const Vec3i& center) const {
const World& world = dimensions().dimension(dimension).world();
int filled = 0;
for (const Vec3i& offset : endRingOffsets) {
Vec3i slot = center + offset;
if (world.blockAt(slot.x, slot.y, slot.z) != Block::EndPortalFrame) continue;
if (hasEndEye(dimension, slot)) ++filled;
}
return filled;
}
bool PortalNetwork::endFrameComplete(DimensionId dimension, const Vec3i& center) const {
return endEyeCount(dimension, center) == endFrameSlots;
}
bool PortalNetwork::activateEndPortal(DimensionId dimension, const Vec3i& center) {
if (!endFrameComplete(dimension, center)) return false;
World& world = dimensions().dimension(dimension).world();
for (int dx = -1; dx <= 1; ++dx)
for (int dz = -1; dz <= 1; ++dz)
world.setBlock(center.x + dx, center.y, center.z + dz, Block::EndPortal);
return true;
}
} // namespace vf