Improve battle result feedback

This commit is contained in:
2026-07-03 04:00:56 +09:00
parent 2d66b1dec3
commit a2ec898791

View File

@@ -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' });