arena/src/main.js

221 lines
5.8 KiB
JavaScript

import Phaser from "phaser";
import { ArenaScene } from "./game/arena/ArenaScene.js";
import {
ARENA_SIZE,
PRESENTATION_TEAM_COUNT,
PRESENTATION_TEAM_SIZE,
} from "./constants.js";
import { createMatchForm } from "./ui/matchForm.js";
import { createAboutDialog } from "./ui/aboutDialog.js";
import { trackVisitor } from "./ui/visitorCounter.js";
const matchForm = createMatchForm();
const aboutDialog = createAboutDialog();
const appNode = document.querySelector("#app");
const startButton = document.querySelector("#start-button");
const drawer = document.querySelector("#fighter-entry");
const drawerCloseButton = document.querySelector("#drawer-close");
const drawerScrim = document.querySelector("#drawer-scrim");
const drawerToggleButton = document.querySelector("#drawer-toggle");
const playerNamesInput = document.querySelector("#player-names");
const pauseButton = document.querySelector("#pause-button");
const restartButton = document.querySelector("#restart-button");
const MOBILE_MATCH_MEDIA_QUERY = "(max-width: 960px)";
function isMatchLive() {
return appNode?.classList.contains("match-live") ?? false;
}
function openOptionsDrawer({ focus = true } = {}) {
appNode?.classList.add("options-open");
setDrawerCollapsed(false);
resetDrawerScroll();
drawer?.setAttribute("aria-hidden", "false");
startButton?.setAttribute("aria-expanded", "true");
if (focus) {
window.setTimeout(() => playerNamesInput?.focus(), 220);
}
}
function closeOptionsDrawer() {
if (isMatchLive()) {
setDrawerCollapsed(true);
return;
}
appNode?.classList.remove("options-open");
appNode?.classList.remove("drawer-collapsed");
drawer?.setAttribute("aria-hidden", "true");
startButton?.setAttribute("aria-expanded", "false");
syncDrawerToggleButton();
}
function startConfiguredMatch(matchConfig) {
if (matchConfig.names.length < 2) {
matchForm.setStatus("참가자 닉네임을 2명 이상 입력하세요");
return;
}
appNode?.classList.remove("match-ended");
appNode?.classList.add("match-live");
if (shouldCompactOptionsDrawer()) {
setDrawerCollapsed(true);
} else {
openOptionsDrawer({ focus: false });
}
arenaScene.startMatch(matchConfig);
syncPauseButton();
}
function getPresentationMatchConfig() {
return {
names: Array.from({ length: PRESENTATION_TEAM_COUNT }, (_, index) => `Player ${index + 1}`),
teamSize: PRESENTATION_TEAM_SIZE,
};
}
function setDrawerCollapsed(collapsed) {
const nextCollapsed = Boolean(collapsed) && isMatchLive();
appNode?.classList.toggle("drawer-collapsed", nextCollapsed);
drawer?.setAttribute("aria-hidden", "false");
if (!nextCollapsed) {
resetDrawerScroll();
}
syncDrawerToggleButton();
}
function syncDrawerToggleButton() {
if (!drawerToggleButton) {
return;
}
const isCollapsed = appNode?.classList.contains("drawer-collapsed") ?? false;
drawerToggleButton.textContent = isCollapsed ? "옵션 펼치기" : "옵션 접기";
drawerToggleButton.setAttribute("aria-expanded", String(!isCollapsed));
}
function syncPauseButton() {
if (!pauseButton) {
return;
}
const isPaused = arenaScene.isMatchPaused();
appNode?.classList.toggle("match-paused", isPaused);
pauseButton.textContent = isPaused ? "계속" : "일시정지";
pauseButton.setAttribute("aria-pressed", String(isPaused));
}
function shouldCompactOptionsDrawer() {
return window.matchMedia?.(MOBILE_MATCH_MEDIA_QUERY).matches ?? window.innerWidth <= 960;
}
function resetDrawerScroll() {
if (drawer) {
drawer.scrollTop = 0;
}
}
function handleMatchEnd() {
appNode?.classList.add("match-ended");
setDrawerCollapsed(true);
syncPauseButton();
}
function revealAppWhenStylesAreReady() {
const stylesheet = document.querySelector('link[data-app-styles], link[rel="stylesheet"]');
const reveal = () => {
window.requestAnimationFrame(() => {
document.documentElement.classList.remove("app-booting");
});
};
if (!stylesheet || stylesheet.sheet) {
reveal();
return;
}
stylesheet.addEventListener("load", reveal, { once: true });
}
startButton?.addEventListener("click", openOptionsDrawer);
drawerCloseButton?.addEventListener("click", closeOptionsDrawer);
drawerScrim?.addEventListener("click", closeOptionsDrawer);
drawerToggleButton?.addEventListener("click", () => {
const isCollapsed = appNode?.classList.contains("drawer-collapsed") ?? false;
setDrawerCollapsed(!isCollapsed);
});
pauseButton?.addEventListener("click", () => {
arenaScene.togglePause();
syncPauseButton();
});
restartButton?.addEventListener("click", () => {
startConfiguredMatch(matchForm.readMatchConfig());
});
window.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
if (aboutDialog?.isOpen()) {
return;
}
closeOptionsDrawer();
}
});
const arenaScene = new ArenaScene({
getInitialMatchConfig: getPresentationMatchConfig,
onMatchEnd: handleMatchEnd,
setStatus: matchForm.setStatus,
});
const game = new Phaser.Game({
type: Phaser.AUTO,
parent: "game",
width: ARENA_SIZE,
height: ARENA_SIZE,
pixelArt: true,
backgroundColor: "#282819",
physics: {
default: "arcade",
arcade: {
debug: false,
},
},
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
},
scene: arenaScene,
});
revealAppWhenStylesAreReady();
matchForm.onSubmit((matchConfig) => {
startConfiguredMatch(matchConfig);
});
const visitorCountNode = document.querySelector("#visitor-count");
trackVisitor({
onUpdate({ uniqueVisitors }) {
if (visitorCountNode) {
visitorCountNode.textContent = `방문자 ${uniqueVisitors.toLocaleString("ko-KR")}`;
}
},
onError(error) {
console.warn(error);
if (visitorCountNode) {
visitorCountNode.textContent = "";
visitorCountNode.hidden = true;
}
},
});
window.arenaGame = game;