Improve combat result feedback
This commit is contained in:
@@ -7729,27 +7729,27 @@ export class BattleScene extends Phaser.Scene {
|
||||
const visibleGains = gains.filter((gain) => gain.amount > 0).slice(0, 5);
|
||||
let cursorX = x;
|
||||
visibleGains.forEach((gain) => {
|
||||
const chipWidth = Phaser.Math.Clamp(gain.label.length * 10 + 52, 72, 96);
|
||||
const chipWidth = Phaser.Math.Clamp(gain.label.length * 11 + 62, 86, 116);
|
||||
if (cursorX + chipWidth > x + maxWidth) {
|
||||
return;
|
||||
}
|
||||
const bg = this.add.rectangle(cursorX, y, chipWidth, 30, 0x0b1118, 0.92);
|
||||
const bg = this.add.rectangle(cursorX, y, chipWidth, 34, 0x0b1118, 0.94);
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(1, 0x53606c, 0.62);
|
||||
const icon = this.createBattleUiIcon(cursorX + 16, y + 15, gain.icon, 24);
|
||||
const text = this.add.text(cursorX + 32, y + 7, `+${gain.amount}`, {
|
||||
const icon = this.createBattleUiIcon(cursorX + 18, y + 17, gain.icon, 28);
|
||||
const text = this.add.text(cursorX + 36, y + 7, `+${gain.amount}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
fontSize: '15px',
|
||||
color: '#a8ffd0',
|
||||
fontStyle: '700'
|
||||
});
|
||||
const label = this.add.text(cursorX + 55, y + 8, gain.label, {
|
||||
const label = this.add.text(cursorX + 65, y + 9, gain.label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '11px',
|
||||
fontSize: '12px',
|
||||
color: '#d4dce6'
|
||||
});
|
||||
container.add([bg, icon, text, label]);
|
||||
cursorX += chipWidth + 5;
|
||||
cursorX += chipWidth + 6;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8271,13 +8271,14 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private formatCombatResult(result: CombatResult) {
|
||||
const outcome = this.formatCombatOutcome(result);
|
||||
const defeated = result.defeated ? `\n${result.defender.name} 퇴각!` : `\n${result.defender.name} HP ${result.defender.hp}/${result.defender.maxHp}`;
|
||||
const bondBonus = result.bondDamageBonus > 0 && result.bondLabel ? `\n공명 효과 ${result.bondLabel}: 피해 +${result.bondDamageBonus}%` : '';
|
||||
const equipment = result.equipmentEffectLabels.length > 0 ? `\n보물 효과: ${result.equipmentEffectLabels.join(', ')}` : '';
|
||||
const bond = result.bondExp > 0 ? `\n공명 경험치 +${result.bondExp}` : '';
|
||||
const counter = result.counter ? `\n${this.formatCounterLine(result.counter)}` : '';
|
||||
return `${result.attacker.name} ${result.usable?.name ?? commandLabels[result.action]}\n${outcome}${defeated}${bondBonus}${equipment}\n${this.formatCharacterGrowth(result.characterGrowth)}\n${this.formatEquipmentGrowth(result.attackerGrowth)}\n${this.formatEquipmentGrowth(result.defenderGrowth)}${bond}${counter}`;
|
||||
const summaryOutcome = this.formatCombatOutcome(result);
|
||||
const summaryDefeated = result.defeated ? `\n${result.defender.name} 격파!` : `\n${result.defender.name} HP ${result.defender.hp}/${result.defender.maxHp}`;
|
||||
const summaryBondBonus = result.bondDamageBonus > 0 && result.bondLabel ? `\n공명 효과 ${result.bondLabel}: 피해 +${result.bondDamageBonus}%` : '';
|
||||
const summaryEquipment = result.equipmentEffectLabels.length > 0 ? `\n장비 효과: ${result.equipmentEffectLabels.join(', ')}` : '';
|
||||
const summaryBond = result.bondExp > 0 ? `\n공명 경험치 +${result.bondExp}` : '';
|
||||
const summaryCounter = result.counter ? `\n${this.formatCounterLine(result.counter)}` : '';
|
||||
return `${result.attacker.name} ${result.usable?.name ?? commandLabels[result.action]}\n${summaryOutcome}${summaryDefeated}${summaryBondBonus}${summaryEquipment}\n${this.formatCharacterGrowth(result.characterGrowth)}\n${this.formatEquipmentGrowth(result.attackerGrowth)}\n${this.formatEquipmentGrowth(result.defenderGrowth)}${summaryBond}${summaryCounter}`;
|
||||
|
||||
}
|
||||
|
||||
private formatCombatOutcome(result: CombatResult) {
|
||||
@@ -8285,17 +8286,45 @@ export class BattleScene extends Phaser.Scene {
|
||||
return `${result.defender.name}이 공격을 회피했습니다.`;
|
||||
}
|
||||
|
||||
const summaryCritical = result.critical ? '치명타! ' : '';
|
||||
return `${summaryCritical}${result.defender.name}에게 ${result.damage} 피해`;
|
||||
|
||||
if (!result.hit) {
|
||||
return `${result.defender.name}이 공격을 회피했습니다.`;
|
||||
}
|
||||
|
||||
const critical = result.critical ? '치명타! ' : '';
|
||||
return `${critical}${result.defender.name}에게 ${result.damage} 피해`;
|
||||
}
|
||||
|
||||
private formatCounterLine(result: CombatResult) {
|
||||
const counterOutcome = result.hit ? `${result.critical ? '치명타로 ' : ''}${result.damage} 피해` : '회피';
|
||||
const counterDefeated = result.defeated ? ` / ${result.defender.name} 격파` : ` / ${result.defender.name} HP ${result.defender.hp}/${result.defender.maxHp}`;
|
||||
return `반격: ${result.attacker.name} -> ${result.defender.name} ${counterOutcome}${counterDefeated}`;
|
||||
|
||||
const outcome = result.hit ? `${result.critical ? '치명타로 ' : ''}${result.damage} 피해` : '회피됨';
|
||||
const defeated = result.defeated ? ` / ${result.defender.name} 퇴각` : ` / ${result.defender.name} HP ${result.defender.hp}/${result.defender.maxHp}`;
|
||||
return `반격(감소): ${result.attacker.name} -> ${result.defender.name} ${outcome}${defeated}`;
|
||||
}
|
||||
|
||||
private combatGrowthLines(result: CombatResult) {
|
||||
const summaryVerdict = result.hit ? `${result.critical ? '치명타 / ' : ''}명중 ${result.hitRate}%` : `회피 / 명중 ${result.hitRate}%`;
|
||||
const summaryLines = [
|
||||
summaryVerdict,
|
||||
this.formatCharacterGrowth(result.characterGrowth),
|
||||
this.formatEquipmentGrowth(result.attackerGrowth),
|
||||
this.formatEquipmentGrowth(result.defenderGrowth)
|
||||
];
|
||||
|
||||
if (result.bondExp > 0) {
|
||||
summaryLines.push(`공명 경험치 +${result.bondExp}`);
|
||||
}
|
||||
if (result.equipmentEffectLabels.length > 0) {
|
||||
summaryLines.push(`장비 효과: ${result.equipmentEffectLabels.join(', ')}`);
|
||||
}
|
||||
|
||||
return summaryLines;
|
||||
|
||||
const verdict = result.hit ? `${result.critical ? '치명타 / ' : ''}명중 ${result.hitRate}%` : `회피됨 / 명중 ${result.hitRate}%`;
|
||||
const lines = [
|
||||
verdict,
|
||||
@@ -8315,6 +8344,12 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private formatSupportResult(result: SupportResult) {
|
||||
const supportEffect =
|
||||
result.usable.effect === 'heal'
|
||||
? `${result.target.name} 병력 +${result.healAmount} (${result.target.hp}/${result.target.maxHp})`
|
||||
: `${result.target.name} 강화: 공격 +${result.buff?.attackBonus ?? 0} / 명중 +${result.buff?.hitBonus ?? 0} / 치명 +${result.buff?.criticalBonus ?? 0}`;
|
||||
return `${result.user.name} ${result.usable.name}\n${supportEffect}\n${this.formatCharacterGrowth(result.characterGrowth)}\n${this.formatEquipmentGrowth(result.equipmentGrowth)}`;
|
||||
|
||||
const effect =
|
||||
result.usable.effect === 'heal'
|
||||
? `${result.target.name} 병력 +${result.healAmount} (${result.target.hp}/${result.target.maxHp})`
|
||||
@@ -9574,7 +9609,8 @@ export class BattleScene extends Phaser.Scene {
|
||||
stage.fillRect(left + 18, top + 82, panelWidth - 36, 18);
|
||||
|
||||
const titleText = result.isCounter ? '반격' : result.usable?.name ?? commandLabels[result.action];
|
||||
const title = this.trackCombatObject(this.add.text(left + panelWidth / 2, top + 24, titleText, {
|
||||
const displayTitleText = result.isCounter ? '반격' : titleText;
|
||||
const title = this.trackCombatObject(this.add.text(left + panelWidth / 2, top + 24, displayTitleText, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '24px',
|
||||
color: '#f4dfad',
|
||||
@@ -9625,7 +9661,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
} else {
|
||||
this.showCombatMiss(defenderX, groundY - 70, depth + 14);
|
||||
}
|
||||
const outcomeCard = this.renderCombatOutcomeCard(result, left + panelWidth / 2 - 198, top + 214, 396, depth + 16);
|
||||
const outcomeCard = this.renderCombatOutcomeCard(result, left + panelWidth / 2 - 226, top + 196, 452, depth + 16);
|
||||
|
||||
await this.animateCombatGauge(defenderStatus.hpFill, result.previousDefenderHp / result.defender.maxHp, result.defender.hp / result.defender.maxHp, 420);
|
||||
defenderStatus.hpText.setText(`${result.defender.hp} / ${result.defender.maxHp}`);
|
||||
@@ -9660,7 +9696,117 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.hideCombatCutIn();
|
||||
}
|
||||
|
||||
private renderCombatOutcomeSummaryCard(result: CombatResult, x: number, y: number, width: number, depth: number) {
|
||||
const height = 84;
|
||||
const isStrategy = result.action === 'strategy';
|
||||
const icon: BattleUiIconKey = result.defeated
|
||||
? 'critical'
|
||||
: !result.hit
|
||||
? 'move'
|
||||
: result.critical
|
||||
? 'critical'
|
||||
: isStrategy
|
||||
? 'success'
|
||||
: 'hit';
|
||||
const title = result.defeated
|
||||
? '격파'
|
||||
: !result.hit
|
||||
? isStrategy
|
||||
? '책략 실패'
|
||||
: '회피'
|
||||
: result.critical
|
||||
? '치명타'
|
||||
: isStrategy
|
||||
? '책략 성공'
|
||||
: '명중';
|
||||
const tone = result.defeated || result.critical ? 0xd8732c : result.hit ? this.previewAccentColor(result) : 0x83d6ff;
|
||||
const container = this.trackCombatObject(this.add.container(x, y));
|
||||
container.setDepth(depth);
|
||||
container.setAlpha(0);
|
||||
container.setScale(0.96);
|
||||
|
||||
const bg = this.add.rectangle(0, 0, width, height, 0x0b121a, 0.98);
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(2, tone, 0.88);
|
||||
|
||||
const iconFrame = this.add.rectangle(42, height / 2, 64, 64, 0x101820, 0.94);
|
||||
iconFrame.setStrokeStyle(1, tone, 0.72);
|
||||
const iconImage = this.createBattleUiIcon(42, height / 2, icon, 58);
|
||||
|
||||
const titleText = this.add.text(84, 10, title, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '23px',
|
||||
color: result.hit ? '#fff2b8' : '#d9f1ff',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 150
|
||||
});
|
||||
|
||||
const detail = result.defeated ? `${result.defender.name} 전투 불능` : `${result.defender.name} HP ${result.defender.hp}/${result.defender.maxHp}`;
|
||||
const detailText = this.add.text(86, 42, detail, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: result.defeated ? '#ffb6a6' : '#c8d2dd',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 190
|
||||
});
|
||||
|
||||
const value = result.hit ? `-${result.damage}` : '0';
|
||||
const valueText = this.add.text(width - 18, 9, value, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '31px',
|
||||
color: result.hit ? '#ffdf7b' : '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
});
|
||||
valueText.setOrigin(1, 0);
|
||||
|
||||
const valueLabel = this.add.text(width - 18, 47, '피해', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
});
|
||||
valueLabel.setOrigin(1, 0);
|
||||
|
||||
container.add([bg, iconFrame, iconImage, titleText, detailText, valueText, valueLabel]);
|
||||
|
||||
const badgeY = 64;
|
||||
this.addCombatOutcomeChip(container, 86, badgeY, result.action === 'strategy' ? 'success' : 'hit', result.hit ? `${result.hitRate}%` : '실패', tone);
|
||||
this.addCombatOutcomeChip(container, 154, badgeY, 'critical', result.critical ? '치명' : `${result.criticalRate}%`, 0xd8732c);
|
||||
this.addCombatOutcomeChip(container, 222, badgeY, 'advantage', result.matchupLabel, palette.blue, 86);
|
||||
if (result.bondDamageBonus > 0) {
|
||||
this.addCombatOutcomeChip(container, width - 114, badgeY, 'leadership', `공명 +${result.bondDamageBonus}%`, palette.gold, 96);
|
||||
}
|
||||
|
||||
this.tweens.add({ targets: container, alpha: 1, scale: 1, y: y - 4, duration: 160, ease: 'Sine.easeOut' });
|
||||
return container;
|
||||
}
|
||||
|
||||
private addCombatOutcomeChip(
|
||||
container: Phaser.GameObjects.Container,
|
||||
x: number,
|
||||
y: number,
|
||||
icon: BattleUiIconKey,
|
||||
label: string,
|
||||
tone: number,
|
||||
width = 62
|
||||
) {
|
||||
const bg = this.add.rectangle(x, y, width, 22, 0x101820, 0.94);
|
||||
bg.setOrigin(0, 0.5);
|
||||
bg.setStrokeStyle(1, tone, 0.54);
|
||||
const chipIcon = this.createBattleUiIcon(x + 13, y, icon, 19);
|
||||
const text = this.add.text(x + 27, y - 6, this.truncateUiText(label, width > 70 ? 8 : 5), {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700',
|
||||
fixedWidth: width - 30
|
||||
});
|
||||
container.add([bg, chipIcon, text]);
|
||||
}
|
||||
|
||||
private renderCombatOutcomeCard(result: CombatResult, x: number, y: number, width: number, depth: number) {
|
||||
return this.renderCombatOutcomeSummaryCard(result, x, y, width, depth);
|
||||
|
||||
const height = 66;
|
||||
const isStrategy = result.action === 'strategy';
|
||||
const icon: BattleUiIconKey = result.defeated
|
||||
@@ -9759,7 +9905,86 @@ export class BattleScene extends Phaser.Scene {
|
||||
await this.delay(170);
|
||||
}
|
||||
|
||||
private renderCombatActionSummaryRibbon(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.94));
|
||||
bg.setOrigin(0);
|
||||
bg.setDepth(depth);
|
||||
bg.setStrokeStyle(1, accent, 0.72);
|
||||
|
||||
const actionFrame = this.trackCombatObject(this.add.rectangle(x + 25, y + 23, 42, 42, 0x101820, 0.9));
|
||||
actionFrame.setDepth(depth + 1);
|
||||
actionFrame.setStrokeStyle(1, accent, 0.68);
|
||||
this.trackCombatIcon(x + 25, y + 23, this.actionIcon(result), 37, depth + 2);
|
||||
|
||||
const attackerWeapon = getItem(result.attacker.equipment.weapon.itemId);
|
||||
const defenderArmor = getItem(result.defender.equipment.armor.itemId);
|
||||
const dealtDamage = result.hit ? result.damage : 0;
|
||||
|
||||
this.renderCombatRibbonEquipment(x + 120, y + 8, this.itemIcon(attackerWeapon, 'weapon'), attackerWeapon.name, '공격 장비', depth + 1);
|
||||
this.renderCombatRibbonEquipment(x + 250, y + 8, this.itemIcon(defenderArmor, 'armor'), defenderArmor.name, '방어 장비', depth + 1);
|
||||
|
||||
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',
|
||||
fontSize: '15px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 72
|
||||
}));
|
||||
actionLabel.setDepth(depth + 1);
|
||||
|
||||
const damageText = this.trackCombatObject(this.add.text(x + width - 14, y + 8, `피해 ${dealtDamage}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '15px',
|
||||
color: result.hit ? '#ffcf91' : '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
damageText.setOrigin(1, 0);
|
||||
damageText.setDepth(depth + 1);
|
||||
|
||||
const outcomeIcon = result.action === 'strategy' ? 'success' : result.critical ? 'critical' : result.hit ? 'hit' : 'move';
|
||||
const outcomeLabel = result.action === 'strategy' ? (result.hit ? '성공' : '실패') : result.hit ? (result.critical ? '치명' : '명중') : '회피';
|
||||
this.renderCombatRibbonBadge(x + 46, y + 38, this.actionIcon(result), this.previewActionTypeLabel(result), depth + 1, 78);
|
||||
this.renderCombatRibbonBadge(x + 130, y + 38, outcomeIcon, outcomeLabel, depth + 1, 62);
|
||||
this.renderCombatRibbonBadge(x + 198, y + 38, 'advantage', result.matchupLabel, depth + 1, 76);
|
||||
if (result.counter) {
|
||||
this.renderCombatRibbonBadge(x + 280, y + 38, 'counter', '반격', depth + 1, 58);
|
||||
}
|
||||
}
|
||||
|
||||
private renderCombatRibbonEquipment(
|
||||
x: number,
|
||||
y: number,
|
||||
icon: BattleUiIconKey,
|
||||
itemName: string,
|
||||
role: string,
|
||||
depth: number
|
||||
) {
|
||||
const frame = this.trackCombatObject(this.add.rectangle(x + 14, y + 14, 34, 34, 0x16212d, 0.9));
|
||||
frame.setDepth(depth);
|
||||
frame.setStrokeStyle(1, 0x647485, 0.58);
|
||||
this.trackCombatIcon(x + 14, y + 14, icon, 30, depth + 1);
|
||||
|
||||
const label = this.trackCombatObject(this.add.text(x + 34, y - 1, this.truncateUiText(itemName, 7), {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#d4dce6',
|
||||
fixedWidth: 84
|
||||
}));
|
||||
label.setDepth(depth);
|
||||
const roleText = this.trackCombatObject(this.add.text(x + 34, y + 17, role, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#9fb0bf',
|
||||
fixedWidth: 84
|
||||
}));
|
||||
roleText.setDepth(depth);
|
||||
}
|
||||
|
||||
private renderCombatActionRibbon(result: CombatResult, x: number, y: number, width: number, depth: number) {
|
||||
this.renderCombatActionSummaryRibbon(result, x, y, width, depth);
|
||||
return;
|
||||
|
||||
const accent = this.previewAccentColor(result);
|
||||
const bg = this.trackCombatObject(this.add.rectangle(x, y, width, 64, 0x0b1118, 0.92));
|
||||
bg.setOrigin(0);
|
||||
@@ -9937,7 +10162,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
const growthEntries = this.supportGrowthEntries(result);
|
||||
|
||||
await this.delay(250);
|
||||
const outcomeCard = this.renderSupportOutcomeCard(result, left + panelWidth / 2 - 190, top + 116, 380, depth + 5);
|
||||
const outcomeCard = this.renderSupportOutcomeCard(result, left + panelWidth / 2 - 216, top + 108, 432, depth + 5);
|
||||
await this.animateCombatGauge(targetStatus.hpFill, result.previousTargetHp / result.target.maxHp, result.target.hp / result.target.maxHp, 480);
|
||||
targetStatus.hpText.setText(`${result.target.hp} / ${result.target.maxHp}`);
|
||||
|
||||
@@ -9956,7 +10181,72 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.hideCombatCutIn();
|
||||
}
|
||||
|
||||
private renderSupportOutcomeSummaryCard(result: SupportResult, x: number, y: number, width: number, depth: number) {
|
||||
const height = 84;
|
||||
const heal = result.usable.effect === 'heal';
|
||||
const tone = heal ? 0x59d18c : 0xd8b15f;
|
||||
const container = this.trackCombatObject(this.add.container(x, y));
|
||||
container.setDepth(depth);
|
||||
container.setAlpha(0);
|
||||
container.setScale(0.96);
|
||||
|
||||
const bg = this.add.rectangle(0, 0, width, height, 0x0b121a, 0.98);
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(2, tone, 0.88);
|
||||
|
||||
const iconFrame = this.add.rectangle(42, height / 2, 64, 64, 0x101820, 0.94);
|
||||
iconFrame.setStrokeStyle(1, tone, 0.72);
|
||||
const iconImage = this.createBattleUiIcon(42, height / 2, this.usablePowerIcon(result.usable), 58);
|
||||
|
||||
const titleText = this.add.text(84, 10, heal ? '병력 회복' : '강화 적용', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '23px',
|
||||
color: heal ? '#a8ffd0' : '#fff2b8',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 156
|
||||
});
|
||||
|
||||
const detail = heal
|
||||
? `${result.target.name} HP ${result.previousTargetHp} → ${result.target.hp}`
|
||||
: `${result.target.name} 공+${result.buff?.attackBonus ?? 0} 명+${result.buff?.hitBonus ?? 0} 치+${result.buff?.criticalBonus ?? 0}`;
|
||||
const detailText = this.add.text(86, 42, detail, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 218
|
||||
});
|
||||
|
||||
const value = heal ? `+${result.healAmount}` : `${result.buff?.turns ?? result.usable.duration ?? 1}`;
|
||||
const valueText = this.add.text(width - 18, 9, value, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '31px',
|
||||
color: heal ? '#a8ffd0' : '#ffdf7b',
|
||||
fontStyle: '700'
|
||||
});
|
||||
valueText.setOrigin(1, 0);
|
||||
|
||||
const valueLabel = this.add.text(width - 18, 47, heal ? '회복' : '턴', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
color: '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
});
|
||||
valueLabel.setOrigin(1, 0);
|
||||
|
||||
container.add([bg, iconFrame, iconImage, titleText, detailText, valueText, valueLabel]);
|
||||
const badgeY = 64;
|
||||
this.addCombatOutcomeChip(container, 86, badgeY, this.usableIcon(result.usable), this.usableEffectLabel(result.usable), tone, 78);
|
||||
this.addCombatOutcomeChip(container, 170, badgeY, 'success', '적용', 0x59d18c, 62);
|
||||
this.addCombatOutcomeChip(container, 238, badgeY, this.usableTargetIcon(result.usable), `${this.usableTargetLabel(result.usable)} ${result.usable.range}칸`, palette.blue, 92);
|
||||
|
||||
this.tweens.add({ targets: container, alpha: 1, scale: 1, y: y - 4, duration: 160, ease: 'Sine.easeOut' });
|
||||
return container;
|
||||
}
|
||||
|
||||
private renderSupportOutcomeCard(result: SupportResult, x: number, y: number, width: number, depth: number) {
|
||||
return this.renderSupportOutcomeSummaryCard(result, x, y, width, depth);
|
||||
|
||||
const height = 66;
|
||||
const heal = result.usable.effect === 'heal';
|
||||
const tone = heal ? 0x59d18c : 0xd8b15f;
|
||||
@@ -10007,7 +10297,61 @@ export class BattleScene extends Phaser.Scene {
|
||||
return container;
|
||||
}
|
||||
|
||||
private renderSupportActionSummaryRibbon(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.94));
|
||||
bg.setOrigin(0);
|
||||
bg.setDepth(depth);
|
||||
bg.setStrokeStyle(1, accent, 0.72);
|
||||
|
||||
const actionFrame = this.trackCombatObject(this.add.rectangle(x + 25, y + 23, 42, 42, 0x101820, 0.9));
|
||||
actionFrame.setDepth(depth + 1);
|
||||
actionFrame.setStrokeStyle(1, accent, 0.68);
|
||||
this.trackCombatIcon(x + 25, y + 23, this.usableIcon(result.usable), 37, 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';
|
||||
this.renderCombatRibbonEquipment(
|
||||
x + 120,
|
||||
y + 8,
|
||||
this.itemIcon(sourceItem, sourceSlot),
|
||||
sourceItem.name,
|
||||
result.usable.command === 'strategy' ? '책략 도구' : '소모 도구',
|
||||
depth + 1
|
||||
);
|
||||
this.renderCombatRibbonEquipment(x + 254, y + 8, this.usablePowerIcon(result.usable), result.target.name, this.usableTargetLabel(result.usable), depth + 1);
|
||||
|
||||
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: 72
|
||||
}));
|
||||
actionLabel.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 + 8, resultLabel, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '15px',
|
||||
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, this.usableTargetIcon(result.usable), `${this.usableTargetLabel(result.usable)} ${result.usable.range}칸`, depth + 1, 78);
|
||||
}
|
||||
|
||||
private renderSupportActionRibbon(result: SupportResult, x: number, y: number, width: number, depth: number) {
|
||||
this.renderSupportActionSummaryRibbon(result, x, y, width, depth);
|
||||
return;
|
||||
|
||||
const accent = this.usableAccentColor(result.usable);
|
||||
const bg = this.trackCombatObject(this.add.rectangle(x, y, width, 64, 0x0b1118, 0.92));
|
||||
bg.setOrigin(0);
|
||||
@@ -10121,6 +10465,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
fontStyle: '700'
|
||||
}));
|
||||
hpLabel.setDepth(85);
|
||||
hpLabel.setText('병력');
|
||||
const hpTrack = this.trackCombatObject(this.add.rectangle(x + 148, y + 54, width - 232, 9, 0x0a0f14, 0.9));
|
||||
hpTrack.setOrigin(0, 0.5);
|
||||
hpTrack.setDepth(85);
|
||||
@@ -10145,6 +10490,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
fontStyle: '700'
|
||||
}));
|
||||
expLabel.setDepth(85);
|
||||
expLabel.setText('경험');
|
||||
const expTrack = this.trackCombatObject(this.add.rectangle(x + 148, y + 84, width - 232, 9, 0x0a0f14, 0.9));
|
||||
expTrack.setOrigin(0, 0.5);
|
||||
expTrack.setDepth(85);
|
||||
@@ -10199,6 +10545,22 @@ export class BattleScene extends Phaser.Scene {
|
||||
color: 0xd8b15f,
|
||||
statGains: growth.statGains
|
||||
};
|
||||
|
||||
return {
|
||||
kind: 'character',
|
||||
label: '장수 경험치',
|
||||
owner: unit.name,
|
||||
amountLabel: `+${growth.amount}`,
|
||||
previousLevel: growth.previousLevel,
|
||||
previousExp: growth.previousExp,
|
||||
previousNext: 100,
|
||||
level: growth.level,
|
||||
exp: growth.exp,
|
||||
next: growth.next,
|
||||
leveled: growth.leveled,
|
||||
color: 0xd8b15f,
|
||||
statGains: growth.statGains
|
||||
};
|
||||
}
|
||||
|
||||
private equipmentGrowthEntry(unit: UnitData, growth: EquipmentGrowthResult): GrowthGaugeEntry {
|
||||
@@ -10234,6 +10596,21 @@ export class BattleScene extends Phaser.Scene {
|
||||
leveled: growth.leveled,
|
||||
color: 0x83d6ff
|
||||
};
|
||||
|
||||
return {
|
||||
kind: 'bond',
|
||||
label: '공명 경험치',
|
||||
owner: growth.label,
|
||||
amountLabel: `+${growth.amount}`,
|
||||
previousLevel: growth.previousLevel,
|
||||
previousExp: growth.previousExp,
|
||||
previousNext: 100,
|
||||
level: growth.level,
|
||||
exp: growth.exp,
|
||||
next: growth.next,
|
||||
leveled: growth.leveled,
|
||||
color: 0x83d6ff
|
||||
};
|
||||
}
|
||||
|
||||
private growthCelebrant(result: CombatResult) {
|
||||
@@ -10273,7 +10650,8 @@ export class BattleScene extends Phaser.Scene {
|
||||
panel.setDepth(config.depth);
|
||||
panel.setStrokeStyle(2, palette.gold, 0.82);
|
||||
|
||||
const title = this.trackCombatObject(this.add.text(config.x + 16, config.y + 12, config.title, {
|
||||
const displayTitle = config.title === '성장 결과' ? config.title : '성장 결과';
|
||||
const title = this.trackCombatObject(this.add.text(config.x + 16, config.y + 12, displayTitle, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
color: '#f4dfad',
|
||||
@@ -10342,6 +10720,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
fontStyle: '700'
|
||||
});
|
||||
levelText.setOrigin(celebrant ? 0 : 0.5, 0);
|
||||
levelText.setText(`Lv ${entry.previousLevel} → Lv ${entry.level}`);
|
||||
const statText = this.add.text(celebrant ? contentX : 0, 16, entry.statGains ? '능력 상승' : `${entry.label} 효과 강화`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
@@ -10351,6 +10730,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
strokeThickness: 3
|
||||
});
|
||||
statText.setOrigin(celebrant ? 0 : 0.5, 0);
|
||||
statText.setText(entry.statGains ? '능력 상승' : `${entry.label} 강화`);
|
||||
container.add([bg, visualFrame, glow, title, levelText, statText]);
|
||||
if (entry.statGains) {
|
||||
this.addStatGainChipsToContainer(container, entry.statGains, celebrant ? contentX : -panelWidth / 2 + 44, 50, panelWidth - 132);
|
||||
@@ -11024,6 +11404,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
lineSpacing: 2
|
||||
}));
|
||||
text.setOrigin(0.5);
|
||||
text.setText(`공명 효과 ${result.bondLabel}\n피해 +${result.bondDamageBonus}%`);
|
||||
text.setDepth(depth + 2);
|
||||
|
||||
this.tweens.add({ targets: aura, scale: 1.7, alpha: 0, duration: 560, ease: 'Sine.easeOut' });
|
||||
@@ -11065,6 +11446,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
strokeThickness: 4
|
||||
}));
|
||||
miss.setOrigin(0.5);
|
||||
miss.setText('회피');
|
||||
miss.setDepth(depth);
|
||||
this.tweens.add({
|
||||
targets: miss,
|
||||
|
||||
Reference in New Issue
Block a user