Improve battle result feedback
This commit is contained in:
@@ -1281,6 +1281,7 @@ type ResultGaugeAnimation = {
|
||||
levelText: Phaser.GameObjects.Text;
|
||||
eventText: Phaser.GameObjects.Text;
|
||||
unit?: UnitData;
|
||||
icon?: BattleUiIconKey;
|
||||
levelPrefix: string;
|
||||
previousLevel: number;
|
||||
previousExp: number;
|
||||
@@ -6829,6 +6830,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
valueText,
|
||||
levelText,
|
||||
eventText,
|
||||
icon: 'leadership',
|
||||
levelPrefix: 'Lv ',
|
||||
previousLevel,
|
||||
previousExp,
|
||||
@@ -6935,12 +6937,14 @@ export class BattleScene extends Phaser.Scene {
|
||||
characterEventText.setDepth(depth + 4);
|
||||
characterEventText.setAlpha(0);
|
||||
if (characterGained > 0 || unit.level > previousLevel) {
|
||||
const statGains = this.characterLevelUpStatGains(unit, unit.level - previousLevel);
|
||||
this.queueResultGaugeAnimation({
|
||||
fill: characterFill,
|
||||
valueText: characterValueText,
|
||||
levelText: characterLevelText,
|
||||
eventText: characterEventText,
|
||||
unit,
|
||||
icon: statGains[0]?.icon ?? 'hp',
|
||||
levelPrefix: 'Lv ',
|
||||
previousLevel,
|
||||
previousExp,
|
||||
@@ -6949,7 +6953,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
exp: unit.exp,
|
||||
next: 100,
|
||||
leveled: unit.level > previousLevel,
|
||||
statGains: this.characterLevelUpStatGains(unit, unit.level - previousLevel),
|
||||
statGains,
|
||||
flashArea: { x, y, width, height: 68, depth: depth + 3 }
|
||||
});
|
||||
}
|
||||
@@ -6997,6 +7001,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
valueText,
|
||||
levelText: label,
|
||||
eventText,
|
||||
icon: slot === 'weapon' ? 'mastery' : slot === 'armor' ? 'armor' : 'accessory',
|
||||
levelPrefix: `${equipmentSlotLabels[slot]} Lv`,
|
||||
previousLevel: previousEquipmentLevel,
|
||||
previousExp: previousEquipmentExp,
|
||||
@@ -7224,11 +7229,16 @@ export class BattleScene extends Phaser.Scene {
|
||||
container.setAlpha(0);
|
||||
container.setScale(0.9);
|
||||
|
||||
const width = 480;
|
||||
const bg = this.add.rectangle(0, 0, width, 96, 0x211624, 0.98);
|
||||
const width = 520;
|
||||
const height = 128;
|
||||
const visualX = -width / 2 + 60;
|
||||
const contentX = -width / 2 + 122;
|
||||
const bg = this.add.rectangle(0, 0, width, height, 0x211624, 0.98);
|
||||
bg.setStrokeStyle(3, palette.gold, 0.96);
|
||||
const burst = this.add.star(-width / 2 + 56, -2, 8, 14, 42, 0xffdf7b, 0.78);
|
||||
const title = this.add.text(-width / 2 + 110, -34, animation.unit ? `${animation.unit.name} LEVEL UP` : 'LEVEL UP', {
|
||||
const visualFrame = this.add.rectangle(visualX, -2, 66, 66, 0x0b1118, 0.92);
|
||||
visualFrame.setStrokeStyle(1, palette.gold, 0.72);
|
||||
const burst = this.add.star(visualX, -10, 8, 14, 42, 0xffdf7b, 0.78);
|
||||
const title = this.add.text(contentX, -48, animation.unit ? `${animation.unit.name} LEVEL UP` : 'LEVEL UP', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '27px',
|
||||
color: '#fff2b8',
|
||||
@@ -7236,13 +7246,13 @@ export class BattleScene extends Phaser.Scene {
|
||||
stroke: '#2b1606',
|
||||
strokeThickness: 5
|
||||
});
|
||||
const levelText = this.add.text(-width / 2 + 110, 0, `${animation.levelPrefix}${animation.previousLevel} → ${animation.levelPrefix}${animation.level}`, {
|
||||
const levelText = this.add.text(contentX, -12, `${animation.levelPrefix}${animation.previousLevel} → ${animation.levelPrefix}${animation.level}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '17px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700'
|
||||
});
|
||||
const statText = this.add.text(-width / 2 + 110, 22, stats ? '능력 상승' : '숙련 효과 강화', {
|
||||
const statText = this.add.text(contentX, 14, stats ? '능력 상승' : '숙련 효과 강화', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
color: stats ? '#a8ffd0' : '#d8b15f',
|
||||
@@ -7250,16 +7260,19 @@ export class BattleScene extends Phaser.Scene {
|
||||
stroke: '#05070a',
|
||||
strokeThickness: 3
|
||||
});
|
||||
container.add([bg, burst, title, levelText, statText]);
|
||||
container.add([bg, visualFrame, burst, title, levelText, statText]);
|
||||
if (animation.statGains) {
|
||||
this.addStatGainChipsToContainer(container, animation.statGains, -width / 2 + 110, 46, width - 124);
|
||||
this.addStatGainChipsToContainer(container, animation.statGains, contentX, 46, width - 132);
|
||||
}
|
||||
|
||||
if (animation.unit) {
|
||||
const sprite = this.add.sprite(-width / 2 + 56, 10, this.unitActionTexture(animation.unit), this.unitActionFrameIndex('south', this.celebrationPose(animation.unit)));
|
||||
sprite.setDisplaySize(76, 76);
|
||||
const sprite = this.add.sprite(visualX, 8, this.unitActionTexture(animation.unit), this.unitActionFrameIndex('south', this.celebrationPose(animation.unit)));
|
||||
sprite.setDisplaySize(82, 82);
|
||||
container.add(sprite);
|
||||
void this.playUnitActionFrames(sprite, animation.unit, 'south', this.celebrationPose(animation.unit), 105, 2);
|
||||
} else {
|
||||
const icon = this.createBattleUiIcon(visualX, -2, animation.icon ?? 'mastery', 54);
|
||||
container.add(icon);
|
||||
}
|
||||
|
||||
this.tweens.add({ targets: burst, angle: 240, scale: 1.34, alpha: 0.16, duration: 760, ease: 'Sine.easeOut' });
|
||||
@@ -7286,27 +7299,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 * 12 + 50, 72, 102);
|
||||
const chipWidth = Phaser.Math.Clamp(gain.label.length * 10 + 52, 72, 96);
|
||||
if (cursorX + chipWidth > x + maxWidth) {
|
||||
return;
|
||||
}
|
||||
const bg = this.add.rectangle(cursorX, y, chipWidth, 28, 0x0b1118, 0.9);
|
||||
const bg = this.add.rectangle(cursorX, y, chipWidth, 30, 0x0b1118, 0.92);
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(1, 0x53606c, 0.5);
|
||||
const icon = this.createBattleUiIcon(cursorX + 15, y + 14, gain.icon, 22);
|
||||
const text = this.add.text(cursorX + 31, y + 6, `+${gain.amount}`, {
|
||||
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}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '12px',
|
||||
fontSize: '13px',
|
||||
color: '#a8ffd0',
|
||||
fontStyle: '700'
|
||||
});
|
||||
const label = this.add.text(cursorX + 54, y + 6, gain.label, {
|
||||
const label = this.add.text(cursorX + 55, y + 8, gain.label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '11px',
|
||||
color: '#d4dce6'
|
||||
});
|
||||
container.add([bg, icon, text, label]);
|
||||
cursorX += chipWidth + 6;
|
||||
cursorX += chipWidth + 5;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9182,7 +9195,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
} else {
|
||||
this.showCombatMiss(defenderX, groundY - 70, depth + 14);
|
||||
}
|
||||
void this.playImmediateGrowthPopups(growthEntries, left + panelWidth / 2, top + 214, depth + 22);
|
||||
const outcomeCard = this.renderCombatOutcomeCard(result, left + panelWidth / 2 - 198, top + 214, 396, 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}`);
|
||||
@@ -9202,18 +9215,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
attackerStatus.nameText.setText(`${result.attacker.name} Lv ${result.characterGrowth.level}`);
|
||||
}
|
||||
|
||||
const resultLabel = result.hit ? `${result.critical ? '치명타! ' : ''}${result.damage} 피해` : '회피!';
|
||||
const resultText = this.trackCombatObject(this.add.text(left + panelWidth / 2, top + 252, resultLabel, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '22px',
|
||||
color: result.hit ? (result.critical ? '#ff8f5f' : '#ffdf7b') : '#d9f1ff',
|
||||
fontStyle: '700',
|
||||
stroke: '#2b0909',
|
||||
strokeThickness: 4
|
||||
}));
|
||||
resultText.setOrigin(0.5);
|
||||
resultText.setDepth(depth + 16);
|
||||
|
||||
await this.dismissCombatOutcomeCard(outcomeCard);
|
||||
await this.playGrowthRewardPanel({
|
||||
x: left + 86,
|
||||
y: top + 410,
|
||||
@@ -9228,6 +9230,105 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.hideCombatCutIn();
|
||||
}
|
||||
|
||||
private renderCombatOutcomeCard(result: CombatResult, x: number, y: number, width: number, depth: number) {
|
||||
const height = 66;
|
||||
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.97);
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(2, tone, 0.84);
|
||||
|
||||
const iconFrame = this.add.rectangle(34, height / 2, 52, 52, 0x101820, 0.94);
|
||||
iconFrame.setStrokeStyle(1, tone, 0.68);
|
||||
const iconImage = this.createBattleUiIcon(34, height / 2, icon, 48);
|
||||
|
||||
const titleText = this.add.text(70, 9, title, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '21px',
|
||||
color: result.hit ? '#fff2b8' : '#d9f1ff',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 148
|
||||
});
|
||||
|
||||
const detail = result.defeated ? `${result.defender.name} 퇴각` : `${result.defender.name} HP ${result.defender.hp}/${result.defender.maxHp}`;
|
||||
const detailText = this.add.text(72, 39, detail, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: result.defeated ? '#ffb6a6' : '#9fb0bf',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 190
|
||||
});
|
||||
|
||||
const value = result.hit ? `피해 ${result.damage}` : '피해 0';
|
||||
const valueText = this.add.text(width - 16, 15, value, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '24px',
|
||||
color: result.hit ? '#ffdf7b' : '#9fb0bf',
|
||||
fontStyle: '700'
|
||||
});
|
||||
valueText.setOrigin(1, 0);
|
||||
|
||||
container.add([bg, iconFrame, iconImage, titleText, detailText, valueText]);
|
||||
|
||||
if (result.defeated) {
|
||||
const badge = this.add.rectangle(width - 82, 42, 66, 22, 0x101820, 0.94);
|
||||
badge.setOrigin(0);
|
||||
badge.setStrokeStyle(1, 0xd8732c, 0.72);
|
||||
const badgeIcon = this.createBattleUiIcon(width - 69, 53, 'counter', 19);
|
||||
const badgeText = this.add.text(width - 54, 47, '퇴각', {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '10px',
|
||||
color: '#f2e3bf',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 31
|
||||
});
|
||||
container.add([badge, badgeIcon, badgeText]);
|
||||
}
|
||||
|
||||
this.tweens.add({ targets: container, alpha: 1, scale: 1, y: y - 4, duration: 160, ease: 'Sine.easeOut' });
|
||||
return container;
|
||||
}
|
||||
|
||||
private async dismissCombatOutcomeCard(card: Phaser.GameObjects.Container) {
|
||||
if (!card.active) {
|
||||
return;
|
||||
}
|
||||
this.tweens.add({
|
||||
targets: card,
|
||||
alpha: 0,
|
||||
y: card.y - 10,
|
||||
duration: 150,
|
||||
ease: 'Sine.easeIn',
|
||||
onComplete: () => card.destroy()
|
||||
});
|
||||
await this.delay(170);
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -9406,25 +9507,11 @@ 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);
|
||||
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}`);
|
||||
|
||||
const resultLine =
|
||||
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 + 132, resultLine, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '19px',
|
||||
color: result.usable.effect === 'heal' ? '#a8ffd0' : '#ffdf7b',
|
||||
fontStyle: '700',
|
||||
stroke: '#05070a',
|
||||
strokeThickness: 4
|
||||
}));
|
||||
resultText.setOrigin(0.5);
|
||||
resultText.setDepth(depth + 5);
|
||||
void this.playImmediateGrowthPopups(growthEntries, left + panelWidth / 2, top + 178, depth + 8);
|
||||
|
||||
await this.dismissCombatOutcomeCard(outcomeCard);
|
||||
await this.playGrowthRewardPanel({
|
||||
x: left + 64,
|
||||
y: top + 322,
|
||||
@@ -9439,6 +9526,57 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.hideCombatCutIn();
|
||||
}
|
||||
|
||||
private renderSupportOutcomeCard(result: SupportResult, x: number, y: number, width: number, depth: number) {
|
||||
const height = 66;
|
||||
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.97);
|
||||
bg.setOrigin(0);
|
||||
bg.setStrokeStyle(2, tone, 0.84);
|
||||
|
||||
const iconFrame = this.add.rectangle(34, height / 2, 52, 52, 0x101820, 0.94);
|
||||
iconFrame.setStrokeStyle(1, tone, 0.68);
|
||||
const iconImage = this.createBattleUiIcon(34, height / 2, this.usablePowerIcon(result.usable), 48);
|
||||
|
||||
const title = heal ? '병력 회복' : '강화 적용';
|
||||
const titleText = this.add.text(70, 9, title, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '21px',
|
||||
color: heal ? '#a8ffd0' : '#fff2b8',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 148
|
||||
});
|
||||
|
||||
const detail = heal
|
||||
? `${result.target.name} HP ${result.target.hp}/${result.target.maxHp}`
|
||||
: `공+${result.buff?.attackBonus ?? 0} 명+${result.buff?.hitBonus ?? 0} 치+${result.buff?.criticalBonus ?? 0}`;
|
||||
const detailText = this.add.text(72, 39, detail, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: '#d4dce6',
|
||||
fontStyle: '700',
|
||||
fixedWidth: 200
|
||||
});
|
||||
|
||||
const value = heal ? `+${result.healAmount}` : `${result.buff?.turns ?? result.usable.duration ?? 1}턴`;
|
||||
const valueText = this.add.text(width - 16, 15, value, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '24px',
|
||||
color: heal ? '#a8ffd0' : '#ffdf7b',
|
||||
fontStyle: '700'
|
||||
});
|
||||
valueText.setOrigin(1, 0);
|
||||
container.add([bg, iconFrame, iconImage, titleText, detailText, valueText]);
|
||||
|
||||
this.tweens.add({ targets: container, alpha: 1, scale: 1, y: y - 4, duration: 160, ease: 'Sine.easeOut' });
|
||||
return container;
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -9678,88 +9816,6 @@ export class BattleScene extends Phaser.Scene {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private playImmediateGrowthPopups(entries: GrowthGaugeEntry[], x: number, y: number, depth: number) {
|
||||
const visibleEntries = entries.filter((entry) => entry.amountLabel !== '+0' || entry.leveled).slice(0, 5);
|
||||
if (visibleEntries.length === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
soundDirector.playGrowthTick();
|
||||
visibleEntries.forEach((entry, index) => {
|
||||
const label = this.growthPopupLabel(entry);
|
||||
const statLine = entry.leveled ? this.statGainLine(entry.statGains) : undefined;
|
||||
const width = Math.min(500, Math.max(230, label.length * 13 + (statLine ? 72 : 34)));
|
||||
const rowY = y + index * 31;
|
||||
const container = this.trackCombatObject(this.add.container(x, rowY));
|
||||
container.setAlpha(0);
|
||||
container.setScale(0.92);
|
||||
container.setDepth(depth + index);
|
||||
|
||||
const bg = this.add.rectangle(0, 0, width, statLine ? 36 : 28, 0x101820, 0.94);
|
||||
bg.setStrokeStyle(2, entry.leveled ? palette.gold : entry.color, entry.leveled ? 0.96 : 0.74);
|
||||
const text = this.add.text(statLine ? -width / 2 + 18 : 0, statLine ? -12 : -9, label, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: entry.leveled ? '16px' : '15px',
|
||||
color: entry.leveled ? '#fff2b8' : '#f4dfad',
|
||||
fontStyle: '700',
|
||||
stroke: '#05070a',
|
||||
strokeThickness: 3
|
||||
});
|
||||
text.setOrigin(statLine ? 0 : 0.5, 0);
|
||||
container.add([bg, text]);
|
||||
|
||||
if (statLine) {
|
||||
const statText = this.add.text(-width / 2 + 18, 8, statLine, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '13px',
|
||||
color: '#a8ffd0',
|
||||
fontStyle: '700',
|
||||
stroke: '#05070a',
|
||||
strokeThickness: 3
|
||||
});
|
||||
container.add(statText);
|
||||
}
|
||||
|
||||
this.tweens.add({
|
||||
targets: container,
|
||||
alpha: 1,
|
||||
scale: entry.leveled ? 1.08 : 1,
|
||||
y: rowY - 6,
|
||||
delay: index * 82,
|
||||
duration: 150,
|
||||
ease: 'Back.easeOut'
|
||||
});
|
||||
this.tweens.add({
|
||||
targets: container,
|
||||
alpha: 0,
|
||||
y: rowY - 34,
|
||||
delay: 620 + index * 82,
|
||||
duration: 420,
|
||||
ease: 'Sine.easeIn',
|
||||
onComplete: () => container.destroy()
|
||||
});
|
||||
});
|
||||
|
||||
return this.delay(visibleEntries.length * 82 + 720);
|
||||
}
|
||||
|
||||
private growthPopupLabel(entry: GrowthGaugeEntry) {
|
||||
if (entry.kind === 'character') {
|
||||
return `${entry.owner} 경험치 ${entry.amountLabel}${entry.leveled ? ' LEVEL UP' : ''}`;
|
||||
}
|
||||
if (entry.kind === 'bond') {
|
||||
return `${entry.owner} 공명 ${entry.amountLabel}${entry.leveled ? ' LEVEL UP' : ''}`;
|
||||
}
|
||||
|
||||
const mastery =
|
||||
entry.slot === 'weapon'
|
||||
? '무기 숙련'
|
||||
: entry.slot === 'armor'
|
||||
? '방어구 숙련'
|
||||
: '보조구 숙련';
|
||||
return `${entry.owner} ${mastery} ${entry.amountLabel}${entry.leveled ? ' LEVEL UP' : ''}`;
|
||||
}
|
||||
|
||||
private statGainLine(gains?: LevelUpStatGain[]) {
|
||||
const visibleGains = gains?.filter((gain) => gain.amount > 0) ?? [];
|
||||
return visibleGains.length > 0 ? visibleGains.map((gain) => `${gain.label} +${gain.amount}`).join(' ') : undefined;
|
||||
@@ -9827,7 +9883,9 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
private async playCombatLevelUpSpotlight(entry: GrowthGaugeEntry, celebrant: UnitData | undefined, x: number, y: number, width: number, depth: number) {
|
||||
const panelWidth = Math.min(540, width - 34);
|
||||
const panelHeight = 116;
|
||||
const panelHeight = 132;
|
||||
const visualX = -panelWidth / 2 + 60;
|
||||
const contentX = celebrant ? -panelWidth / 2 + 122 : -panelWidth / 2 + 102;
|
||||
const container = this.trackCombatObject(this.add.container(x, y));
|
||||
container.setDepth(depth);
|
||||
container.setAlpha(0);
|
||||
@@ -9835,8 +9893,10 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
const bg = this.add.rectangle(0, 0, panelWidth, panelHeight, 0x211624, 0.97);
|
||||
bg.setStrokeStyle(3, palette.gold, 0.96);
|
||||
const glow = this.add.circle(-panelWidth / 2 + 58, 0, 42, 0xffdf7b, 0.12);
|
||||
const title = this.add.text(celebrant ? -panelWidth / 2 + 112 : 0, -36, `${entry.owner} LEVEL UP`, {
|
||||
const visualFrame = this.add.rectangle(visualX, 0, 66, 66, 0x0b1118, 0.92);
|
||||
visualFrame.setStrokeStyle(1, palette.gold, 0.72);
|
||||
const glow = this.add.circle(visualX, 0, 42, 0xffdf7b, 0.12);
|
||||
const title = this.add.text(celebrant ? contentX : 0, -48, `${entry.owner} LEVEL UP`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '25px',
|
||||
color: '#fff2b8',
|
||||
@@ -9845,14 +9905,14 @@ export class BattleScene extends Phaser.Scene {
|
||||
strokeThickness: 5
|
||||
});
|
||||
title.setOrigin(celebrant ? 0 : 0.5, 0);
|
||||
const levelText = this.add.text(celebrant ? -panelWidth / 2 + 112 : 0, -3, `Lv ${entry.previousLevel} → Lv ${entry.level}`, {
|
||||
const levelText = this.add.text(celebrant ? contentX : 0, -12, `Lv ${entry.previousLevel} → Lv ${entry.level}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '18px',
|
||||
color: '#f4dfad',
|
||||
fontStyle: '700'
|
||||
});
|
||||
levelText.setOrigin(celebrant ? 0 : 0.5, 0);
|
||||
const statText = this.add.text(celebrant ? -panelWidth / 2 + 112 : 0, 25, entry.statGains ? '능력 상승' : `${entry.label} 효과 강화`, {
|
||||
const statText = this.add.text(celebrant ? contentX : 0, 16, entry.statGains ? '능력 상승' : `${entry.label} 효과 강화`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '16px',
|
||||
color: '#a8ffd0',
|
||||
@@ -9861,18 +9921,21 @@ export class BattleScene extends Phaser.Scene {
|
||||
strokeThickness: 3
|
||||
});
|
||||
statText.setOrigin(celebrant ? 0 : 0.5, 0);
|
||||
container.add([bg, glow, title, levelText, statText]);
|
||||
container.add([bg, visualFrame, glow, title, levelText, statText]);
|
||||
if (entry.statGains) {
|
||||
this.addStatGainChipsToContainer(container, entry.statGains, celebrant ? -panelWidth / 2 + 112 : -panelWidth / 2 + 40, 51, panelWidth - 140);
|
||||
this.addStatGainChipsToContainer(container, entry.statGains, celebrant ? contentX : -panelWidth / 2 + 44, 50, panelWidth - 132);
|
||||
}
|
||||
|
||||
if (celebrant) {
|
||||
const sprite = this.add.sprite(-panelWidth / 2 + 56, 6, this.unitActionTexture(celebrant), this.unitActionFrameIndex('south', this.celebrationPose(celebrant)));
|
||||
const sprite = this.add.sprite(visualX, 8, this.unitActionTexture(celebrant), this.unitActionFrameIndex('south', this.celebrationPose(celebrant)));
|
||||
sprite.setDisplaySize(82, 82);
|
||||
const burst = this.add.star(-panelWidth / 2 + 56, -12, 8, 12, 34, 0xffdf7b, 0.72);
|
||||
const burst = this.add.star(visualX, -12, 8, 12, 34, 0xffdf7b, 0.72);
|
||||
container.add([burst, sprite]);
|
||||
this.tweens.add({ targets: burst, angle: 220, scale: 1.32, alpha: 0.18, duration: 760, ease: 'Sine.easeOut' });
|
||||
void this.playUnitActionFrames(sprite, celebrant, 'south', this.celebrationPose(celebrant), 105, 2);
|
||||
} else {
|
||||
const icon = this.createBattleUiIcon(visualX, 0, this.growthEntryIcon(entry), 54);
|
||||
container.add(icon);
|
||||
}
|
||||
|
||||
this.tweens.add({ targets: container, alpha: 1, scale: 1.04, duration: 180, ease: 'Back.easeOut' });
|
||||
|
||||
Reference in New Issue
Block a user