Improve strategy and item UI clarity
This commit is contained in:
@@ -1327,6 +1327,18 @@ type BattleUsable = {
|
||||
description: string;
|
||||
};
|
||||
|
||||
type SupportPreview = {
|
||||
usable: BattleUsable;
|
||||
user: UnitData;
|
||||
target: UnitData;
|
||||
healAmount: number;
|
||||
projectedHp: number;
|
||||
attackBonus: number;
|
||||
hitBonus: number;
|
||||
criticalBonus: number;
|
||||
duration: number;
|
||||
};
|
||||
|
||||
type SupportResult = {
|
||||
usable: BattleUsable;
|
||||
user: UnitData;
|
||||
@@ -4218,6 +4230,91 @@ export class BattleScene extends Phaser.Scene {
|
||||
};
|
||||
}
|
||||
|
||||
private usableAccentColor(usable: BattleUsable) {
|
||||
if (usable.command === 'item') {
|
||||
return palette.gold;
|
||||
}
|
||||
if (usable.effect === 'heal') {
|
||||
return palette.green;
|
||||
}
|
||||
if (usable.effect === 'focus') {
|
||||
return palette.blue;
|
||||
}
|
||||
return 0xb64a45;
|
||||
}
|
||||
|
||||
private usableEffectLabel(usable: BattleUsable) {
|
||||
if (usable.effect === 'heal') {
|
||||
return '회복';
|
||||
}
|
||||
if (usable.effect === 'focus') {
|
||||
return '강화';
|
||||
}
|
||||
return '피해';
|
||||
}
|
||||
|
||||
private usableTargetLabel(usable: BattleUsable) {
|
||||
if (usable.target === 'self') {
|
||||
return '자신';
|
||||
}
|
||||
if (usable.target === 'ally') {
|
||||
return '아군';
|
||||
}
|
||||
return '적군';
|
||||
}
|
||||
|
||||
private usablePowerLabel(usable: BattleUsable) {
|
||||
if (usable.effect === 'heal') {
|
||||
return `회복 ${usable.power}+`;
|
||||
}
|
||||
if (usable.effect === 'focus') {
|
||||
return `${usable.duration ?? 1}턴 강화`;
|
||||
}
|
||||
return `위력 ${usable.power}`;
|
||||
}
|
||||
|
||||
private usableTargetCount(user: UnitData, usable: BattleUsable) {
|
||||
if (usable.effect === 'damage') {
|
||||
return this.damageableTargets(user, usable.command, usable).length;
|
||||
}
|
||||
return this.supportTargets(user, usable).length;
|
||||
}
|
||||
|
||||
private usableStockLabel(user: UnitData, usable: BattleUsable) {
|
||||
return usable.command === 'item' ? `보유 ${this.itemStock(user.id, usable.id)}` : '책략';
|
||||
}
|
||||
|
||||
private usableDetailText(user: UnitData, usable: BattleUsable) {
|
||||
const targetCount = this.usableTargetCount(user, usable);
|
||||
return [
|
||||
`${usable.name} / ${this.usableEffectLabel(usable)} / ${this.usableTargetLabel(usable)}`,
|
||||
`${this.usablePowerLabel(usable)} · 사거리 ${usable.range} · 대상 ${targetCount}명`,
|
||||
usable.description
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
private renderUsableMenuPill(x: number, y: number, width: number, icon: BattleUiIconKey, label: string, tone: number) {
|
||||
const pill = this.add.rectangle(x, y, width, 22, 0x0b1118, 0.92);
|
||||
pill.setOrigin(0);
|
||||
pill.setDepth(16);
|
||||
pill.setStrokeStyle(1, tone, 0.54);
|
||||
this.commandMenuObjects.push(pill);
|
||||
|
||||
const iconImage = this.createBattleUiIcon(x + 12, y + 11, icon, 16);
|
||||
iconImage.setDepth(17);
|
||||
this.commandMenuObjects.push(iconImage);
|
||||
|
||||
const text = this.add.text(x + 25, y + 5, label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700',
|
||||
fixedWidth: width - 29
|
||||
});
|
||||
text.setDepth(17);
|
||||
this.commandMenuObjects.push(text);
|
||||
}
|
||||
|
||||
private hideCommandMenu() {
|
||||
this.commandMenuObjects.forEach((object) => object.destroy());
|
||||
this.commandMenuObjects = [];
|
||||
@@ -4237,12 +4334,13 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
this.hideCommandMenu();
|
||||
|
||||
const menuWidth = 276;
|
||||
const titleHeight = 34;
|
||||
const rowHeight = 58;
|
||||
const menuWidth = 322;
|
||||
const titleHeight = 36;
|
||||
const rowHeight = 72;
|
||||
const padding = 9;
|
||||
const menuHeight = padding * 2 + titleHeight + usables.length * rowHeight;
|
||||
const { left, top } = this.commandMenuPosition(pointerX, pointerY, menuWidth, menuHeight);
|
||||
const titleLabel = command === 'strategy' ? '책략 선택' : '도구 선택';
|
||||
|
||||
const panel = this.add.rectangle(left, top, menuWidth, menuHeight, 0x101821, 0.98);
|
||||
panel.setOrigin(0);
|
||||
@@ -4250,9 +4348,9 @@ export class BattleScene extends Phaser.Scene {
|
||||
panel.setDepth(14);
|
||||
this.commandMenuObjects.push(panel);
|
||||
|
||||
const title = this.add.text(left + menuWidth / 2, top + padding + 2, commandLabels[command], {
|
||||
const title = this.add.text(left + menuWidth / 2, top + padding + 2, titleLabel, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
fontSize: '17px',
|
||||
color: '#d8b15f',
|
||||
fontStyle: '700'
|
||||
});
|
||||
@@ -4262,40 +4360,60 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
usables.forEach((usable, index) => {
|
||||
const rowTop = top + padding + titleHeight + index * rowHeight;
|
||||
const bg = this.add.rectangle(left + 8, rowTop, menuWidth - 16, rowHeight - 6, 0x1a2630, 0.94);
|
||||
const accent = this.usableAccentColor(usable);
|
||||
const bg = this.add.rectangle(left + 8, rowTop, menuWidth - 16, rowHeight - 7, 0x1a2630, 0.95);
|
||||
bg.setOrigin(0);
|
||||
bg.setDepth(15);
|
||||
bg.setStrokeStyle(1, command === 'strategy' ? palette.blue : palette.gold, 0.66);
|
||||
bg.setStrokeStyle(1, accent, 0.68);
|
||||
bg.setInteractive({ useHandCursor: true });
|
||||
this.commandMenuObjects.push(bg);
|
||||
|
||||
const iconFrame = this.add.rectangle(left + 28, rowTop + 25, 32, 32, 0x0a0f14, 0.88);
|
||||
iconFrame.setStrokeStyle(1, command === 'strategy' ? palette.blue : palette.gold, 0.58);
|
||||
const iconFrame = this.add.rectangle(left + 31, rowTop + 32, 38, 38, 0x0a0f14, 0.9);
|
||||
iconFrame.setStrokeStyle(1, accent, 0.66);
|
||||
iconFrame.setDepth(16);
|
||||
this.commandMenuObjects.push(iconFrame);
|
||||
|
||||
const icon = this.createBattleUiIcon(left + 28, rowTop + 25, this.usableIcon(usable), 27);
|
||||
const icon = this.createBattleUiIcon(left + 31, rowTop + 32, this.usableIcon(usable), 32);
|
||||
icon.setDepth(17);
|
||||
this.commandMenuObjects.push(icon);
|
||||
|
||||
const stock = command === 'item' ? ` x${this.itemStock(unit.id, usable.id)}` : '';
|
||||
const name = this.add.text(left + 50, rowTop + 7, `${usable.name}${stock}`, {
|
||||
const stock = this.usableStockLabel(unit, usable);
|
||||
const name = this.add.text(left + 56, rowTop + 7, usable.name, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
fontSize: '17px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
});
|
||||
name.setDepth(16);
|
||||
this.commandMenuObjects.push(name);
|
||||
|
||||
const desc = this.add.text(left + 50, rowTop + 29, usable.description, {
|
||||
const stockText = this.add.text(left + menuWidth - 24, rowTop + 9, stock, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#aeb7c2',
|
||||
wordWrap: { width: menuWidth - 66, useAdvancedWrap: true }
|
||||
fontSize: '11px',
|
||||
color: command === 'item' ? '#f4dfad' : '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
});
|
||||
desc.setDepth(16);
|
||||
this.commandMenuObjects.push(desc);
|
||||
stockText.setOrigin(1, 0);
|
||||
stockText.setDepth(16);
|
||||
this.commandMenuObjects.push(stockText);
|
||||
|
||||
const summary = this.add.text(
|
||||
left + 56,
|
||||
rowTop + 31,
|
||||
`${this.usableEffectLabel(usable)} · ${this.usableTargetLabel(usable)} · 사거리 ${usable.range} · 대상 ${this.usableTargetCount(unit, usable)}명`,
|
||||
{
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#aeb7c2',
|
||||
fixedWidth: menuWidth - 74
|
||||
}
|
||||
);
|
||||
summary.setDepth(16);
|
||||
this.commandMenuObjects.push(summary);
|
||||
|
||||
this.renderUsableMenuPill(left + 56, rowTop + 43, 78, this.usableIcon(usable), this.usableEffectLabel(usable), accent);
|
||||
this.renderUsableMenuPill(left + 140, rowTop + 43, 86, 'move', `거리 ${usable.range}`, accent);
|
||||
this.renderUsableMenuPill(left + 232, rowTop + 43, 82, usable.target === 'enemy' ? 'hit' : 'success', this.usablePowerLabel(usable), accent);
|
||||
|
||||
const choose = (choicePointer: Phaser.Input.Pointer) => {
|
||||
if (choicePointer.rightButtonDown()) {
|
||||
@@ -4308,14 +4426,12 @@ export class BattleScene extends Phaser.Scene {
|
||||
};
|
||||
bg.on('pointerover', () => {
|
||||
bg.setFillStyle(0x283947, 0.98);
|
||||
this.renderUnitDetail(unit, `${usable.name}\n${usable.description}`);
|
||||
this.renderUnitDetail(unit, this.usableDetailText(unit, usable));
|
||||
});
|
||||
bg.on('pointerout', () => bg.setFillStyle(0x1a2630, 0.94));
|
||||
bg.on('pointerdown', choose);
|
||||
name.setInteractive({ useHandCursor: true });
|
||||
name.on('pointerdown', choose);
|
||||
desc.setInteractive({ useHandCursor: true });
|
||||
desc.on('pointerdown', choose);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4535,6 +4651,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
private showSupportTargets(user: UnitData, usable: BattleUsable, targets = this.supportTargets(user, usable)) {
|
||||
targets.forEach((target) => {
|
||||
const preview = this.supportPreview(user, target, usable);
|
||||
const marker = this.add.rectangle(
|
||||
this.tileTopLeftX(target.x),
|
||||
this.tileTopLeftY(target.y),
|
||||
@@ -4573,7 +4690,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
hitArea.on('pointerover', () => {
|
||||
marker.setFillStyle(usable.effect === 'heal' ? 0x45b875 : 0x4f86d9, 0.34);
|
||||
marker.setStrokeStyle(3, usable.effect === 'heal' ? 0xd9ffe8 : 0xf2f7ff, 1);
|
||||
this.renderSupportPreview(user, target, usable);
|
||||
this.renderSupportPreview(preview);
|
||||
});
|
||||
hitArea.on('pointerout', () => {
|
||||
marker.setFillStyle(usable.effect === 'heal' ? 0x45b875 : 0x4f86d9, 0.2);
|
||||
@@ -4647,17 +4764,24 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.renderCombatPreviewCard(preview);
|
||||
}
|
||||
|
||||
private renderSupportPreview(user: UnitData, target: UnitData, usable: BattleUsable) {
|
||||
if (usable.effect === 'heal') {
|
||||
const amount = this.supportHealAmount(user, target, usable);
|
||||
this.renderUnitDetail(target, `${user.name} ${usable.name} 예측\n병력 +${amount} / ${target.hp} -> ${Math.min(target.maxHp, target.hp + amount)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderUnitDetail(
|
||||
private supportPreview(user: UnitData, target: UnitData, usable: BattleUsable): SupportPreview {
|
||||
const healAmount = usable.effect === 'heal' ? this.supportHealAmount(user, target, usable) : 0;
|
||||
return {
|
||||
usable,
|
||||
user,
|
||||
target,
|
||||
`${user.name} ${usable.name} 예측\n공격 +${usable.attackBonus ?? 0} / 명중 +${usable.hitBonus ?? 0} / 치명 +${usable.criticalBonus ?? 0}\n${usable.duration ?? 1}턴 동안 유지`
|
||||
);
|
||||
healAmount,
|
||||
projectedHp: Math.min(target.maxHp, target.hp + healAmount),
|
||||
attackBonus: usable.attackBonus ?? 0,
|
||||
hitBonus: usable.hitBonus ?? 0,
|
||||
criticalBonus: usable.criticalBonus ?? 0,
|
||||
duration: usable.duration ?? 1
|
||||
};
|
||||
}
|
||||
|
||||
private renderSupportPreview(preview: SupportPreview) {
|
||||
this.renderUnitDetail(preview.target);
|
||||
this.renderSupportPreviewCard(preview);
|
||||
}
|
||||
|
||||
private equipmentPreviewBonusText(preview: CombatPreview) {
|
||||
@@ -4758,6 +4882,59 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
}
|
||||
|
||||
private renderSupportPreviewCard(preview: SupportPreview) {
|
||||
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 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));
|
||||
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} 예측`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.trackSideObject(this.add.text(left + 52, y + 30, `${preview.target.name} / ${this.usableEffectLabel(preview.usable)} / ${this.usableTargetLabel(preview.usable)}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#9fb0bf'
|
||||
}));
|
||||
|
||||
const metricTop = y + 56;
|
||||
const metricGap = 6;
|
||||
const metricWidth = (width - 20 - metricGap * 2) / 3;
|
||||
if (preview.usable.effect === 'heal') {
|
||||
this.renderPreviewMetric(left + 10, metricTop, metricWidth, 'hp', '회복', `+${preview.healAmount}`, 0x59d18c);
|
||||
this.renderPreviewMetric(left + 10 + metricWidth + metricGap, metricTop, metricWidth, 'success', '적용', '100%', accent);
|
||||
this.renderPreviewMetric(left + 10 + (metricWidth + metricGap) * 2, metricTop, metricWidth, 'move', '거리', `${preview.usable.range}`, accent);
|
||||
} else {
|
||||
this.renderPreviewMetric(left + 10, metricTop, metricWidth, 'attack', '공격', `+${preview.attackBonus}`, 0xd8b15f);
|
||||
this.renderPreviewMetric(left + 10 + metricWidth + metricGap, metricTop, metricWidth, 'hit', '명중', `+${preview.hitBonus}`, accent);
|
||||
this.renderPreviewMetric(left + 10 + (metricWidth + metricGap) * 2, metricTop, metricWidth, 'critical', '치명', `+${preview.criticalBonus}`, 0xd8732c);
|
||||
}
|
||||
|
||||
this.renderSupportForecastRow(preview, left + 10, y + 98, width - 20, accent);
|
||||
|
||||
const badgeY = y + 126;
|
||||
const maxBadgeRight = left + width - 10;
|
||||
let badgeX = left + 10;
|
||||
badgeX = this.renderPreviewBadge(badgeX, badgeY, this.usableIcon(preview.usable), this.usableEffectLabel(preview.usable), maxBadgeRight);
|
||||
badgeX = this.renderPreviewBadge(badgeX, badgeY, 'move', `사거리 ${preview.usable.range}`, maxBadgeRight);
|
||||
badgeX = this.renderPreviewBadge(badgeX, badgeY, 'success', preview.usable.command === 'item' ? '도구 사용' : '책략 사용', maxBadgeRight);
|
||||
if (preview.usable.effect === 'focus') {
|
||||
this.renderPreviewBadge(badgeX, badgeY, 'leadership', `${preview.duration}턴`, maxBadgeRight);
|
||||
}
|
||||
}
|
||||
|
||||
private renderPreviewEquipmentChip(
|
||||
x: number,
|
||||
y: number,
|
||||
@@ -4837,6 +5014,45 @@ export class BattleScene extends Phaser.Scene {
|
||||
hpText.setOrigin(1, 0);
|
||||
}
|
||||
|
||||
private renderSupportForecastRow(preview: SupportPreview, x: number, y: number, width: number, accent: number) {
|
||||
const row = this.trackSideObject(this.add.rectangle(x, y, width, 22, 0x0b1118, 0.94));
|
||||
row.setOrigin(0);
|
||||
row.setStrokeStyle(1, accent, 0.48);
|
||||
const isHeal = preview.usable.effect === 'heal';
|
||||
this.trackSideIcon(x + 12, y + 11, isHeal ? 'hp' : 'leadership', 17);
|
||||
this.trackSideObject(this.add.text(x + 26, y + 5, isHeal ? '적용 후 HP' : '강화 결과', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '11px',
|
||||
color: '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
|
||||
if (isHeal) {
|
||||
this.drawGauge(x + 100, y + 7, width - 178, 8, preview.projectedHp / preview.target.maxHp, 0x59d18c);
|
||||
const hpText = this.trackSideObject(this.add.text(x + width - 8, y + 5, `${preview.target.hp} -> ${preview.projectedHp}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
hpText.setOrigin(1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const buffText = this.trackSideObject(this.add.text(
|
||||
x + width - 8,
|
||||
y + 5,
|
||||
`공+${preview.attackBonus} / 명+${preview.hitBonus} / 치+${preview.criticalBonus} / ${preview.duration}턴`,
|
||||
{
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700'
|
||||
}
|
||||
));
|
||||
buffText.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) {
|
||||
@@ -7844,22 +8060,23 @@ export class BattleScene extends Phaser.Scene {
|
||||
}));
|
||||
title.setOrigin(0.5, 0);
|
||||
title.setDepth(depth + 2);
|
||||
this.renderSupportActionRibbon(result, left + panelWidth / 2 - 220, top + 58, 440, depth + 9);
|
||||
|
||||
const supportPose: UnitActionPose = result.usable.command === 'strategy' ? 'strategy' : 'item';
|
||||
const userSprite = this.trackCombatObject(
|
||||
this.add.sprite(left + 160, top + 146, this.unitActionTexture(result.user), this.unitActionFrameIndex('east', supportPose))
|
||||
this.add.sprite(left + 160, top + 156, this.unitActionTexture(result.user), this.unitActionFrameIndex('east', supportPose))
|
||||
);
|
||||
userSprite.setDepth(depth + 3);
|
||||
this.applyCombatSpriteDisplaySize(userSprite, result.user, true);
|
||||
this.applyActionSpriteBlend(userSprite);
|
||||
void this.playUnitActionFrames(userSprite, result.user, 'east', supportPose, 100, 2);
|
||||
|
||||
const targetSprite = this.trackCombatObject(this.add.sprite(left + panelWidth - 160, top + 146, this.unitTextureKey(result.target), this.unitFrameIndex('west')));
|
||||
const targetSprite = this.trackCombatObject(this.add.sprite(left + panelWidth - 160, top + 156, this.unitTextureKey(result.target), this.unitFrameIndex('west')));
|
||||
targetSprite.setDepth(depth + 3);
|
||||
this.applyCombatSpriteDisplaySize(targetSprite, result.target, true);
|
||||
|
||||
const effectColor = result.usable.effect === 'heal' ? 0x59d18c : 0xd8b15f;
|
||||
const effect = this.trackCombatObject(this.add.circle(left + panelWidth / 2, top + 145, 30, effectColor, 0.76));
|
||||
const effect = this.trackCombatObject(this.add.circle(left + panelWidth / 2, top + 156, 30, effectColor, 0.76));
|
||||
effect.setDepth(depth + 4);
|
||||
this.tweens.add({ targets: effect, scale: 1.45, alpha: 0.18, duration: 520, yoyo: true });
|
||||
soundDirector.playEffect(result.usable.command === 'strategy' ? 'strategy-cast' : 'exp-gain', {
|
||||
@@ -7867,7 +8084,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
stopAfterMs: 900
|
||||
});
|
||||
|
||||
const targetStatus = this.renderCombatStatusPanel(result.target, left + panelWidth / 2 - 196, top + 190, 392);
|
||||
const targetStatus = this.renderCombatStatusPanel(result.target, left + panelWidth / 2 - 196, top + 202, 392);
|
||||
targetStatus.hpFill.setScale(result.previousTargetHp / result.target.maxHp, 1);
|
||||
targetStatus.hpText.setText(`${result.previousTargetHp} / ${result.target.maxHp}`);
|
||||
const growthEntries = this.supportGrowthEntries(result);
|
||||
@@ -7880,7 +8097,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
result.usable.effect === 'heal'
|
||||
? `${result.target.name} 병력 +${result.healAmount}`
|
||||
: `${result.target.name} ${result.usable.name}: 공격 +${result.buff?.attackBonus ?? 0} / 명중 +${result.buff?.hitBonus ?? 0} / 치명 +${result.buff?.criticalBonus ?? 0}`;
|
||||
const resultText = this.trackCombatObject(this.add.text(left + panelWidth / 2, top + 106, resultLine, {
|
||||
const resultText = this.trackCombatObject(this.add.text(left + panelWidth / 2, top + 132, resultLine, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '19px',
|
||||
color: result.usable.effect === 'heal' ? '#a8ffd0' : '#ffdf7b',
|
||||
@@ -7890,7 +8107,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
}));
|
||||
resultText.setOrigin(0.5);
|
||||
resultText.setDepth(depth + 5);
|
||||
void this.playImmediateGrowthPopups(growthEntries, left + panelWidth / 2, top + 156, depth + 8);
|
||||
void this.playImmediateGrowthPopups(growthEntries, left + panelWidth / 2, top + 178, depth + 8);
|
||||
|
||||
await this.playGrowthRewardPanel({
|
||||
x: left + 64,
|
||||
@@ -7906,6 +8123,87 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.hideCombatCutIn();
|
||||
}
|
||||
|
||||
private renderSupportActionRibbon(result: SupportResult, x: number, y: number, width: number, depth: number) {
|
||||
const accent = this.usableAccentColor(result.usable);
|
||||
const bg = this.trackCombatObject(this.add.rectangle(x, y, width, 64, 0x0b1118, 0.92));
|
||||
bg.setOrigin(0);
|
||||
bg.setDepth(depth);
|
||||
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, accent, 0.68);
|
||||
this.trackCombatIcon(x + 24, y + 22, this.usableIcon(result.usable), 31, depth + 2);
|
||||
|
||||
const sourceItem = getItem(result.usable.command === 'strategy' ? result.user.equipment.weapon.itemId : result.user.equipment.accessory.itemId);
|
||||
const sourceSlot: EquipmentSlot = result.usable.command === 'strategy' ? 'weapon' : 'accessory';
|
||||
const sourceFrame = this.trackCombatObject(this.add.rectangle(x + 136, y + 22, 30, 30, 0x16212d, 0.9));
|
||||
sourceFrame.setDepth(depth + 1);
|
||||
sourceFrame.setStrokeStyle(1, 0x647485, 0.58);
|
||||
this.trackCombatIcon(x + 136, y + 22, this.itemIcon(sourceItem, sourceSlot), 25, depth + 2);
|
||||
|
||||
const targetFrame = this.trackCombatObject(this.add.rectangle(x + 256, y + 22, 30, 30, 0x16212d, 0.9));
|
||||
targetFrame.setDepth(depth + 1);
|
||||
targetFrame.setStrokeStyle(1, 0x647485, 0.58);
|
||||
this.trackCombatIcon(x + 256, y + 22, result.usable.effect === 'heal' ? 'hp' : 'leadership', 25, depth + 2);
|
||||
|
||||
const actionLabel = this.trackCombatObject(this.add.text(x + 46, y + 9, result.usable.name, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '15px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 74
|
||||
}));
|
||||
actionLabel.setDepth(depth + 1);
|
||||
|
||||
const sourceLabel = this.trackCombatObject(this.add.text(x + 156, y + 7, sourceItem.name, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#d4dce6',
|
||||
fixedWidth: 90
|
||||
}));
|
||||
sourceLabel.setDepth(depth + 1);
|
||||
const sourceRole = this.trackCombatObject(this.add.text(x + 156, y + 25, result.usable.command === 'strategy' ? '책략 도구' : '소모 도구', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#9fb0bf',
|
||||
fixedWidth: 90
|
||||
}));
|
||||
sourceRole.setDepth(depth + 1);
|
||||
|
||||
const targetLabel = this.trackCombatObject(this.add.text(x + 276, y + 7, result.target.name, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#d4dce6',
|
||||
fixedWidth: 74
|
||||
}));
|
||||
targetLabel.setDepth(depth + 1);
|
||||
const targetRole = this.trackCombatObject(this.add.text(x + 276, y + 25, this.usableTargetLabel(result.usable), {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#9fb0bf',
|
||||
fixedWidth: 74
|
||||
}));
|
||||
targetRole.setDepth(depth + 1);
|
||||
|
||||
const resultLabel =
|
||||
result.usable.effect === 'heal'
|
||||
? `회복 +${result.healAmount}`
|
||||
: `강화 ${result.buff?.turns ?? result.usable.duration ?? 1}턴`;
|
||||
const resultText = this.trackCombatObject(this.add.text(x + width - 14, y + 10, resultLabel, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: result.usable.effect === 'heal' ? '#a8ffd0' : '#ffdf7b',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
resultText.setOrigin(1, 0);
|
||||
resultText.setDepth(depth + 1);
|
||||
|
||||
this.renderCombatRibbonBadge(x + 46, y + 38, this.usableIcon(result.usable), this.usableEffectLabel(result.usable), depth + 1, 68);
|
||||
this.renderCombatRibbonBadge(x + 120, y + 38, 'success', '적용', depth + 1, 58);
|
||||
this.renderCombatRibbonBadge(x + 184, y + 38, 'move', `거리 ${result.usable.range}`, depth + 1, 66);
|
||||
}
|
||||
|
||||
private renderCombatStatusPanel(unit: UnitData, x: number, y: number, width: number, growth?: CharacterGrowthResult) {
|
||||
const panel = this.trackCombatObject(this.add.rectangle(x, y, width, 116, 0x101820, 0.96));
|
||||
panel.setOrigin(0);
|
||||
|
||||
Reference in New Issue
Block a user