diff --git a/README.md b/README.md index 2a0ca59..5779297 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,12 @@ doesn't try to flood-fill out through the open air in front of and behind the ga 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 `gate.chevron-count` -(default 7) blocks evenly spaced around the ring's actual perimeter. Those blocks -swap to `gate.chevron-lit-material` (default glowstone) while dialing/open, and -revert to whatever they looked like at rest when the gate closes - so a chevron can -rest as plain obsidian and blend invisibly into the frame until it lights up. +(default 7) blocks evenly spaced around the ring's actual perimeter - anchored at the +topmost point for a vertical ring, and never the flat bottom row you walk through, +matching a real Stargate. Those blocks swap to `gate.chevron-lit-material` (default +glowstone) while dialing/open, and revert to whatever they looked like at rest when +the gate closes - so a chevron can rest as plain obsidian and blend invisibly into +the frame until it lights up. 1. Build the ring. 2. Optionally place a button directly below where you'll put the sign - this becomes 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 0eaeda7..cbf40ba 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 @@ -266,6 +266,8 @@ public class GateStructureScanner { * Picks {@code chevronCount} chevrons evenly spaced around the ring's actual geometric * perimeter - walks the ring in order (each frame block should have exactly two ring * neighbours) and takes evenly-spaced indices from that walk, not raw scan-discovery order. + * For a vertical ring, spacing is anchored at the topmost point and the flat bottom + * threshold row is never a candidate - a real Stargate never has a chevron underfoot. */ private List pickChevrons(List frameBlocks, BlockFace[] inPlane) { Map plane = new HashMap<>(); @@ -278,16 +280,40 @@ public class GateStructureScanner { List ordered = walkRing(frameBlocks, plane, inPlane); List ring = (ordered != null && ordered.size() >= 4) ? ordered : frameBlocks; - int count = Math.max(0, Math.min(chevronCount, ring.size())); + boolean vertical = false; + for (BlockFace f : inPlane) if (f == BlockFace.UP || f == BlockFace.DOWN) vertical = true; + + List candidates; + if (vertical) { + int n = ring.size(); + int[] rows = new int[n]; + int minRow = Integer.MAX_VALUE, maxRow = Integer.MIN_VALUE, topIdx = 0; + for (int i = 0; i < n; i++) { + rows[i] = project(ring.get(i), inPlane)[0]; // row is world Y for a vertical ring + if (rows[i] < minRow) minRow = rows[i]; + if (rows[i] > maxRow) { maxRow = rows[i]; topIdx = i; } + } + candidates = new ArrayList<>(); + for (int i = 0; i < n; i++) { + int idx = (topIdx + i) % n; + if (rows[idx] == minRow) continue; // skip the flat bottom threshold + candidates.add(ring.get(idx)); + } + if (candidates.isEmpty()) candidates = ring; // degenerate shape fallback + } else { + candidates = ring; // a horizontal ring has no "bottom" to avoid + } + + int count = Math.max(0, Math.min(chevronCount, candidates.size())); List slots = new ArrayList<>(count); if (count == 0) return slots; - double step = ring.size() / (double) count; + double step = candidates.size() / (double) count; Set chosen = new HashSet<>(); for (int i = 0; i < count; i++) { - int idx = (int) Math.round(i * step) % ring.size(); - while (chosen.contains(idx)) idx = (idx + 1) % ring.size(); + int idx = (int) Math.round(i * step) % candidates.size(); + while (chosen.contains(idx)) idx = (idx + 1) % candidates.size(); chosen.add(idx); - Block b = ring.get(idx); + Block b = candidates.get(idx); slots.add(new GateStructure.ChevronSlot(b, b.getType())); } return slots;