Never place a chevron on the flat bottom threshold
pickChevrons() spaced chevrons evenly starting from whichever ring block the flood-fill happened to discover first - an arbitrary point depending on where the ring was punched, with no relationship to the ring's actual top/bottom. That let spacing land a chevron directly on the bottom row you walk through, which a real Stargate never has. For a vertical ring, chevron spacing is now anchored at the true topmost block and the bottom row is excluded from the candidate list entirely before spacing runs. Horizontal rings are unaffected - they have no "bottom" to avoid, so they still use the full ring.
This commit is contained in:
@@ -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
|
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`
|
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
|
(default 7) blocks evenly spaced around the ring's actual perimeter - anchored at the
|
||||||
swap to `gate.chevron-lit-material` (default glowstone) while dialing/open, and
|
topmost point for a vertical ring, and never the flat bottom row you walk through,
|
||||||
revert to whatever they looked like at rest when the gate closes - so a chevron can
|
matching a real Stargate. Those blocks swap to `gate.chevron-lit-material` (default
|
||||||
rest as plain obsidian and blend invisibly into the frame until it lights up.
|
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.
|
1. Build the ring.
|
||||||
2. Optionally place a button directly below where you'll put the sign - this becomes
|
2. Optionally place a button directly below where you'll put the sign - this becomes
|
||||||
|
|||||||
+31
-5
@@ -266,6 +266,8 @@ public class GateStructureScanner {
|
|||||||
* Picks {@code chevronCount} chevrons evenly spaced around the ring's actual geometric
|
* 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
|
* 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.
|
* 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<GateStructure.ChevronSlot> pickChevrons(List<Block> frameBlocks, BlockFace[] inPlane) {
|
private List<GateStructure.ChevronSlot> pickChevrons(List<Block> frameBlocks, BlockFace[] inPlane) {
|
||||||
Map<Long, Block> plane = new HashMap<>();
|
Map<Long, Block> plane = new HashMap<>();
|
||||||
@@ -278,16 +280,40 @@ public class GateStructureScanner {
|
|||||||
List<Block> ordered = walkRing(frameBlocks, plane, inPlane);
|
List<Block> ordered = walkRing(frameBlocks, plane, inPlane);
|
||||||
List<Block> ring = (ordered != null && ordered.size() >= 4) ? ordered : frameBlocks;
|
List<Block> 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<Block> 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<GateStructure.ChevronSlot> slots = new ArrayList<>(count);
|
List<GateStructure.ChevronSlot> slots = new ArrayList<>(count);
|
||||||
if (count == 0) return slots;
|
if (count == 0) return slots;
|
||||||
double step = ring.size() / (double) count;
|
double step = candidates.size() / (double) count;
|
||||||
Set<Integer> chosen = new HashSet<>();
|
Set<Integer> chosen = new HashSet<>();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
int idx = (int) Math.round(i * step) % ring.size();
|
int idx = (int) Math.round(i * step) % candidates.size();
|
||||||
while (chosen.contains(idx)) idx = (idx + 1) % ring.size();
|
while (chosen.contains(idx)) idx = (idx + 1) % candidates.size();
|
||||||
chosen.add(idx);
|
chosen.add(idx);
|
||||||
Block b = ring.get(idx);
|
Block b = candidates.get(idx);
|
||||||
slots.add(new GateStructure.ChevronSlot(b, b.getType()));
|
slots.add(new GateStructure.ChevronSlot(b, b.getType()));
|
||||||
}
|
}
|
||||||
return slots;
|
return slots;
|
||||||
|
|||||||
Reference in New Issue
Block a user