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;