Improve combat prediction panel readability

This commit is contained in:
2026-07-02 22:14:33 +09:00
parent 2eb7258065
commit c408b57c10

View File

@@ -5102,37 +5102,41 @@ export class BattleScene extends Phaser.Scene {
const { panelX, panelWidth } = this.layout;
const left = panelX + 24;
const width = panelWidth - 48;
const height = 176;
const y = Math.min(this.sideContentTop() + 384, this.sideContentBottom(10) - height);
const height = 238;
const y = Math.max(this.sideContentTop(), Math.min(this.sideContentTop() + 344, this.sideContentBottom(10) - height));
const accent = this.previewAccentColor(preview);
const defenderClass = getUnitClass(preview.defender.classKey);
const counterText = preview.counterAvailable ? '반격 있음' : '반격 없음';
const bg = this.trackSideObject(this.add.rectangle(left, y, width, height, 0x121a22, 0.98));
bg.setOrigin(0);
bg.setStrokeStyle(2, accent, 0.78);
const actionFrame = this.trackSideObject(this.add.rectangle(left + 24, y + 27, 42, 42, 0x0a0f14, 0.9));
const actionFrame = this.trackSideObject(this.add.rectangle(left + 34, y + 36, 58, 58, 0x0a0f14, 0.94));
actionFrame.setStrokeStyle(1, accent, 0.76);
this.trackSideIcon(left + 24, y + 27, this.actionIcon(preview), 35);
this.trackSideObject(this.add.text(left + 52, y + 9, `${preview.attacker.name} ${this.previewActionLabel(preview)} 예측`, {
this.trackSideIcon(left + 34, y + 36, this.actionIcon(preview), 50);
this.trackSideObject(this.add.text(left + 74, y + 10, `${preview.attacker.name} ${this.previewActionLabel(preview)}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '16px',
fontSize: '18px',
color: '#f4dfad',
fontStyle: '700'
fontStyle: '700',
fixedWidth: width - 86
}));
this.trackSideObject(this.add.text(left + 52, y + 30, `${preview.defender.name} / ${preview.matchupLabel}`, {
this.trackSideObject(this.add.text(left + 74, y + 36, `${preview.defender.name} · ${defenderClass.name} · ${preview.matchupLabel} · ${counterText}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
color: '#9fb0bf'
color: preview.counterAvailable ? '#ffb6a6' : '#9fb0bf',
fixedWidth: width - 86
}));
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 + 48, chipWidth, this.itemIcon(attackerWeapon, 'weapon'), '공격 장비', attackerWeapon.name);
this.renderPreviewEquipmentChip(left + 52 + chipWidth + chipGap, y + 48, chipWidth, this.itemIcon(defenderArmor, 'armor'), '방어 장비', defenderArmor.name);
const chipWidth = (width - 20 - chipGap) / 2;
this.renderPreviewEquipmentChip(left + 10, y + 70, chipWidth, this.itemIcon(attackerWeapon, 'weapon'), '공격 장비', attackerWeapon.name);
this.renderPreviewEquipmentChip(left + 10 + chipWidth + chipGap, y + 70, chipWidth, this.itemIcon(defenderArmor, 'armor'), '방어 장비', defenderArmor.name);
const metricTop = y + 86;
const metricTop = y + 122;
const metricGap = 6;
const metricWidth = (width - 20 - metricGap * 2) / 3;
this.renderPreviewMetric(left + 10, metricTop, metricWidth, this.previewDamageIcon(preview), '피해', `${preview.damage}`, 0xb64a45);
@@ -5147,18 +5151,16 @@ export class BattleScene extends Phaser.Scene {
);
this.renderPreviewMetric(left + 10 + (metricWidth + metricGap) * 2, metricTop, metricWidth, 'critical', '치명', `${preview.criticalRate}%`, 0xd8732c);
this.renderPreviewForecastRow(preview, left + 10, y + 126, width - 20, accent);
this.renderPreviewForecastRow(preview, left + 10, y + 184, width - 20, accent);
const badgeY = y + 150;
const badgeY = y + 210;
const maxBadgeRight = left + width - 10;
let badgeX = left + 10;
badgeX = this.renderPreviewBadge(badgeX, badgeY, this.actionIcon(preview), this.previewActionTypeLabel(preview), maxBadgeRight);
badgeX = this.renderPreviewBadge(badgeX, badgeY, preview.action === 'strategy' ? 'success' : 'hit', `${preview.hitRate}% ${this.previewSuccessLabel(preview)}`, maxBadgeRight);
badgeX = this.renderPreviewBadge(badgeX, badgeY, 'advantage', preview.matchupLabel, maxBadgeRight);
badgeX = this.renderPreviewBadge(badgeX, badgeY, 'terrain', preview.terrainLabel, maxBadgeRight);
if (preview.counterAvailable) {
badgeX = this.renderPreviewBadge(badgeX, badgeY, 'counter', '반격 가능', maxBadgeRight);
}
badgeX = this.renderPreviewBadge(badgeX, badgeY, 'counter', counterText, maxBadgeRight);
if (preview.bondDamageBonus > 0 && preview.bondLabel) {
badgeX = this.renderPreviewBadge(badgeX, badgeY, 'leadership', `공명 +${preview.bondDamageBonus}%`, maxBadgeRight);
}
@@ -5171,30 +5173,51 @@ export class BattleScene extends Phaser.Scene {
const { panelX, panelWidth } = this.layout;
const left = panelX + 24;
const width = panelWidth - 48;
const height = 158;
const y = Math.min(this.sideContentTop() + 402, this.sideContentBottom(10) - height);
const height = 238;
const y = Math.max(this.sideContentTop(), Math.min(this.sideContentTop() + 344, this.sideContentBottom(10) - height));
const accent = this.usableAccentColor(preview.usable);
const bg = this.trackSideObject(this.add.rectangle(left, y, width, height, 0x121a22, 0.98));
bg.setOrigin(0);
bg.setStrokeStyle(2, accent, 0.78);
const actionFrame = this.trackSideObject(this.add.rectangle(left + 24, y + 27, 42, 42, 0x0a0f14, 0.9));
const actionFrame = this.trackSideObject(this.add.rectangle(left + 34, y + 36, 58, 58, 0x0a0f14, 0.94));
actionFrame.setStrokeStyle(1, accent, 0.76);
this.trackSideIcon(left + 24, y + 27, this.usableIcon(preview.usable), 35);
this.trackSideObject(this.add.text(left + 52, y + 9, `${preview.user.name} ${preview.usable.name} 예측`, {
this.trackSideIcon(left + 34, y + 36, this.usableIcon(preview.usable), 50);
this.trackSideObject(this.add.text(left + 74, y + 10, `${preview.user.name} ${preview.usable.name}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '16px',
fontSize: '18px',
color: '#f4dfad',
fontStyle: '700'
fontStyle: '700',
fixedWidth: width - 86
}));
this.trackSideObject(this.add.text(left + 52, y + 30, `${preview.target.name} / ${this.usableEffectLabel(preview.usable)} / ${this.usableTargetLabel(preview.usable)}`, {
this.trackSideObject(this.add.text(left + 74, y + 36, `${preview.target.name} · ${this.usableEffectLabel(preview.usable)} · ${this.usableTargetLabel(preview.usable)} ${preview.usable.range}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
color: '#9fb0bf'
color: '#9fb0bf',
fixedWidth: width - 86
}));
const metricTop = y + 56;
const chipGap = 8;
const chipWidth = (width - 20 - chipGap) / 2;
this.renderPreviewEquipmentChip(
left + 10,
y + 70,
chipWidth,
preview.usable.command === 'item' ? 'accessory' : 'strategy',
this.usableChannelLabel(preview.usable),
preview.usable.name
);
this.renderPreviewEquipmentChip(
left + 10 + chipWidth + chipGap,
y + 70,
chipWidth,
this.usableTargetIcon(preview.usable),
'대상/사거리',
`${preview.target.name} · ${preview.usable.range}`
);
const metricTop = y + 122;
const metricGap = 6;
const metricWidth = (width - 20 - metricGap * 2) / 3;
if (preview.usable.effect === 'heal') {
@@ -5207,9 +5230,9 @@ export class BattleScene extends Phaser.Scene {
this.renderPreviewMetric(left + 10 + (metricWidth + metricGap) * 2, metricTop, metricWidth, 'critical', '치명', `+${preview.criticalBonus}`, 0xd8732c);
}
this.renderSupportForecastRow(preview, left + 10, y + 98, width - 20, accent);
this.renderSupportForecastRow(preview, left + 10, y + 184, width - 20, accent);
const badgeY = y + 126;
const badgeY = y + 210;
const maxBadgeRight = left + width - 10;
let badgeX = left + 10;
badgeX = this.renderPreviewBadge(badgeX, badgeY, this.usableIcon(preview.usable), this.usableEffectLabel(preview.usable), maxBadgeRight);
@@ -5228,24 +5251,24 @@ export class BattleScene extends Phaser.Scene {
roleLabel: string,
label: string
) {
const chip = this.trackSideObject(this.add.rectangle(x, y, width, 34, 0x0b1118, 0.96));
const chip = this.trackSideObject(this.add.rectangle(x, y, width, 42, 0x0b1118, 0.96));
chip.setOrigin(0);
chip.setStrokeStyle(1, 0x647485, 0.58);
const iconFrame = this.trackSideObject(this.add.rectangle(x + 18, y + 17, 30, 30, 0x16212d, 0.92));
const iconFrame = this.trackSideObject(this.add.rectangle(x + 21, y + 21, 36, 36, 0x16212d, 0.92));
iconFrame.setStrokeStyle(1, 0x53606c, 0.55);
this.trackSideIcon(x + 18, y + 17, icon, 27);
this.trackSideObject(this.add.text(x + 38, y + 4, roleLabel, {
this.trackSideIcon(x + 21, y + 21, icon, 33);
this.trackSideObject(this.add.text(x + 44, y + 5, roleLabel, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '10px',
color: '#9fb0bf',
fontStyle: '700'
}));
this.trackSideObject(this.add.text(x + 38, y + 17, label, {
this.trackSideObject(this.add.text(x + 44, y + 20, label, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
fontSize: '13px',
color: '#d4dce6',
fontStyle: '700',
wordWrap: { width: width - 48, useAdvancedWrap: true }
fixedWidth: width - 52
}));
}
@@ -5258,18 +5281,18 @@ export class BattleScene extends Phaser.Scene {
value: string,
tone = 0x647485
) {
const bg = this.trackSideObject(this.add.rectangle(x, y, width, 40, 0x16212d, 0.94));
const bg = this.trackSideObject(this.add.rectangle(x, y, width, 52, 0x16212d, 0.94));
bg.setOrigin(0);
bg.setStrokeStyle(1, tone, 0.62);
this.trackSideIcon(x + 18, y + 20, icon, 28);
this.trackSideObject(this.add.text(x + 38, y + 6, label, {
this.trackSideIcon(x + 20, y + 26, icon, 34);
this.trackSideObject(this.add.text(x + 42, y + 7, label, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
fontSize: '11px',
color: '#9fb0bf'
}));
const valueText = this.trackSideObject(this.add.text(x + width - 8, y + 20, value, {
const valueText = this.trackSideObject(this.add.text(x + width - 8, y + 32, value, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '17px',
fontSize: '21px',
color: '#f2e3bf',
fontStyle: '700'
}));
@@ -5279,20 +5302,20 @@ export class BattleScene extends Phaser.Scene {
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, 24, 0x0b1118, 0.94));
const row = this.trackSideObject(this.add.rectangle(x, y, width, 28, 0x0b1118, 0.94));
row.setOrigin(0);
row.setStrokeStyle(1, accent, 0.48);
this.trackSideIcon(x + 14, y + 12, 'hp', 20);
this.trackSideObject(this.add.text(x + 30, y + 6, label, {
this.trackSideIcon(x + 15, y + 14, 'hp', 23);
this.trackSideObject(this.add.text(x + 34, y + 7, label, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
fontSize: '12px',
color: '#9fb0bf',
fontStyle: '700'
}));
this.drawGauge(x + 102, y + 8, width - 176, 8, projectedHp / preview.defender.maxHp, projectedHp <= 0 ? 0xb64a45 : 0x59d18c);
const hpText = this.trackSideObject(this.add.text(x + width - 8, y + 6, `${preview.defender.hp} -> ${projectedHp}`, {
this.drawGauge(x + 108, y + 10, width - 184, 9, projectedHp / preview.defender.maxHp, projectedHp <= 0 ? 0xb64a45 : 0x59d18c);
const hpText = this.trackSideObject(this.add.text(x + width - 8, y + 7, `${preview.defender.hp} -> ${projectedHp}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
fontSize: '13px',
color: projectedHp <= 0 ? '#ff8f5f' : '#f2e3bf',
fontStyle: '700'
}));
@@ -5300,23 +5323,23 @@ export class BattleScene extends Phaser.Scene {
}
private renderSupportForecastRow(preview: SupportPreview, x: number, y: number, width: number, accent: number) {
const row = this.trackSideObject(this.add.rectangle(x, y, width, 24, 0x0b1118, 0.94));
const row = this.trackSideObject(this.add.rectangle(x, y, width, 28, 0x0b1118, 0.94));
row.setOrigin(0);
row.setStrokeStyle(1, accent, 0.48);
const isHeal = preview.usable.effect === 'heal';
this.trackSideIcon(x + 14, y + 12, this.usablePowerIcon(preview.usable), 20);
this.trackSideObject(this.add.text(x + 26, y + 5, isHeal ? '적용 후 HP' : '강화 결과', {
this.trackSideIcon(x + 15, y + 14, this.usablePowerIcon(preview.usable), 23);
this.trackSideObject(this.add.text(x + 34, y + 7, isHeal ? '적용 후 HP' : '강화 결과', {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '11px',
fontSize: '12px',
color: '#9fb0bf',
fontStyle: '700'
}));
if (isHeal) {
this.drawGauge(x + 104, y + 8, width - 182, 8, preview.projectedHp / preview.target.maxHp, 0x59d18c);
const hpText = this.trackSideObject(this.add.text(x + width - 8, y + 6, `${preview.target.hp} -> ${preview.projectedHp}`, {
this.drawGauge(x + 108, y + 10, width - 184, 9, preview.projectedHp / preview.target.maxHp, 0x59d18c);
const hpText = this.trackSideObject(this.add.text(x + width - 8, y + 7, `${preview.target.hp} -> ${preview.projectedHp}`, {
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
fontSize: '13px',
color: '#f2e3bf',
fontStyle: '700'
}));
@@ -5326,11 +5349,11 @@ export class BattleScene extends Phaser.Scene {
const buffText = this.trackSideObject(this.add.text(
x + width - 8,
y + 6,
y + 7,
`공+${preview.attackBonus} / 명+${preview.hitBonus} / 치+${preview.criticalBonus} / ${preview.duration}`,
{
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
fontSize: '12px',
fontSize: '13px',
color: '#f2e3bf',
fontStyle: '700'
}