import Phaser from 'phaser'; import { firstBattleMap, firstBattleUnits, type TerrainType, type UnitData } from '../data/scenario'; import { palette } from '../ui/palette'; const tileSize = 48; const mapOffset = { x: 64, y: 72 }; const terrainColor: Record = { plain: 0x6d8056, forest: 0x355f42, hill: 0x82724e, fort: 0x8b6f48 }; export class BattleScene extends Phaser.Scene { private selectedUnit?: UnitData; private infoText?: Phaser.GameObjects.Text; private turnText?: Phaser.GameObjects.Text; private markers: Phaser.GameObjects.Rectangle[] = []; constructor() { super('BattleScene'); } create() { const { width, height } = this.scale; this.add.rectangle(0, 0, width, height, 0x0f1418).setOrigin(0); this.drawMap(); this.drawUnits(); this.drawSidePanel(width, height); this.setInfo('아군 차례입니다. 유비를 선택해 이동 범위를 확인하세요.'); } private drawMap() { firstBattleMap.terrain.forEach((row, y) => { row.forEach((terrain, x) => { const tileX = mapOffset.x + x * tileSize; const tileY = mapOffset.y + y * tileSize; const tile = this.add.rectangle(tileX, tileY, tileSize, tileSize, terrainColor[terrain]).setOrigin(0); tile.setStrokeStyle(1, 0x182028, 0.8); }); }); } private drawUnits() { firstBattleUnits.forEach((unit) => { const sprite = this.add.image( mapOffset.x + unit.x * tileSize + tileSize / 2, mapOffset.y + unit.y * tileSize + tileSize / 2, unit.faction === 'ally' ? 'unit-ally' : 'unit-enemy' ); sprite.setInteractive({ useHandCursor: unit.faction === 'ally' }); sprite.on('pointerdown', () => this.selectUnit(unit)); this.add.text(sprite.x - 22, sprite.y + 22, unit.name, { fontSize: '13px', color: unit.faction === 'ally' ? '#e7edf7' : '#ffebe7', backgroundColor: '#10131a', padding: { left: 4, right: 4, top: 2, bottom: 2 } }); }); } private drawSidePanel(width: number, height: number) { const panelX = mapOffset.x + firstBattleMap.width * tileSize + 36; this.add.rectangle(panelX, 72, width - panelX - 64, height - 128, palette.panel, 0.96).setOrigin(0); this.add.text(panelX + 30, 108, '첫 전투', { fontSize: '32px', color: '#e8dfca', fontStyle: '700' }); this.turnText = this.add.text(panelX + 30, 164, '1턴 / 아군', { fontSize: '22px', color: '#d8b15f' }); this.infoText = this.add.text(panelX + 30, 224, '', { fontSize: '20px', color: '#e8dfca', wordWrap: { width: width - panelX - 124, useAdvancedWrap: true }, lineSpacing: 8 }); this.add.text(panelX + 30, height - 186, '승리 조건: 적 전멸', { fontSize: '18px', color: '#9aa3ad' }); this.add.text(panelX + 30, height - 148, '패배 조건: 유비 퇴각', { fontSize: '18px', color: '#9aa3ad' }); this.add.text(panelX + 30, height - 110, '다음 단계: 이동/공격/AI 구현', { fontSize: '18px', color: '#9aa3ad' }); this.turnText.setText('1턴 / 아군'); } private selectUnit(unit: UnitData) { if (unit.faction !== 'ally') { this.setInfo(`${unit.name}: ${unit.className}, HP ${unit.hp}/${unit.maxHp}`); return; } this.selectedUnit = unit; this.clearMarkers(); this.showMoveRange(unit); this.setInfo(`${unit.name} (${unit.className})\nHP ${unit.hp}/${unit.maxHp}, 공격 ${unit.attack}, 이동 ${unit.move}`); } private showMoveRange(unit: UnitData) { for (let y = 0; y < firstBattleMap.height; y += 1) { for (let x = 0; x < firstBattleMap.width; x += 1) { const distance = Math.abs(unit.x - x) + Math.abs(unit.y - y); if (distance > 0 && distance <= unit.move) { const marker = this.add.rectangle( mapOffset.x + x * tileSize, mapOffset.y + y * tileSize, tileSize, tileSize, palette.blue, 0.24 ); marker.setOrigin(0); marker.setStrokeStyle(1, palette.blue, 0.6); this.markers.push(marker); } } } } private clearMarkers() { this.markers.forEach((marker) => marker.destroy()); this.markers = []; } private setInfo(text: string) { this.infoText?.setText(text); } }