fix: spectator board rendering
This commit is contained in:
+15
-8
@@ -1,6 +1,7 @@
|
||||
(() => {
|
||||
const columns = ['А', 'Б', 'В', 'Г', 'Д', 'Ж', 'З', 'И', 'Й', 'К'];
|
||||
const storage = { token: 'battleship.sessionToken', name: 'battleship.displayName' };
|
||||
const boardKeys = Object.freeze({ own: 'own', opponent: 'opponent', player1: 'player1', player2: 'player2' });
|
||||
const screens = ['connect', 'lobby', 'game', 'result', 'reconnecting', 'error'];
|
||||
const el = id => document.getElementById(id);
|
||||
const ui = {
|
||||
@@ -336,6 +337,7 @@
|
||||
return payload && payload.type === 'state' && Array.isArray(payload.boards) && payload.boards.length === 2 &&
|
||||
payload.boards.every(board => typeof board === 'string' && /^[01234]{100}$/.test(board)) &&
|
||||
typeof payload.version === 'number' && typeof payload.gameId === 'number' &&
|
||||
['player1', 'player2', 'spectator'].includes(payload.viewer) &&
|
||||
(payload.winner === null || payload.winner === 0 || payload.winner === 1) && Array.isArray(payload.statistics) &&
|
||||
Array.isArray(payload.players) && payload.players.length === 2 && payload.players.every(name => typeof name === 'string' && name.length <= 80) &&
|
||||
payload.statistics.length === 2 && payload.statistics.every(entry => Array.isArray(entry) && entry.length === 4 &&
|
||||
@@ -348,6 +350,8 @@
|
||||
const previousState = state;
|
||||
state = payload;
|
||||
role = payload.viewer;
|
||||
const validBoardKeys = role === 'spectator' ? [boardKeys.player1, boardKeys.player2] : [boardKeys.own, boardKeys.opponent];
|
||||
if (!validBoardKeys.includes(activeBoard)) activeBoard = validBoardKeys[0];
|
||||
if (hadGap) pollState();
|
||||
selectedTarget = selectedTarget && payload.boards[opponentIndex()][selectedTarget.y * 10 + selectedTarget.x] === '0' ? selectedTarget : undefined;
|
||||
document.title = `Морской бой — версия ${payload.version}`;
|
||||
@@ -511,18 +515,19 @@
|
||||
|
||||
function boardDefinitions(result = false) {
|
||||
if (role === 'spectator' || result) return [
|
||||
{ index: 0, title: `Поле: ${playerLabel(0)}`, key: 'player1', targetable: false },
|
||||
{ index: 1, title: `Поле: ${playerLabel(1)}`, key: 'player2', targetable: false }
|
||||
{ index: 0, title: `Поле: ${playerLabel(0)}`, key: boardKeys.player1, targetable: false },
|
||||
{ index: 1, title: `Поле: ${playerLabel(1)}`, key: boardKeys.player2, targetable: false }
|
||||
];
|
||||
return [
|
||||
{ index: ownIndex(), title: 'Моё поле', key: 'own', targetable: false },
|
||||
{ index: opponentIndex(), title: state.mode === 'bot' ? 'Поле ESP32' : `Поле: ${playerName(opponentIndex())}`, key: 'opponent', targetable: canShoot() }
|
||||
{ index: ownIndex(), title: 'Моё поле', key: boardKeys.own, targetable: false },
|
||||
{ index: opponentIndex(), title: state.mode === 'bot' ? 'Поле ESP32' : `Поле: ${playerName(opponentIndex())}`, key: boardKeys.opponent, targetable: canShoot() }
|
||||
];
|
||||
}
|
||||
|
||||
function renderBoards(container, result = false) {
|
||||
container.replaceChildren();
|
||||
const definitions = boardDefinitions(result);
|
||||
if (!result && !definitions.some(definition => definition.key === activeBoard)) activeBoard = definitions[0].key;
|
||||
definitions.forEach(definition => {
|
||||
const board = boardElement(definition.index, definition.title, definition.targetable, result);
|
||||
board.hidden = !result && definition.key !== activeBoard;
|
||||
@@ -532,7 +537,7 @@
|
||||
ui.tabs.replaceChildren();
|
||||
definitions.forEach(definition => {
|
||||
const tab = document.createElement('button');
|
||||
tab.type = 'button'; tab.role = 'tab'; tab.textContent = definition.key === 'opponent' ? playerLabel(definition.index) : definition.title;
|
||||
tab.type = 'button'; tab.role = 'tab'; tab.textContent = definition.key === boardKeys.opponent ? playerLabel(definition.index) : definition.title;
|
||||
tab.title = definition.title; tab.setAttribute('aria-label', definition.title);
|
||||
tab.setAttribute('aria-selected', String(definition.key === activeBoard));
|
||||
tab.addEventListener('click', () => { activeBoard = definition.key; renderGame(); });
|
||||
@@ -566,15 +571,17 @@
|
||||
if (!state) return;
|
||||
stopLobbyInfoPolling();
|
||||
showScreen('game');
|
||||
if (canShoot() && !selectedTarget) activeBoard = 'opponent';
|
||||
ui.turn.textContent = canShoot() ? 'Ваш ход' : `Ходит ${playerLabel(state.turn === 'player2' ? 1 : 0)}`;
|
||||
if (canShoot() && !selectedTarget) activeBoard = boardKeys.opponent;
|
||||
const turnPlayer = playerLabel(state.turn === 'player2' ? 1 : 0);
|
||||
const watching = role === 'spectator';
|
||||
ui.turn.textContent = canShoot() ? 'Ваш ход' : watching ? `Наблюдаем за боем · Ходит ${turnPlayer}` : `Ходит ${turnPlayer}`;
|
||||
ui.wins.textContent = `${playerLabel(0)} ${state.wins[0]} : ${state.wins[1]} ${playerLabel(1)}`;
|
||||
renderBoards(ui.boards);
|
||||
const available = canShoot();
|
||||
ui.cue.classList.toggle('is-ready', available && !selectedTarget);
|
||||
ui.cue.classList.toggle('has-target', Boolean(available && selectedTarget));
|
||||
setSpriteIcon(ui.cueIcon, available ? (selectedTarget ? 'icon-blast' : 'icon-target') : 'icon-hourglass');
|
||||
ui.cueText.textContent = available ? (selectedTarget ? 'Жми «Огонь»!' : 'Твой ход! Выбери клетку') : 'Ждём соперника…';
|
||||
ui.cueText.textContent = available ? (selectedTarget ? 'Жми «Огонь»!' : 'Твой ход! Выбери клетку') : watching ? 'Наблюдаем за боем' : 'Ждём соперника…';
|
||||
ui.shotControls.hidden = !isPlayer();
|
||||
ui.target.textContent = firePending ? 'Выстрел отправляется…' : available ? (selectedTarget ? `Цель: ${columns[selectedTarget.x]}${selectedTarget.y + 1}` : 'Выберите клетку на поле соперника.') : 'Ожидайте своего хода.';
|
||||
ui.fire.disabled = !selectedTarget || !canFireTarget(selectedTarget);
|
||||
|
||||
Reference in New Issue
Block a user