Refine combat preview readability
This commit is contained in:
@@ -4670,42 +4670,76 @@ export class BattleScene extends Phaser.Scene {
|
||||
return parts.join(' / ') || '보정 없음';
|
||||
}
|
||||
|
||||
private previewActionLabel(preview: CombatPreview) {
|
||||
return preview.usable?.name ?? commandLabels[preview.action];
|
||||
}
|
||||
|
||||
private previewAccentColor(preview: CombatPreview) {
|
||||
if (preview.action === 'strategy') {
|
||||
return palette.blue;
|
||||
}
|
||||
if (preview.action === 'item') {
|
||||
return palette.green;
|
||||
}
|
||||
return palette.gold;
|
||||
}
|
||||
|
||||
private previewSuccessLabel(preview: CombatPreview) {
|
||||
return preview.action === 'strategy' ? '성공' : '명중';
|
||||
}
|
||||
|
||||
private renderCombatPreviewCard(preview: CombatPreview) {
|
||||
const { panelX, panelWidth } = this.layout;
|
||||
const left = panelX + 24;
|
||||
const width = panelWidth - 48;
|
||||
const height = 152;
|
||||
const y = Math.min(this.sideContentTop() + 408, this.sideContentBottom(10) - height);
|
||||
const height = 176;
|
||||
const y = Math.min(this.sideContentTop() + 384, this.sideContentBottom(10) - height);
|
||||
const accent = this.previewAccentColor(preview);
|
||||
|
||||
const bg = this.trackSideObject(this.add.rectangle(left, y, width, height, 0x121a22, 0.98));
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(2, palette.gold, 0.72);
|
||||
bg.setStrokeStyle(2, accent, 0.78);
|
||||
|
||||
const actionFrame = this.trackSideObject(this.add.rectangle(left + 24, y + 27, 42, 42, 0x0a0f14, 0.9));
|
||||
actionFrame.setStrokeStyle(1, preview.action === 'strategy' ? palette.blue : palette.gold, 0.72);
|
||||
actionFrame.setStrokeStyle(1, accent, 0.76);
|
||||
this.trackSideIcon(left + 24, y + 27, this.actionIcon(preview), 35);
|
||||
this.trackSideObject(this.add.text(left + 52, y + 10, `${preview.attacker.name} ${commandLabels[preview.action]} 예측`, {
|
||||
this.trackSideObject(this.add.text(left + 52, y + 9, `${preview.attacker.name} ${this.previewActionLabel(preview)} 예측`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.trackSideObject(this.add.text(left + 52, y + 30, `${preview.defender.name} / ${preview.matchupLabel}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#9fb0bf'
|
||||
}));
|
||||
|
||||
const attackerWeapon = getItem(preview.attacker.equipment.weapon.itemId);
|
||||
const defenderArmor = getItem(preview.defender.equipment.armor.itemId);
|
||||
const chipGap = 8;
|
||||
const chipWidth = (width - 68 - chipGap) / 2;
|
||||
this.renderPreviewEquipmentChip(left + 52, y + 42, chipWidth, this.itemIcon(attackerWeapon, 'weapon'), attackerWeapon.name);
|
||||
this.renderPreviewEquipmentChip(left + 52 + chipWidth + chipGap, y + 42, chipWidth, this.itemIcon(defenderArmor, 'armor'), defenderArmor.name);
|
||||
this.renderPreviewEquipmentChip(left + 52, y + 48, chipWidth, this.itemIcon(attackerWeapon, 'weapon'), '공격 장비', attackerWeapon.name);
|
||||
this.renderPreviewEquipmentChip(left + 52 + chipWidth + chipGap, y + 48, chipWidth, this.itemIcon(defenderArmor, 'armor'), '방어 장비', defenderArmor.name);
|
||||
|
||||
const metricTop = y + 78;
|
||||
const metricTop = y + 86;
|
||||
const metricGap = 6;
|
||||
const metricWidth = (width - 20 - metricGap * 2) / 3;
|
||||
this.renderPreviewMetric(left + 10, metricTop, metricWidth, 'attack', '피해', `${preview.damage}`);
|
||||
this.renderPreviewMetric(left + 10 + metricWidth + metricGap, metricTop, metricWidth, preview.action === 'strategy' ? 'success' : 'hit', preview.action === 'strategy' ? '성공' : '명중', `${preview.hitRate}%`);
|
||||
this.renderPreviewMetric(left + 10 + (metricWidth + metricGap) * 2, metricTop, metricWidth, 'critical', '치명', `${preview.criticalRate}%`);
|
||||
this.renderPreviewMetric(left + 10, metricTop, metricWidth, 'attack', '피해', `${preview.damage}`, 0xb64a45);
|
||||
this.renderPreviewMetric(
|
||||
left + 10 + metricWidth + metricGap,
|
||||
metricTop,
|
||||
metricWidth,
|
||||
preview.action === 'strategy' ? 'success' : 'hit',
|
||||
this.previewSuccessLabel(preview),
|
||||
`${preview.hitRate}%`,
|
||||
accent
|
||||
);
|
||||
this.renderPreviewMetric(left + 10 + (metricWidth + metricGap) * 2, metricTop, metricWidth, 'critical', '치명', `${preview.criticalRate}%`, 0xd8732c);
|
||||
|
||||
const badgeY = y + 122;
|
||||
this.renderPreviewForecastRow(preview, left + 10, y + 126, width - 20, accent);
|
||||
|
||||
const badgeY = y + 150;
|
||||
const maxBadgeRight = left + width - 10;
|
||||
let badgeX = left + 10;
|
||||
badgeX = this.renderPreviewBadge(badgeX, badgeY, 'advantage', preview.matchupLabel, maxBadgeRight);
|
||||
@@ -4724,26 +4758,47 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
}
|
||||
|
||||
private renderPreviewEquipmentChip(x: number, y: number, width: number, icon: BattleUiIconKey, label: string) {
|
||||
const chip = this.trackSideObject(this.add.rectangle(x, y, width, 27, 0x0b1118, 0.96));
|
||||
private renderPreviewEquipmentChip(
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
icon: BattleUiIconKey,
|
||||
roleLabel: string,
|
||||
label: string
|
||||
) {
|
||||
const chip = this.trackSideObject(this.add.rectangle(x, y, width, 30, 0x0b1118, 0.96));
|
||||
chip.setOrigin(0);
|
||||
chip.setStrokeStyle(1, 0x647485, 0.58);
|
||||
const iconFrame = this.trackSideObject(this.add.rectangle(x + 15, y + 13.5, 23, 23, 0x16212d, 0.92));
|
||||
const iconFrame = this.trackSideObject(this.add.rectangle(x + 16, y + 15, 24, 24, 0x16212d, 0.92));
|
||||
iconFrame.setStrokeStyle(1, 0x53606c, 0.55);
|
||||
this.trackSideIcon(x + 15, y + 13.5, icon, 21);
|
||||
this.trackSideObject(this.add.text(x + 31, y + 6, label, {
|
||||
this.trackSideIcon(x + 16, y + 15, icon, 22);
|
||||
this.trackSideObject(this.add.text(x + 33, y + 3, roleLabel, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.trackSideObject(this.add.text(x + 33, y + 15, label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700',
|
||||
wordWrap: { width: width - 38, useAdvancedWrap: true }
|
||||
wordWrap: { width: width - 42, useAdvancedWrap: true }
|
||||
}));
|
||||
}
|
||||
|
||||
private renderPreviewMetric(x: number, y: number, width: number, icon: BattleUiIconKey, label: string, value: string) {
|
||||
private renderPreviewMetric(
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
icon: BattleUiIconKey,
|
||||
label: string,
|
||||
value: string,
|
||||
tone = 0x647485
|
||||
) {
|
||||
const bg = this.trackSideObject(this.add.rectangle(x, y, width, 36, 0x16212d, 0.94));
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(1, 0x647485, 0.58);
|
||||
bg.setStrokeStyle(1, tone, 0.62);
|
||||
this.trackSideIcon(x + 16, y + 18, icon, 23);
|
||||
this.trackSideObject(this.add.text(x + 33, y + 5, label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
@@ -4759,6 +4814,29 @@ export class BattleScene extends Phaser.Scene {
|
||||
valueText.setOrigin(1, 0.5);
|
||||
}
|
||||
|
||||
private renderPreviewForecastRow(preview: CombatPreview, x: number, y: number, width: number, accent: number) {
|
||||
const projectedHp = Math.max(0, preview.defender.hp - preview.damage);
|
||||
const label = preview.action === 'strategy' ? '성공 시 HP' : '명중 시 HP';
|
||||
const row = this.trackSideObject(this.add.rectangle(x, y, width, 20, 0x0b1118, 0.94));
|
||||
row.setOrigin(0);
|
||||
row.setStrokeStyle(1, accent, 0.48);
|
||||
this.trackSideIcon(x + 12, y + 10, 'hp', 17);
|
||||
this.trackSideObject(this.add.text(x + 26, y + 4, label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '11px',
|
||||
color: '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.drawGauge(x + 96, y + 6, width - 170, 8, projectedHp / preview.defender.maxHp, projectedHp <= 0 ? 0xb64a45 : 0x59d18c);
|
||||
const hpText = this.trackSideObject(this.add.text(x + width - 8, y + 4, `${preview.defender.hp} -> ${projectedHp}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: projectedHp <= 0 ? '#ff8f5f' : '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
hpText.setOrigin(1, 0);
|
||||
}
|
||||
|
||||
private renderPreviewBadge(x: number, y: number, icon: BattleUiIconKey, label: string, maxRight?: number) {
|
||||
const width = Phaser.Math.Clamp(label.length * 8 + 42, 68, 138);
|
||||
if (maxRight && x + width > maxRight) {
|
||||
@@ -7620,19 +7698,27 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private renderCombatActionRibbon(result: CombatResult, x: number, y: number, width: number, depth: number) {
|
||||
const accent = this.previewAccentColor(result);
|
||||
const bg = this.trackCombatObject(this.add.rectangle(x, y, width, 64, 0x0b1118, 0.92));
|
||||
bg.setOrigin(0);
|
||||
bg.setDepth(depth);
|
||||
bg.setStrokeStyle(1, palette.gold, 0.58);
|
||||
bg.setStrokeStyle(1, accent, 0.66);
|
||||
|
||||
const actionFrame = this.trackCombatObject(this.add.rectangle(x + 24, y + 22, 36, 36, 0x101820, 0.9));
|
||||
actionFrame.setDepth(depth + 1);
|
||||
actionFrame.setStrokeStyle(1, result.action === 'strategy' ? palette.blue : palette.gold, 0.62);
|
||||
actionFrame.setStrokeStyle(1, accent, 0.68);
|
||||
this.trackCombatIcon(x + 24, y + 22, this.actionIcon(result), 31, depth + 2);
|
||||
const attackerWeapon = getItem(result.attacker.equipment.weapon.itemId);
|
||||
const defenderArmor = getItem(result.defender.equipment.armor.itemId);
|
||||
this.trackCombatIcon(x + 136, y + 22, this.itemIcon(attackerWeapon, 'weapon'), 25, depth + 1);
|
||||
this.trackCombatIcon(x + 252, y + 22, this.itemIcon(defenderArmor, 'armor'), 25, depth + 1);
|
||||
|
||||
const weaponFrame = this.trackCombatObject(this.add.rectangle(x + 134, y + 22, 30, 30, 0x16212d, 0.9));
|
||||
weaponFrame.setDepth(depth + 1);
|
||||
weaponFrame.setStrokeStyle(1, 0x647485, 0.58);
|
||||
this.trackCombatIcon(x + 134, y + 22, this.itemIcon(attackerWeapon, 'weapon'), 25, depth + 2);
|
||||
const armorFrame = this.trackCombatObject(this.add.rectangle(x + 244, y + 22, 30, 30, 0x16212d, 0.9));
|
||||
armorFrame.setDepth(depth + 1);
|
||||
armorFrame.setStrokeStyle(1, 0x647485, 0.58);
|
||||
this.trackCombatIcon(x + 244, y + 22, this.itemIcon(defenderArmor, 'armor'), 25, depth + 2);
|
||||
|
||||
const actionLabel = this.trackCombatObject(this.add.text(x + 46, y + 9, result.usable?.name ?? commandLabels[result.action], {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
@@ -7643,21 +7729,45 @@ export class BattleScene extends Phaser.Scene {
|
||||
}));
|
||||
actionLabel.setDepth(depth + 1);
|
||||
|
||||
const weaponLabel = this.trackCombatObject(this.add.text(x + 154, y + 10, attackerWeapon.name, {
|
||||
const weaponLabel = this.trackCombatObject(this.add.text(x + 154, y + 7, attackerWeapon.name, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#d4dce6',
|
||||
fixedWidth: 82
|
||||
}));
|
||||
weaponLabel.setDepth(depth + 1);
|
||||
const weaponRole = this.trackCombatObject(this.add.text(x + 154, y + 25, '공격 장비', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#9fb0bf',
|
||||
fixedWidth: 82
|
||||
}));
|
||||
weaponRole.setDepth(depth + 1);
|
||||
|
||||
const armorLabel = this.trackCombatObject(this.add.text(x + 270, y + 10, defenderArmor.name, {
|
||||
const armorLabel = this.trackCombatObject(this.add.text(x + 264, y + 7, defenderArmor.name, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#d4dce6',
|
||||
fixedWidth: 74
|
||||
}));
|
||||
armorLabel.setDepth(depth + 1);
|
||||
const armorRole = this.trackCombatObject(this.add.text(x + 264, y + 25, '방어 장비', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#9fb0bf',
|
||||
fixedWidth: 74
|
||||
}));
|
||||
armorRole.setDepth(depth + 1);
|
||||
|
||||
const dealtDamage = result.hit ? result.damage : 0;
|
||||
const damageText = this.trackCombatObject(this.add.text(x + width - 14, y + 10, `피해 ${dealtDamage}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: result.hit ? '#ffcf91' : '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
damageText.setOrigin(1, 0);
|
||||
damageText.setDepth(depth + 1);
|
||||
|
||||
const outcomeIcon = result.action === 'strategy' ? 'success' : result.critical ? 'critical' : 'hit';
|
||||
const outcomeLabel = result.action === 'strategy' ? (result.hit ? '성공' : '실패') : result.hit ? (result.critical ? '치명' : '명중') : '회피';
|
||||
|
||||
Reference in New Issue
Block a user