Add roster and unit stat panels
This commit is contained in:
@@ -19,10 +19,19 @@ export type UnitData = {
|
||||
maxHp: number;
|
||||
attack: number;
|
||||
move: number;
|
||||
stats: UnitStats;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export type UnitStats = {
|
||||
might: number;
|
||||
intelligence: number;
|
||||
leadership: number;
|
||||
agility: number;
|
||||
luck: number;
|
||||
};
|
||||
|
||||
export type TerrainType = 'plain' | 'forest' | 'hill' | 'fort';
|
||||
|
||||
export type BattleMap = {
|
||||
@@ -138,11 +147,95 @@ export const firstBattleMap: BattleMap = {
|
||||
};
|
||||
|
||||
export const firstBattleUnits: UnitData[] = [
|
||||
{ id: 'liu-bei', name: '유비', faction: 'ally', className: '의병장', hp: 32, maxHp: 32, attack: 9, move: 4, x: 1, y: 9 },
|
||||
{ id: 'guan-yu', name: '관우', faction: 'ally', className: '보병', hp: 30, maxHp: 30, attack: 10, move: 3, x: 2, y: 10 },
|
||||
{ id: 'zhang-fei', name: '장비', faction: 'ally', className: '창병', hp: 30, maxHp: 30, attack: 10, move: 3, x: 1, y: 10 },
|
||||
{ id: 'rebel-a', name: '황건병', faction: 'enemy', className: '도적', hp: 20, maxHp: 20, attack: 6, move: 3, x: 8, y: 2 },
|
||||
{ id: 'rebel-b', name: '황건병', faction: 'enemy', className: '도적', hp: 20, maxHp: 20, attack: 6, move: 3, x: 9, y: 3 },
|
||||
{ id: 'rebel-c', name: '황건궁병', faction: 'enemy', className: '궁병', hp: 18, maxHp: 18, attack: 7, move: 3, x: 10, y: 2 },
|
||||
{ id: 'rebel-leader', name: '두령 한석', faction: 'enemy', className: '두령', hp: 30, maxHp: 30, attack: 9, move: 3, x: 10, y: 4 }
|
||||
{
|
||||
id: 'liu-bei',
|
||||
name: '유비',
|
||||
faction: 'ally',
|
||||
className: '의병장',
|
||||
hp: 32,
|
||||
maxHp: 32,
|
||||
attack: 9,
|
||||
move: 4,
|
||||
stats: { might: 76, intelligence: 78, leadership: 84, agility: 70, luck: 88 },
|
||||
x: 1,
|
||||
y: 9
|
||||
},
|
||||
{
|
||||
id: 'guan-yu',
|
||||
name: '관우',
|
||||
faction: 'ally',
|
||||
className: '보병',
|
||||
hp: 30,
|
||||
maxHp: 30,
|
||||
attack: 10,
|
||||
move: 3,
|
||||
stats: { might: 96, intelligence: 74, leadership: 91, agility: 72, luck: 68 },
|
||||
x: 2,
|
||||
y: 10
|
||||
},
|
||||
{
|
||||
id: 'zhang-fei',
|
||||
name: '장비',
|
||||
faction: 'ally',
|
||||
className: '창병',
|
||||
hp: 30,
|
||||
maxHp: 30,
|
||||
attack: 10,
|
||||
move: 3,
|
||||
stats: { might: 95, intelligence: 42, leadership: 83, agility: 67, luck: 61 },
|
||||
x: 1,
|
||||
y: 10
|
||||
},
|
||||
{
|
||||
id: 'rebel-a',
|
||||
name: '황건병',
|
||||
faction: 'enemy',
|
||||
className: '도적',
|
||||
hp: 20,
|
||||
maxHp: 20,
|
||||
attack: 6,
|
||||
move: 3,
|
||||
stats: { might: 49, intelligence: 34, leadership: 38, agility: 45, luck: 40 },
|
||||
x: 8,
|
||||
y: 2
|
||||
},
|
||||
{
|
||||
id: 'rebel-b',
|
||||
name: '황건병',
|
||||
faction: 'enemy',
|
||||
className: '도적',
|
||||
hp: 20,
|
||||
maxHp: 20,
|
||||
attack: 6,
|
||||
move: 3,
|
||||
stats: { might: 47, intelligence: 32, leadership: 36, agility: 48, luck: 38 },
|
||||
x: 9,
|
||||
y: 3
|
||||
},
|
||||
{
|
||||
id: 'rebel-c',
|
||||
name: '황건궁병',
|
||||
faction: 'enemy',
|
||||
className: '궁병',
|
||||
hp: 18,
|
||||
maxHp: 18,
|
||||
attack: 7,
|
||||
move: 3,
|
||||
stats: { might: 52, intelligence: 40, leadership: 43, agility: 56, luck: 42 },
|
||||
x: 10,
|
||||
y: 2
|
||||
},
|
||||
{
|
||||
id: 'rebel-leader',
|
||||
name: '두령 한석',
|
||||
faction: 'enemy',
|
||||
className: '두령',
|
||||
hp: 30,
|
||||
maxHp: 30,
|
||||
attack: 9,
|
||||
move: 3,
|
||||
stats: { might: 64, intelligence: 46, leadership: 61, agility: 52, luck: 50 },
|
||||
x: 10,
|
||||
y: 4
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Phaser from 'phaser';
|
||||
import { soundDirector } from '../audio/SoundDirector';
|
||||
import { firstBattleMap, firstBattleUnits, type TerrainType, type UnitData } from '../data/scenario';
|
||||
import { firstBattleMap, firstBattleUnits, type TerrainType, type UnitData, type UnitStats } from '../data/scenario';
|
||||
import { palette } from '../ui/palette';
|
||||
|
||||
const terrainColor: Record<TerrainType, number> = {
|
||||
@@ -42,6 +42,7 @@ type UnitView = {
|
||||
|
||||
type BattlePhase = 'idle' | 'moving' | 'command';
|
||||
type BattleCommand = 'attack' | 'strategy' | 'item' | 'wait';
|
||||
type RosterTab = 'ally' | 'enemy';
|
||||
|
||||
type CommandButton = {
|
||||
command: BattleCommand;
|
||||
@@ -64,13 +65,27 @@ const commandLabels: Record<BattleCommand, string> = {
|
||||
wait: '대기'
|
||||
};
|
||||
|
||||
const rosterLabels: Record<RosterTab, string> = {
|
||||
ally: '아군',
|
||||
enemy: '적군'
|
||||
};
|
||||
|
||||
const statLabels: Array<{ key: keyof UnitStats; label: string }> = [
|
||||
{ key: 'might', label: '무력' },
|
||||
{ key: 'intelligence', label: '지력' },
|
||||
{ key: 'leadership', label: '통솔력' },
|
||||
{ key: 'agility', label: '민첩성' },
|
||||
{ key: 'luck', label: '운' }
|
||||
];
|
||||
|
||||
export class BattleScene extends Phaser.Scene {
|
||||
private layout!: BattleLayout;
|
||||
private phase: BattlePhase = 'idle';
|
||||
private selectedUnit?: UnitData;
|
||||
private infoText?: Phaser.GameObjects.Text;
|
||||
private turnText?: Phaser.GameObjects.Text;
|
||||
private markers: Phaser.GameObjects.Rectangle[] = [];
|
||||
private rosterTab: RosterTab = 'ally';
|
||||
private sidePanelObjects: Phaser.GameObjects.GameObject[] = [];
|
||||
private commandButtons: CommandButton[] = [];
|
||||
private commandMenuObjects: Array<Phaser.GameObjects.Rectangle | Phaser.GameObjects.Text> = [];
|
||||
private unitViews = new Map<string, UnitView>();
|
||||
@@ -96,7 +111,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.drawMap();
|
||||
this.drawUnits();
|
||||
this.drawSidePanel();
|
||||
this.setInfo('아군 차례입니다.\n행동할 장수를 선택한 뒤 파란 칸으로 이동하세요.');
|
||||
this.renderRosterPanel('ally', '행동할 장수를 선택하세요.');
|
||||
}
|
||||
|
||||
private createLayout(width: number, height: number): BattleLayout {
|
||||
@@ -214,25 +229,17 @@ export class BattleScene extends Phaser.Scene {
|
||||
fontSize: '22px',
|
||||
color: '#d8b15f'
|
||||
});
|
||||
this.infoText = this.add.text(panelX + 24, panelY + 144, '', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '20px',
|
||||
color: '#e8dfca',
|
||||
wordWrap: { width: panelWidth - 48, useAdvancedWrap: true },
|
||||
lineSpacing: 8
|
||||
});
|
||||
|
||||
this.add.text(panelX + 24, panelY + panelHeight - 166, '승리 조건: 적 전멸', {
|
||||
this.add.text(panelX + 24, panelY + panelHeight - 104, '승리 조건: 적 전멸', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '18px',
|
||||
color: '#9aa3ad'
|
||||
});
|
||||
this.add.text(panelX + 24, panelY + panelHeight - 126, '패배 조건: 유비 퇴각', {
|
||||
this.add.text(panelX + 24, panelY + panelHeight - 72, '패배 조건: 유비 퇴각', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '18px',
|
||||
color: '#9aa3ad'
|
||||
});
|
||||
this.add.text(panelX + 24, panelY + panelHeight - 86, '이동 후 명령: 공격 / 책략 / 도구 / 대기', {
|
||||
this.add.text(panelX + 24, panelY + panelHeight - 40, '이동 후 명령: 공격 / 책략 / 도구 / 대기', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '18px',
|
||||
color: '#9aa3ad'
|
||||
@@ -246,7 +253,12 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
if (unit.faction !== 'ally') {
|
||||
this.setInfo(`${unit.name}: ${unit.className}\nHP ${unit.hp}/${unit.maxHp}`);
|
||||
this.selectedUnit = undefined;
|
||||
this.pendingMove = undefined;
|
||||
this.phase = 'idle';
|
||||
this.clearMarkers();
|
||||
this.hideCommandMenu();
|
||||
this.renderUnitDetail(unit, '적 부대 정보입니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -255,7 +267,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.clearMarkers();
|
||||
this.hideCommandMenu();
|
||||
this.phase = 'idle';
|
||||
this.setInfo(`${unit.name}은 이미 행동을 마쳤습니다.\n흑백으로 표시된 장수는 이번 턴에 다시 움직일 수 없습니다.`);
|
||||
this.renderUnitDetail(unit, '이미 행동을 마친 장수입니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,7 +277,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.clearMarkers();
|
||||
this.hideCommandMenu();
|
||||
this.showMoveRange(unit);
|
||||
this.setInfo(`${unit.name} (${unit.className})\nHP ${unit.hp}/${unit.maxHp}\n공격 ${unit.attack} / 이동 ${unit.move}\n이동할 파란 칸을 선택하세요.`);
|
||||
this.renderUnitDetail(unit, '이동할 파란 칸을 선택하세요.');
|
||||
}
|
||||
|
||||
private showMoveRange(unit: UnitData) {
|
||||
@@ -323,7 +335,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
const targetY = this.tileCenterY(y);
|
||||
this.moveUnitView(unit, x, y, view);
|
||||
this.showCommandMenu(unit, pointer?.x ?? targetX, pointer?.y ?? targetY);
|
||||
this.setInfo(`${unit.name} 이동 완료\n위치: ${x + 1}, ${y + 1}\n공격, 책략, 도구, 대기 중 하나를 선택하세요.\n우클릭하면 이동을 취소합니다.`);
|
||||
this.renderUnitDetail(unit, '공격, 책략, 도구, 대기 중 하나를 선택하세요.\n우클릭하면 이동을 취소합니다.');
|
||||
}
|
||||
|
||||
private canMoveTo(unit: UnitData, x: number, y: number) {
|
||||
@@ -470,7 +482,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
soundDirector.playSelect();
|
||||
|
||||
const actionMessage = this.commandResultMessage(unit, command);
|
||||
this.setInfo(`${actionMessage}\n\n${this.remainingAllyCount()}명의 아군이 아직 행동할 수 있습니다.`);
|
||||
this.renderRosterPanel('ally', `${actionMessage}\n${this.remainingAllyCount()}명의 아군이 아직 행동할 수 있습니다.`);
|
||||
}
|
||||
|
||||
private commandResultMessage(unit: UnitData, command: BattleCommand) {
|
||||
@@ -508,7 +520,10 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.moveUnitView(unit, fromX, fromY, view, 180);
|
||||
this.clearMarkers();
|
||||
this.showMoveRange(unit);
|
||||
this.setInfo(`${unit.name} 이동 취소\n${toX + 1}, ${toY + 1}에서 원래 위치 ${fromX + 1}, ${fromY + 1}로 되돌렸습니다.\n다시 이동할 파란 칸을 선택하세요.`);
|
||||
this.renderUnitDetail(
|
||||
unit,
|
||||
`이동을 취소했습니다.\n${toX + 1}, ${toY + 1}에서 원래 위치 ${fromX + 1}, ${fromY + 1}로 되돌렸습니다.`
|
||||
);
|
||||
}
|
||||
|
||||
private moveUnitView(
|
||||
@@ -555,7 +570,253 @@ export class BattleScene extends Phaser.Scene {
|
||||
view.label.setBackgroundColor('rgba(20,20,20,0.66)');
|
||||
}
|
||||
|
||||
private renderRosterPanel(tab: RosterTab = this.rosterTab, message?: string) {
|
||||
this.rosterTab = tab;
|
||||
this.clearSidePanelContent();
|
||||
|
||||
const { panelX, panelY, panelWidth, panelHeight } = this.layout;
|
||||
const left = panelX + 24;
|
||||
const width = panelWidth - 48;
|
||||
const top = panelY + 132;
|
||||
const tabGap = 8;
|
||||
const tabWidth = (width - tabGap) / 2;
|
||||
|
||||
(['ally', 'enemy'] as RosterTab[]).forEach((targetTab, index) => {
|
||||
const active = targetTab === tab;
|
||||
const x = left + index * (tabWidth + tabGap);
|
||||
const tabBg = this.trackSideObject(this.add.rectangle(x, top, tabWidth, 38, active ? 0x25384a : 0x141e29, active ? 0.98 : 0.86));
|
||||
tabBg.setOrigin(0);
|
||||
tabBg.setStrokeStyle(1, active ? palette.gold : 0x53606c, active ? 0.92 : 0.62);
|
||||
tabBg.setInteractive({ useHandCursor: true });
|
||||
tabBg.on('pointerdown', () => this.renderRosterPanel(targetTab));
|
||||
|
||||
const tabText = this.trackSideObject(this.add.text(x + tabWidth / 2, top + 19, rosterLabels[targetTab], {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '18px',
|
||||
color: active ? '#f4dfad' : '#aeb7c2',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
tabText.setOrigin(0.5);
|
||||
});
|
||||
|
||||
const units = firstBattleUnits.filter((unit) => unit.faction === tab);
|
||||
const listTop = top + 54;
|
||||
units.forEach((unit, index) => {
|
||||
this.renderRosterRow(unit, left, listTop + index * 50, width);
|
||||
});
|
||||
|
||||
if (message) {
|
||||
const messageTop = Math.min(panelY + panelHeight - 254, listTop + units.length * 50 + 14);
|
||||
this.renderPanelMessage(message, left, messageTop, width);
|
||||
}
|
||||
}
|
||||
|
||||
private renderRosterRow(unit: UnitData, x: number, y: number, width: number) {
|
||||
const acted = this.actedUnitIds.has(unit.id);
|
||||
const active = this.selectedUnit?.id === unit.id;
|
||||
const rowBg = this.trackSideObject(this.add.rectangle(x, y, width, 42, active ? 0x2f4050 : 0x101820, acted ? 0.54 : 0.9));
|
||||
rowBg.setOrigin(0);
|
||||
rowBg.setStrokeStyle(1, active ? palette.gold : unit.faction === 'ally' ? palette.blue : 0xb86b55, active ? 0.88 : 0.52);
|
||||
rowBg.setInteractive({ useHandCursor: true });
|
||||
rowBg.on('pointerdown', () => this.selectUnit(unit));
|
||||
|
||||
const nameColor = acted ? '#a8a8a8' : unit.faction === 'ally' ? '#e9f0f8' : '#ffe0d8';
|
||||
this.trackSideObject(this.add.text(x + 12, y + 7, unit.name, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '17px',
|
||||
color: nameColor,
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.trackSideObject(this.add.text(x + 88, y + 9, unit.className, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: acted ? '#8c8c8c' : '#9fb0bf'
|
||||
}));
|
||||
|
||||
const hpText = this.trackSideObject(this.add.text(x + width - 12, y + 7, `${unit.hp}/${unit.maxHp}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
color: acted ? '#9a9a9a' : '#f0e4c8',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
hpText.setOrigin(1, 0);
|
||||
|
||||
this.drawGauge(x + 88, y + 29, width - 100, 6, unit.hp / unit.maxHp, acted ? 0x9a9a9a : 0x59d18c);
|
||||
|
||||
if (acted) {
|
||||
const actedText = this.trackSideObject(this.add.text(x + 12, y + 27, '행동완료', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#bdbdbd'
|
||||
}));
|
||||
actedText.setAlpha(0.78);
|
||||
}
|
||||
}
|
||||
|
||||
private renderUnitDetail(unit: UnitData, message?: string) {
|
||||
this.clearSidePanelContent();
|
||||
|
||||
const { panelX, panelY, panelWidth } = this.layout;
|
||||
const left = panelX + 24;
|
||||
const width = panelWidth - 48;
|
||||
const top = panelY + 124;
|
||||
const acted = this.actedUnitIds.has(unit.id);
|
||||
const factionColor = unit.faction === 'ally' ? palette.blue : 0xb86b55;
|
||||
|
||||
const header = this.trackSideObject(this.add.rectangle(left, top, width, 60, 0x101820, 0.94));
|
||||
header.setOrigin(0);
|
||||
header.setStrokeStyle(1, factionColor, 0.76);
|
||||
|
||||
this.trackSideObject(this.add.text(left + 14, top + 9, rosterLabels[unit.faction], {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: unit.faction === 'ally' ? '#9fd0ff' : '#ffb2a4',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.trackSideObject(this.add.text(left + 14, top + 29, `${unit.name} ${unit.className}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '22px',
|
||||
color: acted ? '#bdbdbd' : '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
|
||||
if (this.phase !== 'command') {
|
||||
const backBg = this.trackSideObject(this.add.rectangle(left + width - 66, top + 10, 52, 28, 0x1b2834, 0.9));
|
||||
backBg.setOrigin(0);
|
||||
backBg.setStrokeStyle(1, palette.gold, 0.72);
|
||||
backBg.setInteractive({ useHandCursor: true });
|
||||
backBg.on('pointerdown', () => this.returnToRosterPanel(unit.faction));
|
||||
|
||||
const backText = this.trackSideObject(this.add.text(left + width - 40, top + 24, '목록', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
backText.setOrigin(0.5);
|
||||
}
|
||||
|
||||
this.trackSideObject(this.add.text(left, top + 78, '병력', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '17px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
const hpValue = this.trackSideObject(this.add.text(left + width, top + 76, `${unit.hp} / ${unit.maxHp}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '18px',
|
||||
color: '#f0e4c8',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
hpValue.setOrigin(1, 0);
|
||||
this.drawGauge(left + 76, top + 84, width - 160, 10, unit.hp / unit.maxHp, 0x59d18c);
|
||||
|
||||
this.renderSmallValueBox(left, top + 112, '공격', `${unit.attack}`);
|
||||
this.renderSmallValueBox(left + width / 2 + 6, top + 112, '이동', `${unit.move}`);
|
||||
|
||||
const statTop = top + 164;
|
||||
statLabels.forEach((stat, index) => {
|
||||
this.renderStatRow(stat.label, unit.stats[stat.key], left, statTop + index * 32, width);
|
||||
});
|
||||
|
||||
const positionText = `위치 ${unit.x + 1}, ${unit.y + 1}${acted ? ' / 행동완료' : ''}`;
|
||||
this.trackSideObject(this.add.text(left, top + 328, positionText, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '15px',
|
||||
color: acted ? '#bdbdbd' : '#9fb0bf'
|
||||
}));
|
||||
|
||||
if (message) {
|
||||
this.renderPanelMessage(message, left, top + 352, width);
|
||||
}
|
||||
}
|
||||
|
||||
private renderSmallValueBox(x: number, y: number, label: string, value: string) {
|
||||
const boxWidth = (this.layout.panelWidth - 60) / 2;
|
||||
const bg = this.trackSideObject(this.add.rectangle(x, y, boxWidth, 38, 0x16212d, 0.92));
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(1, 0x53606c, 0.56);
|
||||
this.trackSideObject(this.add.text(x + 12, y + 9, label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: '#9fb0bf'
|
||||
}));
|
||||
const valueText = this.trackSideObject(this.add.text(x + boxWidth - 12, y + 7, value, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '18px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
valueText.setOrigin(1, 0);
|
||||
}
|
||||
|
||||
private renderStatRow(label: string, value: number, x: number, y: number, width: number) {
|
||||
this.trackSideObject(this.add.text(x, y, label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
|
||||
this.drawGauge(x + 76, y + 8, width - 130, 9, value / 100, value >= 80 ? 0xd8b15f : 0x58aee0);
|
||||
|
||||
const valueText = this.trackSideObject(this.add.text(x + width, y - 1, `${value}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '17px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
valueText.setOrigin(1, 0);
|
||||
}
|
||||
|
||||
private drawGauge(x: number, y: number, width: number, height: number, ratio: number, color: number) {
|
||||
const track = this.trackSideObject(this.add.rectangle(x, y, width, height, 0x0a0f14, 0.82));
|
||||
track.setOrigin(0);
|
||||
track.setStrokeStyle(1, 0x40515e, 0.58);
|
||||
|
||||
const fillWidth = Math.max(2, Math.floor(width * Phaser.Math.Clamp(ratio, 0, 1)));
|
||||
const fill = this.trackSideObject(this.add.rectangle(x + 1, y + 1, fillWidth - 2, Math.max(2, height - 2), color, 0.96));
|
||||
fill.setOrigin(0);
|
||||
}
|
||||
|
||||
private renderPanelMessage(text: string, x: number, y: number, width: number) {
|
||||
const bg = this.trackSideObject(this.add.rectangle(x, y, width, 74, 0x101820, 0.72));
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(1, 0x53606c, 0.42);
|
||||
this.trackSideObject(this.add.text(x + 12, y + 10, text, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '15px',
|
||||
color: '#d4dce6',
|
||||
wordWrap: { width: width - 24, useAdvancedWrap: true },
|
||||
lineSpacing: 4
|
||||
}));
|
||||
}
|
||||
|
||||
private returnToRosterPanel(tab: RosterTab) {
|
||||
this.selectedUnit = undefined;
|
||||
this.pendingMove = undefined;
|
||||
this.phase = 'idle';
|
||||
this.clearMarkers();
|
||||
this.hideCommandMenu();
|
||||
this.renderRosterPanel(tab);
|
||||
}
|
||||
|
||||
private clearSidePanelContent() {
|
||||
this.sidePanelObjects.forEach((object) => object.destroy());
|
||||
this.sidePanelObjects = [];
|
||||
}
|
||||
|
||||
private trackSideObject<T extends Phaser.GameObjects.GameObject>(object: T) {
|
||||
this.sidePanelObjects.push(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
private setInfo(text: string) {
|
||||
this.infoText?.setText(text);
|
||||
if (this.selectedUnit) {
|
||||
this.renderUnitDetail(this.selectedUnit, text);
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderRosterPanel(this.rosterTab, text);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user