The glass board's marks are drawn over the glass and the figures: a dot on an empty square a piece may go to, a ring around a figure it may take or that is chosen, a halo for the last move, each on a dark rim so it reads on the pale air as well as the underworld

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCLa2PaEeCj8njQZSWGqHx
This commit is contained in:
Eric WagonerandClaude Fable 5.1 committed 2026-10-27 08:08:38 -04:00
1 parent ff69d53e8c
commit cf92c9bd27
1 file changed
+25 -13
+25 -13
View File
@@ -121,15 +121,27 @@
} }
// Marks on squares: what is selected, where it may go, the last move, a King in check. // Marks on squares: what is selected, where it may go, the last move, a King in check.
// They are drawn over everything, so that neither the glass above nor a figure on the
// square can hide them, and each sits on a dark rim so it reads on the pale air as well as
// the underworld. A dot is an empty square a piece may go to; a ring hugs a figure's base.
const marks = new THREE.Group(); const marks = new THREE.Group();
scene.add(marks); scene.add(marks);
const markGeometry = new THREE.PlaneGeometry(CELL * 0.92, CELL * 0.92); const SHAPE = {
function mark(i: number, colour: string, opacity: number) { dot: [new THREE.CircleGeometry(CELL * 0.18, 24), new THREE.CircleGeometry(CELL * 0.23, 24)],
const m = new THREE.Mesh(markGeometry, new THREE.MeshBasicMaterial({ color: colour, transparent: true, opacity, depthWrite: false })); ring: [new THREE.RingGeometry(CELL * 0.5, CELL * 0.6, 48), new THREE.RingGeometry(CELL * 0.46, CELL * 0.64, 48)],
m.userData.square = i; halo: [new THREE.RingGeometry(CELL * 0.66, CELL * 0.74, 48), new THREE.RingGeometry(CELL * 0.63, CELL * 0.77, 48)]
m.rotation.x = -Math.PI / 2; };
m.position.copy(at(i)).add(new THREE.Vector3(0, 0.015, 0)); function mark(i: number, shape: keyof typeof SHAPE, colour: string, opacity = 1) {
marks.add(m); const [face, rim] = SHAPE[shape];
const layers: [THREE.BufferGeometry, string, number][] = [[rim, '#1b1410', opacity * 0.75], [face, colour, opacity]];
for (const [geometry, color, alpha] of layers) {
const m = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color, transparent: true, opacity: alpha, depthWrite: false, depthTest: false, toneMapped: false }));
m.userData.square = i;
m.renderOrder = 10 + marks.children.length;
m.rotation.x = -Math.PI / 2;
m.position.copy(at(i)).add(new THREE.Vector3(0, 0.015, 0));
marks.add(m);
}
} }
// Figures: one scene per kind, shared geometry, a livery per side and a dimmed one for a piece held by a Basilisk. // Figures: one scene per kind, shared geometry, a livery per side and a dimmed one for a piece held by a Basilisk.
@@ -356,12 +368,12 @@
}); });
$effect(() => { $effect(() => {
marks.clear(); marks.clear();
for (const i of reach) mark(i, MARK.reach, 0.22); for (const i of reach) mark(i, g.cells[i] === '' ? 'dot' : 'ring', MARK.reach, 0.85);
for (const i of shownSquares) mark(i, MARK.last, 0.4); for (const i of shownSquares) mark(i, 'halo', MARK.last);
for (const i of shownCaptured) mark(i, MARK.fallen, 0.45); for (const i of shownCaptured) mark(i, 'halo', MARK.fallen);
for (const i of targets) mark(i, MARK.target, 0.5); for (const i of targets) mark(i, g.cells[i] === '' ? 'dot' : 'ring', MARK.target);
if (selected !== null) mark(selected, MARK.selected, 0.6); if (selected !== null) mark(selected, 'ring', MARK.selected);
if (checkSquare >= 0) mark(checkSquare, MARK.check, 0.6); if (checkSquare >= 0 && checkSquare !== selected) mark(checkSquare, 'ring', MARK.check);
wake(); wake();
}); });
$effect(() => look(view)); $effect(() => look(view));