feat(trails): render authoritative player route trails

This commit is contained in:
2026-09-13 23:31:26 +03:00
parent 8ad856b931
commit 3518f8a152
12 changed files with 367 additions and 6 deletions
+31 -1
View File
@@ -306,7 +306,7 @@
return false;
}
const cellLimit = snapshot.width * snapshot.height;
return snapshot.cells.every(cell => Array.isArray(cell) && cell.length === 3 && Number.isInteger(cell[0]) && cell[0] >= 0 && cell[0] < cellLimit && Number.isInteger(cell[1]) && cell[1] >= 0 && cell[1] <= 15 && Number.isInteger(cell[2]) && cell[2] >= 0 && cell[2] <= 15) && snapshot.objects.every(object => Array.isArray(object) && object.length === 4 && Number.isInteger(object[3]) && object[3] >= 0 && object[3] < cellLimit);
return snapshot.cells.every(cell => Array.isArray(cell) && (cell.length === 3 || cell.length === 5) && Number.isInteger(cell[0]) && cell[0] >= 0 && cell[0] < cellLimit && Number.isInteger(cell[1]) && cell[1] >= 0 && cell[1] <= 15 && Number.isInteger(cell[2]) && cell[2] >= 0 && cell[2] <= 15 && (cell.length === 3 || (Number.isInteger(cell[3]) && cell[3] >= 0 && cell[3] <= 3 && Number.isInteger(cell[4]) && cell[4] >= 0 && cell[4] <= 3))) && snapshot.objects.every(object => Array.isArray(object) && object.length === 4 && Number.isInteger(object[3]) && object[3] >= 0 && object[3] < cellLimit);
}
class SnapshotAssembler {
@@ -781,6 +781,34 @@
return slot === 2 ? {name: "Синий герой", keyName: "синего игрока", owner: "two"} : {name: "Жёлтый герой", keyName: "жёлтого игрока", owner: "one"};
}
function renderTrails(maze, cells, width, minX, minY, columns, rows) {
const layer = documentObject.createElementNS("http://www.w3.org/2000/svg", "svg");
layer.classList.add("trail-layer");
layer.setAttribute("viewBox", `0 0 ${columns} ${rows}`);
layer.setAttribute("preserveAspectRatio", "none");
layer.setAttribute("aria-hidden", "true");
const paths = [[], []];
cells.forEach(cell => {
if (!Array.isArray(cell) || cell.length !== 5) return;
const x = cell[0] % width - minX;
const y = Math.floor(cell[0] / width) - minY;
[[cell[3], x + 1, y], [cell[4], x, y + 1]].forEach(([owners, endX, endY]) => {
if (!owners) return;
const segment = `M ${x + .5} ${y + .5} L ${endX + .5} ${endY + .5}`;
if (owners & 1) paths[0].push(segment);
if (owners & 2) paths[1].push(segment);
});
});
paths.forEach((segments, owner) => {
if (!segments.length) return;
const path = documentObject.createElementNS("http://www.w3.org/2000/svg", "path");
path.classList.add("trail-path", owner === 0 ? "trail-player-one" : "trail-player-two");
path.setAttribute("d", segments.join(" "));
layer.appendChild(path);
});
maze.appendChild(layer);
}
function renderMap(snapshot) {
const maze = byId("maze");
maze.replaceChildren();
@@ -816,6 +844,7 @@
if (data.index === snapshot.opponent) addPawn(cell, slot === 1 ? 2 : 1, true);
maze.appendChild(cell);
});
renderTrails(maze, snapshot.cells, snapshot.width, minX, minY, columns, rows);
renderHedgeEdges(maze, snapshot.cells, snapshot.width, minX, minY);
byId("map-placeholder").hidden = true;
}
@@ -900,6 +929,7 @@
});
maze.appendChild(cell);
});
renderTrails(maze, snapshot.cells, snapshot.width, 0, 0, snapshot.width, snapshot.height);
renderHedgeEdges(maze, snapshot.cells, snapshot.width, 0, 0);
players.forEach((player, index) => {
const key = player.key == null ? "без ключа" : `ключ P${player.key + 1}`;