Add first battle objective tracking
This commit is contained in:
@@ -31,6 +31,9 @@ export type BattleDefeatConditionDefinition = {
|
||||
export type BattleScenarioDefinition = {
|
||||
id: BattleScenarioId;
|
||||
title: string;
|
||||
victoryConditionLabel: string;
|
||||
defeatConditionLabel: string;
|
||||
openingObjectiveLines: string[];
|
||||
map: BattleMap;
|
||||
units: UnitData[];
|
||||
bonds: BattleBond[];
|
||||
@@ -48,6 +51,13 @@ export type BattleScenarioDefinition = {
|
||||
export const firstBattleScenario: BattleScenarioDefinition = {
|
||||
id: 'first-battle-zhuo-commandery',
|
||||
title: '탁현의 전투',
|
||||
victoryConditionLabel: '두령 한석 격파',
|
||||
defeatConditionLabel: '유비 퇴각',
|
||||
openingObjectiveLines: [
|
||||
'두령 한석을 격파하면 황건적의 전열이 무너집니다.',
|
||||
'유비가 퇴각하면 의용군은 전투를 지속할 수 없습니다.',
|
||||
'마을을 확보하고 8턴 안에 승리하면 추가 보상이 붙습니다.'
|
||||
],
|
||||
map: firstBattleMap,
|
||||
units: firstBattleUnits,
|
||||
bonds: firstBattleBonds,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Phaser from 'phaser';
|
||||
import { soundDirector } from '../audio/SoundDirector';
|
||||
import { type BattleBond, type UnitData, type UnitStats } from '../data/scenario';
|
||||
import { defaultBattleScenario, getBattleScenario, type BattleScenarioDefinition } from '../data/battles';
|
||||
import { defaultBattleScenario, getBattleScenario, type BattleObjectiveDefinition, type BattleScenarioDefinition } from '../data/battles';
|
||||
import { getTerrainRule, getUnitClass, type TerrainType, type UnitClassKey } from '../data/battleRules';
|
||||
import {
|
||||
equipmentExpToNext,
|
||||
@@ -152,6 +152,10 @@ type BattleObjectiveResult = {
|
||||
rewardGold: number;
|
||||
};
|
||||
|
||||
type BattleObjectiveState = BattleObjectiveResult & {
|
||||
status: 'active' | 'done' | 'failed';
|
||||
};
|
||||
|
||||
type EquipmentGrowthResult = {
|
||||
slot: EquipmentSlot;
|
||||
itemName: string;
|
||||
@@ -515,6 +519,8 @@ export class BattleScene extends Phaser.Scene {
|
||||
private targetingAction?: DamageCommand;
|
||||
private selectedUsable?: BattleUsable;
|
||||
private turnText?: Phaser.GameObjects.Text;
|
||||
private objectiveTrackerText?: Phaser.GameObjects.Text;
|
||||
private objectiveTrackerSubText?: Phaser.GameObjects.Text;
|
||||
private markers: Phaser.GameObjects.Rectangle[] = [];
|
||||
private rosterTab: RosterTab = 'ally';
|
||||
private sidePanelObjects: Phaser.GameObjects.GameObject[] = [];
|
||||
@@ -734,28 +740,36 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.add.rectangle(panelX, panelY, panelWidth, panelHeight, palette.panel, 0.96).setOrigin(0);
|
||||
this.add.rectangle(panelX, panelY, 3, panelHeight, palette.gold, 0.82).setOrigin(0);
|
||||
|
||||
this.add.text(panelX + 24, panelY + 28, '첫 전투', {
|
||||
this.add.text(panelX + 24, panelY + 24, battleScenario.title, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '30px',
|
||||
fontSize: '28px',
|
||||
color: '#e8dfca',
|
||||
fontStyle: '700',
|
||||
padding: { top: 4, bottom: 4 }
|
||||
});
|
||||
this.turnText = this.add.text(panelX + 24, panelY + 88, '1턴 / 아군', {
|
||||
this.turnText = this.add.text(panelX + 24, panelY + 76, '1턴 / 아군', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '22px',
|
||||
color: '#d8b15f'
|
||||
});
|
||||
this.add.text(panelX + 24, panelY + panelHeight - 196, '승리: 적 전멸 패배: 유비 퇴각', {
|
||||
this.objectiveTrackerText = this.add.text(panelX + 24, panelY + 108, '', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '15px',
|
||||
color: '#9aa3ad'
|
||||
fontSize: '13px',
|
||||
color: '#d4dce6',
|
||||
wordWrap: { width: panelWidth - 48, useAdvancedWrap: true }
|
||||
});
|
||||
this.objectiveTrackerSubText = this.add.text(panelX + 24, panelY + 126, '', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#9aa3ad',
|
||||
wordWrap: { width: panelWidth - 48, useAdvancedWrap: true }
|
||||
});
|
||||
this.add.text(panelX + 24, panelY + panelHeight - 172, '가장자리 커서 / 미니맵 클릭으로 시야 이동', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: '#9aa3ad'
|
||||
});
|
||||
this.updateObjectiveTracker();
|
||||
this.drawMiniMap();
|
||||
}
|
||||
|
||||
@@ -1699,6 +1713,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
soundDirector.playSelect();
|
||||
this.pushBattleLog(message);
|
||||
this.checkBattleEvents();
|
||||
this.updateObjectiveTracker();
|
||||
|
||||
if (this.resolveBattleOutcomeIfNeeded()) {
|
||||
return;
|
||||
@@ -1761,10 +1776,11 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.hideTurnEndPrompt();
|
||||
this.hideBattleAlert();
|
||||
this.hideBattleEventBanner();
|
||||
this.updateObjectiveTracker();
|
||||
|
||||
const message =
|
||||
outcome === 'victory'
|
||||
? '승리! 황건적을 모두 물리쳤습니다.'
|
||||
? `승리! ${battleScenario.victoryConditionLabel}에 성공했습니다.`
|
||||
: '패배... 유비가 패주했습니다.';
|
||||
this.pushBattleLog(message);
|
||||
this.renderSituationPanel(message);
|
||||
@@ -1869,53 +1885,73 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private resultObjectives(outcome: BattleOutcome): BattleObjectiveResult[] {
|
||||
return battleScenario.objectives.map((objective) => {
|
||||
if (objective.kind === 'defeat-leader') {
|
||||
const targetId = objective.unitId ?? leaderUnitId;
|
||||
const defeated = this.isUnitDefeated(targetId);
|
||||
return {
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved: outcome === 'victory' && defeated,
|
||||
detail: defeated ? `${this.unitName(targetId)} 격파` : `${this.unitName(targetId)} 생존`,
|
||||
rewardGold: objective.rewardGold
|
||||
};
|
||||
}
|
||||
return this.objectiveStates(outcome).map((objective) => ({
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved: objective.achieved,
|
||||
detail: objective.detail,
|
||||
rewardGold: objective.rewardGold
|
||||
}));
|
||||
}
|
||||
|
||||
if (objective.kind === 'keep-unit-alive') {
|
||||
const targetId = objective.unitId ?? 'liu-bei';
|
||||
const alive = this.isUnitAlive(targetId);
|
||||
return {
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved: alive,
|
||||
detail: alive ? `${this.unitName(targetId)} 생존` : `${this.unitName(targetId)} 퇴각`,
|
||||
rewardGold: objective.rewardGold
|
||||
};
|
||||
}
|
||||
private objectiveStates(outcome?: BattleOutcome): BattleObjectiveState[] {
|
||||
return battleScenario.objectives.map((objective) => this.objectiveState(objective, outcome));
|
||||
}
|
||||
|
||||
if (objective.kind === 'secure-terrain') {
|
||||
const terrain = objective.terrain ?? 'village';
|
||||
const secured = !this.hasEnemyOnTerrain(terrain);
|
||||
return {
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved: outcome === 'victory' && secured,
|
||||
detail: secured ? '점거한 적 없음' : '적이 점거 중',
|
||||
rewardGold: objective.rewardGold
|
||||
};
|
||||
}
|
||||
|
||||
const maxTurn = objective.maxTurn ?? quickVictoryTurnLimit;
|
||||
const achieved = outcome === 'victory' && this.turnNumber <= maxTurn;
|
||||
private objectiveState(objective: BattleObjectiveDefinition, outcome?: BattleOutcome): BattleObjectiveState {
|
||||
if (objective.kind === 'defeat-leader') {
|
||||
const targetId = objective.unitId ?? leaderUnitId;
|
||||
const target = battleUnits.find((unit) => unit.id === targetId);
|
||||
const defeated = this.isUnitDefeated(targetId);
|
||||
const achieved = outcome ? outcome === 'victory' && defeated : defeated;
|
||||
return {
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved,
|
||||
detail: `${this.turnNumber}턴 종료`,
|
||||
status: achieved ? 'done' : outcome === 'defeat' ? 'failed' : 'active',
|
||||
detail: defeated ? `${this.unitName(targetId)} 격파` : `${this.unitName(targetId)} 병력 ${target?.hp ?? 0}/${target?.maxHp ?? 0}`,
|
||||
rewardGold: objective.rewardGold
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (objective.kind === 'keep-unit-alive') {
|
||||
const targetId = objective.unitId ?? 'liu-bei';
|
||||
const alive = this.isUnitAlive(targetId);
|
||||
return {
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved: alive,
|
||||
status: alive ? 'active' : 'failed',
|
||||
detail: alive ? `${this.unitName(targetId)} 생존` : `${this.unitName(targetId)} 퇴각`,
|
||||
rewardGold: objective.rewardGold
|
||||
};
|
||||
}
|
||||
|
||||
if (objective.kind === 'secure-terrain') {
|
||||
const terrain = objective.terrain ?? 'village';
|
||||
const secured = !this.hasEnemyOnTerrain(terrain);
|
||||
const achieved = outcome ? outcome === 'victory' && secured : secured;
|
||||
return {
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved,
|
||||
status: achieved ? 'done' : outcome === 'defeat' ? 'failed' : 'active',
|
||||
detail: secured ? '마을 주변 안전' : '마을 주변 교전 중',
|
||||
rewardGold: objective.rewardGold
|
||||
};
|
||||
}
|
||||
|
||||
const maxTurn = objective.maxTurn ?? quickVictoryTurnLimit;
|
||||
const inTime = this.turnNumber <= maxTurn;
|
||||
const achieved = outcome ? outcome === 'victory' && inTime : inTime;
|
||||
return {
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved,
|
||||
status: achieved ? (outcome === 'victory' ? 'done' : 'active') : 'failed',
|
||||
detail: inTime ? `${this.turnNumber}/${maxTurn}턴 진행 중` : `${this.turnNumber}/${maxTurn}턴 초과`,
|
||||
rewardGold: objective.rewardGold
|
||||
};
|
||||
}
|
||||
|
||||
private resultGold(outcome: BattleOutcome) {
|
||||
@@ -2644,11 +2680,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private showOpeningBattleEvent() {
|
||||
this.triggerBattleEvent('opening', '첫 전투 목표', [
|
||||
'황건 두령을 격파하면 승리합니다.',
|
||||
'유비가 퇴각하면 패배합니다.',
|
||||
'마을을 확보하고 빠르게 승리하면 추가 보상이 붙습니다.'
|
||||
]);
|
||||
this.triggerBattleEvent('opening', '첫 전투 목표', battleScenario.openingObjectiveLines);
|
||||
}
|
||||
|
||||
private triggerBattleEvent(key: string, title: string, lines: string[]) {
|
||||
@@ -3201,6 +3233,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
this.resetActedStyles();
|
||||
this.updateTurnText();
|
||||
this.updateObjectiveTracker();
|
||||
this.renderRosterPanel(this.rosterTab, '저장된 전투 상태입니다.');
|
||||
}
|
||||
|
||||
@@ -3290,6 +3323,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
this.activeFaction = 'enemy';
|
||||
this.updateTurnText();
|
||||
this.updateObjectiveTracker();
|
||||
const recoveryMessage = this.applyTerrainRecovery('enemy');
|
||||
this.renderSituationPanel(['아군 턴을 종료했습니다.', recoveryMessage, '적군이 행동을 시작합니다.'].filter(Boolean).join('\n'));
|
||||
void this.runEnemyTurn();
|
||||
@@ -3312,6 +3346,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
if (message) {
|
||||
messages.push(message);
|
||||
this.pushBattleLog(message);
|
||||
this.updateObjectiveTracker();
|
||||
this.renderSituationPanel(messages.slice(-3).join('\n'));
|
||||
}
|
||||
|
||||
@@ -4360,6 +4395,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
const recoveryMessage = this.applyTerrainRecovery('ally');
|
||||
this.resetActedStyles();
|
||||
this.updateTurnText();
|
||||
this.updateObjectiveTracker();
|
||||
this.checkBattleEvents();
|
||||
this.renderRosterPanel('ally', [`${this.turnNumber}턴 아군 차례입니다.`, recoveryMessage, '행동할 장수를 선택하세요.'].filter(Boolean).join('\n'));
|
||||
}
|
||||
@@ -4446,6 +4482,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
private updateTurnText() {
|
||||
this.turnText?.setText(`${this.turnNumber}턴 / ${factionLabels[this.activeFaction]}`);
|
||||
this.updateObjectiveTracker();
|
||||
}
|
||||
|
||||
private resetActedStyles() {
|
||||
@@ -5070,12 +5107,16 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.battleLog = [firstLine, ...this.battleLog].slice(0, 8);
|
||||
}
|
||||
|
||||
private sideContentTop() {
|
||||
return this.layout.panelY + 152;
|
||||
}
|
||||
|
||||
private renderBondPanel(message?: string) {
|
||||
this.clearSidePanelContent();
|
||||
const { panelX, panelY, panelWidth } = this.layout;
|
||||
const { panelX, panelWidth } = this.layout;
|
||||
const left = panelX + 24;
|
||||
const width = panelWidth - 48;
|
||||
const top = panelY + 132;
|
||||
const top = this.sideContentTop();
|
||||
|
||||
this.trackSideObject(this.add.text(left, top, '공명 관계', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
@@ -5130,14 +5171,15 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
private renderSituationPanel(message?: string) {
|
||||
this.clearSidePanelContent();
|
||||
const { panelX, panelY, panelWidth } = this.layout;
|
||||
const { panelX, panelWidth } = this.layout;
|
||||
const left = panelX + 24;
|
||||
const width = panelWidth - 48;
|
||||
const top = panelY + 132;
|
||||
const top = this.sideContentTop();
|
||||
const actedCount = battleUnits.filter((unit) => unit.faction === 'ally' && this.actedUnitIds.has(unit.id)).length;
|
||||
const allyCount = battleUnits.filter((unit) => unit.faction === 'ally').length;
|
||||
const enemyCount = battleUnits.filter((unit) => unit.faction === 'enemy' && unit.hp > 0).length;
|
||||
const bestBond = Array.from(this.bondStates.values()).sort((a, b) => b.level - a.level)[0];
|
||||
const objectiveStates = this.objectiveStates(this.battleOutcome);
|
||||
|
||||
this.trackSideObject(this.add.text(left, top, '전황', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
@@ -5146,29 +5188,133 @@ export class BattleScene extends Phaser.Scene {
|
||||
fontStyle: '700'
|
||||
}));
|
||||
|
||||
this.renderSituationLine('현재 턴', `${this.turnNumber}턴 / ${factionLabels[this.activeFaction]}`, left, top + 48, width);
|
||||
this.renderSituationLine('아군 행동', `${actedCount} / ${allyCount}`, left, top + 92, width);
|
||||
this.renderSituationLine('남은 적', `${enemyCount}`, left, top + 136, width);
|
||||
this.renderSituationLine('BGM', soundDirector.isMuted() ? '꺼짐' : '켜짐', left, top + 180, width);
|
||||
this.renderSituationLine('현재 턴', `${this.turnNumber}턴 / ${factionLabels[this.activeFaction]}`, left, top + 46, width);
|
||||
this.renderSituationLine('아군 행동', `${actedCount} / ${allyCount}`, left, top + 84, width);
|
||||
this.renderSituationLine('남은 적', `${enemyCount}`, left, top + 122, width);
|
||||
this.renderSituationLine('BGM', soundDirector.isMuted() ? '꺼짐' : '켜짐', left, top + 160, width);
|
||||
this.renderSituationLine(
|
||||
'최고 공명',
|
||||
bestBond ? `${this.unitName(bestBond.unitIds[0])}·${this.unitName(bestBond.unitIds[1])} ${bestBond.level}` : '-',
|
||||
left,
|
||||
top + 224,
|
||||
top + 198,
|
||||
width
|
||||
);
|
||||
|
||||
this.renderPanelMessage(message ?? '빈 맵에서 우클릭하면 전술 메뉴를 다시 열 수 있습니다.', left, top + 286, width);
|
||||
if (message) {
|
||||
this.renderPanelMessage(this.compactSituationMessage(message), left, top + 246, width, 72);
|
||||
return;
|
||||
}
|
||||
|
||||
this.trackSideObject(this.add.text(left, top + 238, '목표 현황', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '17px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
objectiveStates.forEach((objective, index) => {
|
||||
this.renderObjectiveStateRow(objective, left, top + 264 + index * 28, width);
|
||||
});
|
||||
}
|
||||
|
||||
private compactSituationMessage(message: string) {
|
||||
return message
|
||||
.split('\n')
|
||||
.slice(0, 2)
|
||||
.map((line) => (line.length > 18 ? `${line.slice(0, 17)}...` : line))
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
private updateObjectiveTracker() {
|
||||
if (!this.objectiveTrackerText || !this.objectiveTrackerSubText) {
|
||||
return;
|
||||
}
|
||||
|
||||
const leader = battleUnits.find((unit) => unit.id === leaderUnitId);
|
||||
const leaderProgress = leader && leader.hp > 0 ? `${leader.hp}/${leader.maxHp}` : '완료';
|
||||
const quickObjective = this.objectiveStates(this.battleOutcome).find((objective) => objective.id === 'quick');
|
||||
const quickText = quickObjective ? ` · ${quickObjective.label} ${this.objectiveStatusText(quickObjective)}` : '';
|
||||
const victoryText =
|
||||
this.battleOutcome === 'victory'
|
||||
? `승리: ${battleScenario.victoryConditionLabel} 완료`
|
||||
: `승리: ${battleScenario.victoryConditionLabel} (${leaderProgress})`;
|
||||
const defeatText =
|
||||
this.battleOutcome === 'defeat'
|
||||
? `패배: ${battleScenario.defeatConditionLabel} 발생`
|
||||
: `패배: ${battleScenario.defeatConditionLabel}`;
|
||||
|
||||
this.objectiveTrackerText.setText(victoryText);
|
||||
this.objectiveTrackerSubText.setText(`${defeatText}${quickText}`);
|
||||
}
|
||||
|
||||
private renderObjectiveStateRow(objective: BattleObjectiveState, x: number, y: number, width: number) {
|
||||
const bg = this.trackSideObject(this.add.rectangle(x, y, width, 25, 0x101820, 0.86));
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(1, this.objectiveStatusStroke(objective), 0.5);
|
||||
|
||||
this.trackSideObject(this.add.text(x + 10, y + 5, this.objectiveStatusText(objective), {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: this.objectiveStatusColor(objective),
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.trackSideObject(this.add.text(x + 54, y + 4, objective.label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
const detail = this.trackSideObject(this.add.text(x + width - 10, y + 4, objective.detail, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#9fb0bf'
|
||||
}));
|
||||
detail.setOrigin(1, 0);
|
||||
}
|
||||
|
||||
private objectiveStatusText(objective: BattleObjectiveState) {
|
||||
if (objective.status === 'done') {
|
||||
return '완료';
|
||||
}
|
||||
if (objective.status === 'failed') {
|
||||
return '실패';
|
||||
}
|
||||
if (objective.id === 'liu-bei') {
|
||||
return '유지';
|
||||
}
|
||||
if (objective.id === 'quick') {
|
||||
return '가능';
|
||||
}
|
||||
return '진행';
|
||||
}
|
||||
|
||||
private objectiveStatusColor(objective: BattleObjectiveState) {
|
||||
if (objective.status === 'done') {
|
||||
return '#a8ffd0';
|
||||
}
|
||||
if (objective.status === 'failed') {
|
||||
return '#ffb6a6';
|
||||
}
|
||||
return '#f4dfad';
|
||||
}
|
||||
|
||||
private objectiveStatusStroke(objective: BattleObjectiveState) {
|
||||
if (objective.status === 'done') {
|
||||
return 0x59d18c;
|
||||
}
|
||||
if (objective.status === 'failed') {
|
||||
return 0xb86b55;
|
||||
}
|
||||
return palette.gold;
|
||||
}
|
||||
|
||||
private renderTerrainDetail(x: number, y: number) {
|
||||
this.clearSidePanelContent();
|
||||
const terrain = battleMap.terrain[y][x];
|
||||
const terrainRule = getTerrainRule(terrain);
|
||||
const { panelX, panelY, panelWidth } = this.layout;
|
||||
const { panelX, panelWidth } = this.layout;
|
||||
const left = panelX + 24;
|
||||
const width = panelWidth - 48;
|
||||
const top = panelY + 132;
|
||||
const top = this.sideContentTop();
|
||||
const attackDefense = Math.floor(terrainRule.defenseBonus / 3);
|
||||
const strategyDefense = Math.floor(terrainRule.defenseBonus / 4);
|
||||
const itemDefense = Math.floor(terrainRule.defenseBonus / 5);
|
||||
@@ -5244,7 +5390,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
const { panelX, panelY, panelWidth, panelHeight } = this.layout;
|
||||
const left = panelX + 24;
|
||||
const width = panelWidth - 48;
|
||||
const top = panelY + 132;
|
||||
const top = this.sideContentTop();
|
||||
const tabGap = 8;
|
||||
const tabWidth = (width - tabGap) / 2;
|
||||
|
||||
@@ -5326,10 +5472,10 @@ export class BattleScene extends Phaser.Scene {
|
||||
private renderUnitDetail(unit: UnitData, message?: string) {
|
||||
this.clearSidePanelContent();
|
||||
|
||||
const { panelX, panelY, panelWidth } = this.layout;
|
||||
const { panelX, panelWidth } = this.layout;
|
||||
const left = panelX + 24;
|
||||
const width = panelWidth - 48;
|
||||
const top = panelY + 124;
|
||||
const top = this.sideContentTop();
|
||||
const acted = this.actedUnitIds.has(unit.id);
|
||||
const factionColor = unit.faction === 'ally' ? palette.blue : 0xb86b55;
|
||||
const unitClass = getUnitClass(unit.classKey);
|
||||
@@ -5605,6 +5751,9 @@ export class BattleScene extends Phaser.Scene {
|
||||
return {
|
||||
scene: this.scene.key,
|
||||
battleId: battleScenario.id,
|
||||
battleTitle: battleScenario.title,
|
||||
victoryConditionLabel: battleScenario.victoryConditionLabel,
|
||||
defeatConditionLabel: battleScenario.defeatConditionLabel,
|
||||
turnNumber: this.turnNumber,
|
||||
activeFaction: this.activeFaction,
|
||||
phase: this.phase,
|
||||
@@ -5629,7 +5778,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
actedUnitIds: Array.from(this.actedUnitIds),
|
||||
attackIntents: this.attackIntents.map((intent) => ({ ...intent })),
|
||||
battleLog: [...this.battleLog],
|
||||
objectives: this.resultObjectives(this.battleOutcome ?? 'victory').map((objective) => ({ ...objective })),
|
||||
objectives: this.objectiveStates(this.battleOutcome).map((objective) => ({ ...objective })),
|
||||
battleStats: this.serializeBattleStats(),
|
||||
triggeredBattleEvents: Array.from(this.triggeredBattleEvents),
|
||||
selectedUsable: this.selectedUsable?.id ?? null,
|
||||
|
||||
Reference in New Issue
Block a user