Update chess/public/js/app.js
This commit is contained in:
+58
-109
@@ -99,10 +99,6 @@ const elements = {
|
|||||||
partySummary: document.querySelector("#partySummary"),
|
partySummary: document.querySelector("#partySummary"),
|
||||||
copyInviteButton: document.querySelector("#copyInviteButton"),
|
copyInviteButton: document.querySelector("#copyInviteButton"),
|
||||||
|
|
||||||
// ДОБАВЛЕНЫ КНОПКИ "НОВАЯ ИГРА"
|
|
||||||
newGameToolbarButton: document.querySelector("#newGameToolbarButton"),
|
|
||||||
newPartyGameButton: document.querySelector("#newPartyGameButton"),
|
|
||||||
|
|
||||||
flipBoardButton: document.querySelector("#flipBoardButton"),
|
flipBoardButton: document.querySelector("#flipBoardButton"),
|
||||||
|
|
||||||
whitePlayerLabel: document.querySelector("#whitePlayerLabel"),
|
whitePlayerLabel: document.querySelector("#whitePlayerLabel"),
|
||||||
@@ -524,22 +520,22 @@ function formatStatus() {
|
|||||||
const status = currentStatus();
|
const status = currentStatus();
|
||||||
|
|
||||||
if (status.phase === "checkmate") {
|
if (status.phase === "checkmate") {
|
||||||
return "Мат!";
|
return "Checkmate";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.phase === "draw") {
|
if (status.phase === "draw") {
|
||||||
return "Ничья";
|
return "Draw";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.check) {
|
if (status.check) {
|
||||||
return appState.engine.state.turn === "w"
|
return appState.engine.state.turn === "w"
|
||||||
? "Шах белым"
|
? "White is in check"
|
||||||
: "Шах черным";
|
: "Black is in check";
|
||||||
}
|
}
|
||||||
|
|
||||||
return appState.engine.state.turn === "w"
|
return appState.engine.state.turn === "w"
|
||||||
? "Ход белых"
|
? "White to move"
|
||||||
: "Ход черных";
|
: "Black to move";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -550,7 +546,7 @@ function getGameOverMessage() {
|
|||||||
if (!snapshot) {
|
if (!snapshot) {
|
||||||
return (
|
return (
|
||||||
appState.gameOverMessage ||
|
appState.gameOverMessage ||
|
||||||
"Игра окончена."
|
"Game over."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,21 +559,21 @@ function getGameOverMessage() {
|
|||||||
switch (reason) {
|
switch (reason) {
|
||||||
case "timeout":
|
case "timeout":
|
||||||
return winner === "w"
|
return winner === "w"
|
||||||
? "Белые выиграли по времени."
|
? "White wins on time."
|
||||||
: "Черные выиграли по времени.";
|
: "Black wins on time.";
|
||||||
|
|
||||||
case "checkmate":
|
case "checkmate":
|
||||||
return winner === "w"
|
return winner === "w"
|
||||||
? "Белые выиграли матом."
|
? "White wins by checkmate."
|
||||||
: "Черные выиграли матом.";
|
: "Black wins by checkmate.";
|
||||||
|
|
||||||
case "draw":
|
case "draw":
|
||||||
return "Ничья.";
|
return "Draw.";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
appState.gameOverMessage ||
|
appState.gameOverMessage ||
|
||||||
"Игра окончена."
|
"Game over."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -590,17 +586,17 @@ function getGameOverMessage() {
|
|||||||
function renderRoster() {
|
function renderRoster() {
|
||||||
if (appState.mode === "ai") {
|
if (appState.mode === "ai") {
|
||||||
const human =
|
const human =
|
||||||
appState.playerName || "Вы";
|
appState.playerName || "You";
|
||||||
|
|
||||||
const whiteName =
|
const whiteName =
|
||||||
appState.playerColor === "w"
|
appState.playerColor === "w"
|
||||||
? human
|
? human
|
||||||
: `Компьютер (${appState.aiLevel})`;
|
: `Computer (${appState.aiLevel})`;
|
||||||
|
|
||||||
const blackName =
|
const blackName =
|
||||||
appState.playerColor === "b"
|
appState.playerColor === "b"
|
||||||
? human
|
? human
|
||||||
: `Компьютер (${appState.aiLevel})`;
|
: `Computer (${appState.aiLevel})`;
|
||||||
|
|
||||||
if (elements.whitePlayerLabel) {
|
if (elements.whitePlayerLabel) {
|
||||||
elements.whitePlayerLabel.textContent =
|
elements.whitePlayerLabel.textContent =
|
||||||
@@ -624,7 +620,7 @@ function renderRoster() {
|
|||||||
|
|
||||||
if (elements.spectatorList) {
|
if (elements.spectatorList) {
|
||||||
elements.spectatorList.innerHTML =
|
elements.spectatorList.innerHTML =
|
||||||
"<li class='empty-state'>В режиме ИИ зрителей нет</li>";
|
"<li>None in AI mode</li>";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elements.spectatorCount) {
|
if (elements.spectatorCount) {
|
||||||
@@ -648,7 +644,7 @@ function renderRoster() {
|
|||||||
? ""
|
? ""
|
||||||
: " · offline"
|
: " · offline"
|
||||||
}`
|
}`
|
||||||
: "Свободно";
|
: "Open seat";
|
||||||
|
|
||||||
const blackName =
|
const blackName =
|
||||||
black
|
black
|
||||||
@@ -657,7 +653,7 @@ function renderRoster() {
|
|||||||
? ""
|
? ""
|
||||||
: " · offline"
|
: " · offline"
|
||||||
}`
|
}`
|
||||||
: "Свободно";
|
: "Open seat";
|
||||||
|
|
||||||
elements.whitePlayerLabel &&
|
elements.whitePlayerLabel &&
|
||||||
(elements.whitePlayerLabel.textContent =
|
(elements.whitePlayerLabel.textContent =
|
||||||
@@ -689,7 +685,7 @@ function renderRoster() {
|
|||||||
|
|
||||||
if (!spectators.length) {
|
if (!spectators.length) {
|
||||||
elements.spectatorList.innerHTML =
|
elements.spectatorList.innerHTML =
|
||||||
"<li class='empty-state'>Пока никого нет</li>";
|
"<li>None yet</li>";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -721,7 +717,7 @@ function renderPartySummary() {
|
|||||||
|
|
||||||
if (!appState.party) {
|
if (!appState.party) {
|
||||||
elements.partySummary.textContent =
|
elements.partySummary.textContent =
|
||||||
"Создайте комнату, чтобы играть белыми. Следующий игрок присоединится за черных.";
|
"Create a room to become White. The next player joins as Black. Up to 20 spectators can watch live.";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -731,18 +727,18 @@ function renderPartySummary() {
|
|||||||
|
|
||||||
const roleText =
|
const roleText =
|
||||||
role === "white"
|
role === "white"
|
||||||
? "Играете за Белых"
|
? "playing as White"
|
||||||
: role === "black"
|
: role === "black"
|
||||||
? "Играете за Черных"
|
? "playing as Black"
|
||||||
: "Зритель";
|
: "watching as a spectator";
|
||||||
|
|
||||||
const timeLabel =
|
const timeLabel =
|
||||||
appState.partySnapshot
|
appState.partySnapshot
|
||||||
?.timeControl?.label ||
|
?.timeControl?.label ||
|
||||||
"Без лимита";
|
"Без часов";
|
||||||
|
|
||||||
elements.partySummary.textContent =
|
elements.partySummary.textContent =
|
||||||
`Комната ${appState.party.code} · ${roleText}. ${timeLabel}.`;
|
`Party ${appState.party.code} · ${roleText}. ${timeLabel}.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -774,7 +770,7 @@ function renderClocks() {
|
|||||||
elements.aiTimeControl
|
elements.aiTimeControl
|
||||||
?.selectedOptions?.[0]
|
?.selectedOptions?.[0]
|
||||||
?.textContent ||
|
?.textContent ||
|
||||||
"Без лимита",
|
"Без часов",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,7 +784,7 @@ function renderClocks() {
|
|||||||
elements.whiteClock.textContent = "∞";
|
elements.whiteClock.textContent = "∞";
|
||||||
elements.whiteClock.classList.remove(
|
elements.whiteClock.classList.remove(
|
||||||
"active",
|
"active",
|
||||||
"danger"
|
"low"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,7 +792,7 @@ function renderClocks() {
|
|||||||
elements.blackClock.textContent = "∞";
|
elements.blackClock.textContent = "∞";
|
||||||
elements.blackClock.classList.remove(
|
elements.blackClock.classList.remove(
|
||||||
"active",
|
"active",
|
||||||
"danger"
|
"low"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,8 +816,8 @@ function renderClocks() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
elements.whiteClock.classList.toggle(
|
elements.whiteClock.classList.toggle(
|
||||||
"danger",
|
"low",
|
||||||
white <= 10 && white > 0
|
white <= 10
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -836,8 +832,8 @@ function renderClocks() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
elements.blackClock.classList.toggle(
|
elements.blackClock.classList.toggle(
|
||||||
"danger",
|
"low",
|
||||||
black <= 10 && black > 0
|
black <= 10
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,37 +848,31 @@ function renderConnectionBadge() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сбросим классы перед установкой
|
|
||||||
elements.connectionBadge.className = "status-chip";
|
|
||||||
|
|
||||||
if (appState.mode === "ai") {
|
if (appState.mode === "ai") {
|
||||||
elements.connectionBadge.classList.add("connection-solo");
|
|
||||||
elements.connectionBadge.textContent =
|
elements.connectionBadge.textContent =
|
||||||
appState.isAiThinking
|
appState.isAiThinking
|
||||||
? "Компьютер думает..."
|
? "Computer thinking"
|
||||||
: `Соло (${appState.aiLevel})`;
|
: `AI: ${appState.aiLevel}`;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!appState.party) {
|
if (!appState.party) {
|
||||||
elements.connectionBadge.classList.add("connection-solo");
|
|
||||||
elements.connectionBadge.textContent =
|
elements.connectionBadge.textContent =
|
||||||
"Вне комнаты";
|
"Party idle";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.connectionBadge.classList.add("connection-party");
|
|
||||||
const role =
|
const role =
|
||||||
appState.party.role === "white"
|
appState.party.role === "white"
|
||||||
? "Белые"
|
? "White"
|
||||||
: appState.party.role === "black"
|
: appState.party.role === "black"
|
||||||
? "Черные"
|
? "Black"
|
||||||
: "Зритель";
|
: "Spectator";
|
||||||
|
|
||||||
elements.connectionBadge.textContent =
|
elements.connectionBadge.textContent =
|
||||||
`Комната ${appState.party.code} · ${role}`;
|
`Party ${appState.party.code} · ${role}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -901,13 +891,7 @@ function render() {
|
|||||||
|
|
||||||
if (elements.turnBadge) {
|
if (elements.turnBadge) {
|
||||||
elements.turnBadge.textContent =
|
elements.turnBadge.textContent =
|
||||||
appState.engine.state.turn === "w" ? "Ход белых" : "Ход черных";
|
status;
|
||||||
|
|
||||||
// Меняем класс цвета бейджа
|
|
||||||
elements.turnBadge.className =
|
|
||||||
appState.engine.state.turn === "w"
|
|
||||||
? "status-chip turn-white"
|
|
||||||
: "status-chip turn-black";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elements.statusMessage) {
|
if (elements.statusMessage) {
|
||||||
@@ -924,12 +908,6 @@ function render() {
|
|||||||
"hidden",
|
"hidden",
|
||||||
!appState.party
|
!appState.party
|
||||||
);
|
);
|
||||||
|
|
||||||
// Показываем кнопку "Новая игра" в мультиплеере только если мы в комнате (и опционально, если игра окончена)
|
|
||||||
elements.newPartyGameButton?.classList.toggle(
|
|
||||||
"hidden",
|
|
||||||
!appState.party
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1035,7 +1013,7 @@ async function submitMove(move) {
|
|||||||
|
|
||||||
showToast(
|
showToast(
|
||||||
response.error ||
|
response.error ||
|
||||||
"Ход отклонен.",
|
"Move rejected.",
|
||||||
"error"
|
"error"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1061,7 +1039,7 @@ async function submitMove(move) {
|
|||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
showToast(
|
showToast(
|
||||||
result.error ||
|
result.error ||
|
||||||
"Недопустимый ход.",
|
"Illegal move.",
|
||||||
"error"
|
"error"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1326,8 +1304,8 @@ function updateAiClock() {
|
|||||||
|
|
||||||
appState.gameOverMessage =
|
appState.gameOverMessage =
|
||||||
color === "w"
|
color === "w"
|
||||||
? "Черные выиграли по времени."
|
? "Black wins on time."
|
||||||
: "Белые выиграли по времени.";
|
: "White wins on time.";
|
||||||
|
|
||||||
stopAiClock();
|
stopAiClock();
|
||||||
|
|
||||||
@@ -1399,32 +1377,6 @@ function resetLocalGame() {
|
|||||||
|
|
||||||
startAiClock();
|
startAiClock();
|
||||||
scheduleAiTurn();
|
scheduleAiTurn();
|
||||||
render();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* =========================================================
|
|
||||||
NEW GAME HANDLER (Добавлено)
|
|
||||||
========================================================= */
|
|
||||||
|
|
||||||
async function startNewGame() {
|
|
||||||
if (appState.mode === "ai") {
|
|
||||||
resetLocalGame();
|
|
||||||
showToast("Новая игра с ИИ начата!", "success");
|
|
||||||
} else if (appState.mode === "party" && appState.party) {
|
|
||||||
// Если вы играете с другом, мы отправляем запрос на сервер,
|
|
||||||
// чтобы он сбросил состояние комнаты. (Предполагается, что такой API существует)
|
|
||||||
const response = await postJson("/api/party/restart", {
|
|
||||||
partyCode: appState.party.code,
|
|
||||||
clientId: appState.party.clientId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
showToast(response.error || "Не удалось перезапустить игру на сервере.", "error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showToast("Новая игра в комнате началась!", "success");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1629,7 +1581,7 @@ function connectPartyStream() {
|
|||||||
elements.connectionBadge
|
elements.connectionBadge
|
||||||
) {
|
) {
|
||||||
elements.connectionBadge.textContent =
|
elements.connectionBadge.textContent =
|
||||||
`Комната ${code} · переподключение...`;
|
`Party ${code} · reconnecting`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1672,7 +1624,7 @@ async function createParty() {
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
showToast(
|
showToast(
|
||||||
response.error ||
|
response.error ||
|
||||||
"Не удалось создать комнату.",
|
"Unable to create party.",
|
||||||
"error"
|
"error"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1692,7 +1644,7 @@ async function createParty() {
|
|||||||
connectPartyStream();
|
connectPartyStream();
|
||||||
|
|
||||||
showToast(
|
showToast(
|
||||||
`Комната ${response.party.code} создана.`,
|
`Party ${response.party.code} created.`,
|
||||||
"success"
|
"success"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1710,7 +1662,7 @@ async function joinParty(code) {
|
|||||||
|
|
||||||
if (!normalizedCode) {
|
if (!normalizedCode) {
|
||||||
showToast(
|
showToast(
|
||||||
"Сначала введите код комнаты.",
|
"Enter a party code first.",
|
||||||
"error"
|
"error"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1722,7 +1674,7 @@ async function joinParty(code) {
|
|||||||
normalizedCode
|
normalizedCode
|
||||||
) {
|
) {
|
||||||
showToast(
|
showToast(
|
||||||
`Вы уже находитесь в комнате ${normalizedCode}.`
|
`You are already in party ${normalizedCode}.`
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1754,7 +1706,7 @@ async function joinParty(code) {
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
showToast(
|
showToast(
|
||||||
response.error ||
|
response.error ||
|
||||||
"Не удалось войти в комнату.",
|
"Unable to join party.",
|
||||||
"error"
|
"error"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1774,7 +1726,7 @@ async function joinParty(code) {
|
|||||||
connectPartyStream();
|
connectPartyStream();
|
||||||
|
|
||||||
showToast(
|
showToast(
|
||||||
`Вы присоединились к комнате ${response.party.code}.`,
|
`Joined party ${response.party.code}.`,
|
||||||
"success"
|
"success"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1822,7 +1774,7 @@ async function leaveParty(
|
|||||||
|
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
showToast(
|
showToast(
|
||||||
"Вы покинули комнату."
|
"Left the party room."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1850,12 +1802,12 @@ async function copyInviteLink() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
showToast(
|
showToast(
|
||||||
"Ссылка-инвайт скопирована!",
|
"Invite link copied.",
|
||||||
"success"
|
"success"
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
showToast(
|
showToast(
|
||||||
`Код комнаты: ${appState.party.code}`
|
`Party code: ${appState.party.code}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1920,16 +1872,12 @@ function removeStoredPartyId(code) {
|
|||||||
========================================================= */
|
========================================================= */
|
||||||
|
|
||||||
function bindEvents() {
|
function bindEvents() {
|
||||||
// ПРИВЯЗКА КНОПОК НОВОЙ ИГРЫ
|
|
||||||
elements.newGameToolbarButton?.addEventListener("click", startNewGame);
|
|
||||||
elements.newPartyGameButton?.addEventListener("click", startNewGame);
|
|
||||||
|
|
||||||
elements.playerNameInput?.addEventListener(
|
elements.playerNameInput?.addEventListener(
|
||||||
"input",
|
"input",
|
||||||
event => {
|
event => {
|
||||||
appState.playerName =
|
appState.playerName =
|
||||||
event.target.value.trim() ||
|
event.target.value.trim() ||
|
||||||
"Гость";
|
"Guest";
|
||||||
|
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
STORAGE_KEYS.name,
|
STORAGE_KEYS.name,
|
||||||
@@ -2012,7 +1960,7 @@ function bindEvents() {
|
|||||||
resetLocalGame();
|
resetLocalGame();
|
||||||
|
|
||||||
showToast(
|
showToast(
|
||||||
"Новая игра с ИИ началась.",
|
"New AI game ready.",
|
||||||
"success"
|
"success"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2166,3 +2114,4 @@ async function init() {
|
|||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user