183 lines
6.0 KiB
JavaScript
183 lines
6.0 KiB
JavaScript
import Phaser from "phaser";
|
|
import { drawArena } from "./arenaRenderer.js";
|
|
import { clearCombatObjects, updateFighter } from "./combat.js";
|
|
import { ARENA_SIZE } from "./config.js";
|
|
import { createFighterAnimations, preloadFighterSheets } from "./fighterAssets.js";
|
|
import { createFighter, syncFighterHud } from "./fighterFactory.js";
|
|
import { fighterManifest } from "./fighterManifest.js";
|
|
import { pickFighters } from "./fighterSelection.js";
|
|
import { createMatchSetup, matchStatusText } from "./matchSetup.js";
|
|
|
|
export class ArenaScene extends Phaser.Scene {
|
|
constructor({ getInitialMatchConfig, setStatus }) {
|
|
super("arena");
|
|
this.fighters = [];
|
|
this.getInitialMatchConfig = getInitialMatchConfig;
|
|
this.matchId = 0;
|
|
this.matchOver = false;
|
|
this.ready = false;
|
|
this.setStatus = (message) => {
|
|
// 기존 배너 제거
|
|
const oldBanner = document.querySelector(".victory-banner");
|
|
if (oldBanner) oldBanner.remove();
|
|
|
|
// 승리 또는 무승부 메시지인 경우 전용 배너 생성
|
|
if (message.includes("승리") || message.includes("무승부")) {
|
|
const banner = document.createElement("div");
|
|
banner.className = "victory-banner";
|
|
banner.textContent = message;
|
|
document.querySelector(".arena-shell").appendChild(banner);
|
|
}
|
|
};
|
|
this.teams = [];
|
|
}
|
|
|
|
preload() {
|
|
preloadFighterSheets(this, fighterManifest);
|
|
}
|
|
|
|
create() {
|
|
this.physics.world.setBounds(0, 0, ARENA_SIZE, ARENA_SIZE);
|
|
this.cameras.main.setBounds(0, 0, ARENA_SIZE, ARENA_SIZE);
|
|
this.cameras.main.setBackgroundColor("#282819");
|
|
drawArena(this);
|
|
createFighterAnimations(this, fighterManifest);
|
|
|
|
// 미니맵 카메라 설정
|
|
this.minimapCamera = this.cameras.add(10, 10, 150, 150).setZoom(150 / ARENA_SIZE).setName('minimap');
|
|
this.minimapCamera.setBackgroundColor(0x000000);
|
|
this.minimapCamera.scrollX = 0;
|
|
this.minimapCamera.scrollY = 0;
|
|
this.minimapCamera.setAlpha(0); // 기본적으로는 숨김
|
|
|
|
// 마우스 휠로 줌 조절
|
|
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
|
|
const zoomStep = 0.1;
|
|
const newZoom = Phaser.Math.Clamp(this.cameras.main.zoom + (deltaY > 0 ? -zoomStep : zoomStep), 1, 3);
|
|
this.cameras.main.setZoom(newZoom);
|
|
|
|
// 확대 시 미니맵 표시
|
|
this.minimapCamera.setAlpha(newZoom > 1 ? 0.8 : 0);
|
|
});
|
|
|
|
this.ready = true;
|
|
this.startMatch(this.getInitialMatchConfig());
|
|
}
|
|
|
|
startMatch({ names = [], teamSize } = {}) {
|
|
if (!this.ready) {
|
|
return;
|
|
}
|
|
|
|
if (names.length < 2) {
|
|
this.setStatus("참가자 닉네임을 2명 이상 입력하세요.");
|
|
return;
|
|
}
|
|
|
|
const matchSetup = createMatchSetup(names, teamSize);
|
|
const matchSkins = pickFighters(fighterManifest, matchSetup.fighters.length);
|
|
|
|
this.matchId += 1;
|
|
this.matchOver = false;
|
|
clearCombatObjects(this);
|
|
this.fighters.forEach((fighter) => fighter.destroy());
|
|
this.teams = matchSetup.teams;
|
|
this.fighters = matchSetup.fighters.map((fighterSetup, index) =>
|
|
createFighter(this, {
|
|
...fighterSetup,
|
|
skin: matchSkins[index],
|
|
}),
|
|
);
|
|
|
|
this.setStatus(matchStatusText(this.teams));
|
|
this.updateScoreboard();
|
|
}
|
|
update(time) {
|
|
this.fighters.forEach(syncFighterHud);
|
|
|
|
if (this.matchOver) {
|
|
return;
|
|
}
|
|
|
|
// 확대 상태일 때 생존 캐릭터들의 중앙으로 카메라 이동
|
|
if (this.cameras.main.zoom > 1) {
|
|
const aliveFighters = this.fighters.filter(f => !f.isDead);
|
|
if (aliveFighters.length > 0) {
|
|
const avgX = aliveFighters.reduce((sum, f) => sum + f.x, 0) / aliveFighters.length;
|
|
const avgY = aliveFighters.reduce((sum, f) => sum + f.y, 0) / aliveFighters.length;
|
|
|
|
// 소수점 단위 변동으로 인한 지터링 방지를 위해 반올림 처리 및 부드러운 이동(Lerp) 적용
|
|
const targetX = Math.round(avgX);
|
|
const targetY = Math.round(avgY);
|
|
|
|
// 현재 카메라 위치에서 목표 위치로 서서히 이동 (0.1은 따라가는 속도)
|
|
this.cameras.main.scrollX += (targetX - this.cameras.main.centerX) * 0.1;
|
|
this.cameras.main.scrollY += (targetY - this.cameras.main.centerY) * 0.1;
|
|
}
|
|
} else {
|
|
// 줌이 1일 때는 경기장 중앙에 고정
|
|
this.cameras.main.centerOn(ARENA_SIZE / 2, ARENA_SIZE / 2);
|
|
}
|
|
|
|
this.fighters.forEach((fighter) => {
|
|
updateFighter(this, fighter, time, () => {
|
|
this.updateScoreboard();
|
|
this.finishMatch();
|
|
});
|
|
});
|
|
}
|
|
|
|
updateScoreboard() {
|
|
const scoreLeft = document.getElementById("score-left");
|
|
const scoreRight = document.getElementById("score-right");
|
|
|
|
if (!scoreLeft || !scoreRight) return;
|
|
|
|
scoreLeft.innerHTML = "";
|
|
scoreRight.innerHTML = "";
|
|
|
|
this.teams.forEach((team, index) => {
|
|
const aliveCount = this.fighters.filter(
|
|
(f) => f.team.id === team.id && !f.isDead
|
|
).length;
|
|
|
|
const teamEl = document.createElement("div");
|
|
teamEl.className = "team-score";
|
|
teamEl.style.backgroundColor = `${team.color}44`; // 44 is alpha for 26%
|
|
teamEl.style.borderLeft = `4px solid ${team.color}`;
|
|
teamEl.innerHTML = `<span>${team.label}</span> <span>${aliveCount}</span>`;
|
|
|
|
if (index % 2 === 0) {
|
|
scoreLeft.appendChild(teamEl);
|
|
} else {
|
|
scoreRight.appendChild(teamEl);
|
|
}
|
|
});
|
|
}
|
|
|
|
finishMatch() {
|
|
const livingFighters = this.fighters.filter((fighter) => !fighter.isDead);
|
|
const livingTeams = new Set(livingFighters.map((fighter) => fighter.team.id));
|
|
|
|
if (livingTeams.size > 1) {
|
|
return;
|
|
}
|
|
|
|
this.matchOver = true;
|
|
clearCombatObjects(this);
|
|
this.fighters.forEach((fighter) => {
|
|
if (fighter.body) {
|
|
fighter.body.setVelocity(0, 0);
|
|
}
|
|
});
|
|
|
|
if (livingTeams.size === 1) {
|
|
const winningTeamId = Array.from(livingTeams)[0];
|
|
const winningTeam = this.teams.find((team) => team.id === winningTeamId);
|
|
this.setStatus(`${winningTeam?.label ?? "Unknown"} 승리!`);
|
|
} else {
|
|
this.setStatus("무승부!");
|
|
}
|
|
}
|
|
}
|