feat: victory celebration animations

Normal wins: winner flex pose with camera flash bursts + confetti rain.
Upset wins (100+ Elo underdog): dramatic zoom on winner with golden
flash. Upset tag shown on win overlay. Wired into FightViewer after
KO/Perfect sequence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-08 21:35:04 +00:00
co-authored by Claude Opus 4.6
parent cc0fb4553a
commit 88ceba43b0
2 changed files with 50 additions and 1 deletions
+42
View File
@@ -12118,6 +12118,48 @@ export async function createFightScene(config: FightSceneConfig) {
await k.wait(0.7)
},
async playVictoryCelebration(winningSide: 'a' | 'b', isUpset: boolean) {
const winner = k.get(winningSide === 'a' ? 'fighterA' : 'fighterB')[0] as Fighter
const loser = k.get(winningSide === 'a' ? 'fighterB' : 'fighterA')[0] as Fighter
if (!winner) return
if (isUpset) {
// UPSET: Dramatic zoom on winner + shocked gasp
const origSX = winner.scale.x
const origSY = winner.scale.y
sfxZoomWhoosh()
await Promise.all([
k.tween(Math.abs(origSX), 4, 0.25, (v: number) => { winner.scale.x = origSX > 0 ? v : -v; winner.scale.y = v }, k.easings.easeOutQuad),
k.tween(winner.pos.x, W / 2, 0.25, (v: number) => { winner.pos.x = v }, k.easings.easeOutQuad),
])
screenFlash('#ffe14d', 0.15)
k.shake(8)
await k.wait(0.6)
// Zoom back
await Promise.all([
k.tween(winner.scale.y, Math.abs(origSY), 0.2, (v: number) => { winner.scale.x = origSX > 0 ? v : -v; winner.scale.y = v }, k.easings.easeInQuad),
k.tween(winner.pos.x, winningSide === 'a' ? HOME_A : HOME_B, 0.2, (v: number) => { winner.pos.x = v }, k.easings.easeInQuad),
])
} else {
// NORMAL: Winner flex pose with camera flash
winner.play('win')
await k.wait(0.3)
screenFlash('#ffffff', 0.08)
sfxSpecial()
await k.wait(0.15)
screenFlash('#ffffff', 0.06)
await k.wait(0.15)
screenFlash('#ffffff', 0.04)
// Confetti shower
for (let i = 0; i < 20; i++) {
const cx = Math.random() * W
const confetti = k.add([k.rect(4, 4), k.pos(cx, -5), k.color(safeColor(k, ['#ff2d7b', '#00f0ff', '#ffe14d', '#39ff14', '#b83dff', '#ff6600'][i % 6])), k.opacity(0.9), k.z(20), k.rotate(Math.random() * 360)])
k.tween(confetti.pos.y, GROUND_Y + 10, 0.8 + Math.random() * 0.5, (v: number) => { confetti.pos.y = v; confetti.angle += 180 * k.dt() }).then(() => confetti.destroy())
}
}
await k.wait(0.4)
},
startMusic() { startMusic() },
stopMusic() { stopMusic() },