feat: extend Portal
This commit is contained in:
1 parent
81d5504932
commit
a919a0bfe6
1 file changed
+161
@@ -154,3 +154,164 @@ int PortalScan::light(World& world, const PortalFrame& frame) {
|
|||||||
return lit;
|
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};
|
||||||
|
|
||||||
Reference in new issue
Block a user