diff --git a/src/game/scenes/BattleScene.ts b/src/game/scenes/BattleScene.ts index 09f321c..f383288 100644 --- a/src/game/scenes/BattleScene.ts +++ b/src/game/scenes/BattleScene.ts @@ -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' }