feat: expand Portal
This commit is contained in:
1 parent
a919a0bfe6
commit
e2cf8b6dcc
1 file changed
+146
@@ -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
|
||||
Reference in new issue
Block a user