diff --git a/README.md b/README.md index 021dd0c..fa28659 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,9 @@ Build everything with `./gradlew build`. Jars land in each module's `build/libs/ There's no fixed size or shape - build any single connected ring out of `gate.frame-materials` (default: obsidian, gold block, gilded blackstone, birch/oak planks) with a fully sealed hollow interior. No gaps; the interior must be enclosed. +"Connected" includes the usual diagonal stepping a round/octagonal ring needs at its +corners (blocks touching only edge-to-edge or corner-to-corner still count), so a +circular build doesn't need every block to share a full face with the next. Chevrons aren't a separate material you place. When the gate is linked, the plugin flood-fills the ring, walks it in geometric order, and picks out the blocks that stick diff --git a/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/gate/GateStructureScanner.java b/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/gate/GateStructureScanner.java index 66e4d1f..f3032c0 100644 --- a/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/gate/GateStructureScanner.java +++ b/stargate-paper/src/main/java/dev/skywalker3200/stargate/paper/gate/GateStructureScanner.java @@ -25,6 +25,25 @@ public class GateStructureScanner { BlockFace.UP, BlockFace.DOWN, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST }; + // Round/octagonal rings built the usual Minecraft way step diagonally at the corners - two + // frame blocks touching only edge-to-edge (or even just corner-to-corner), not face-to-face. + // The frame search has to follow those too, or a stepped ring gets treated as several + // disconnected fragments instead of one structure. + private static final int[][] ALL_26_OFFSETS = buildAll26Offsets(); + + private static int[][] buildAll26Offsets() { + List offsets = new ArrayList<>(); + for (int dx = -1; dx <= 1; dx++) { + for (int dy = -1; dy <= 1; dy++) { + for (int dz = -1; dz <= 1; dz++) { + if (dx == 0 && dy == 0 && dz == 0) continue; + offsets.add(new int[]{dx, dy, dz}); + } + } + } + return offsets.toArray(new int[0][]); + } + public static final class ScanResult { public final GateStructure structure; // null on failure public final String failureReason; // null on success @@ -84,8 +103,8 @@ public class GateStructureScanner { + "Raise max-frame-blocks or make the frame materials more specific."); } - for (BlockFace face : NEIGHBORS) { - Block next = cur.getRelative(face); + for (int[] d : ALL_26_OFFSETS) { + Block next = cur.getRelative(d[0], d[1], d[2]); long k = key(next); if (frameKeys.contains(k)) continue; if (isFrameMaterial(next.getType())) { @@ -224,14 +243,18 @@ public class GateStructureScanner { return chevrons.isEmpty() ? fallbackEvenlySpaced(frameBlocks) : chevrons; } - /** Walks the ring in geometric order assuming each frame block has exactly two in-plane neighbours; null if that assumption fails. */ + private static final int[][] PLANE_8_OFFSETS = { + {1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1} + }; + + /** Walks the ring in geometric order assuming each frame block has exactly two in-plane neighbours (orthogonal or diagonal, for stepped rings); null if that assumption fails. */ private List walkRing(List frameBlocks, Map plane, boolean horizontalIsX) { Map> adjacency = new HashMap<>(); for (Block b : frameBlocks) { int col = horizontalIsX ? b.getX() : b.getZ(); int row = b.getY(); List neighbors = new ArrayList<>(); - for (int[] d : new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}) { + for (int[] d : PLANE_8_OFFSETS) { long k = (((long) (row + d[0])) << 32) ^ ((col + d[1]) & 0xFFFFFFFFL); Block n = plane.get(k); if (n != null && n != b) neighbors.add(n);