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:
co-authored by
Claude Opus 4.6
parent
cc0fb4553a
commit
88ceba43b0
@@ -587,12 +587,19 @@ async function _doReplay() {
|
|||||||
await sleep(300) // let fatality voice finish
|
await sleep(300) // let fatality voice finish
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Victory celebration — detect upsets (winner had lower Elo)
|
||||||
|
const winnerElo = winningSide === 'a' ? (props.fight.botA?.eloRating ?? 1200) : (props.fight.botB?.eloRating ?? 1200)
|
||||||
|
const loserElo = winningSide === 'a' ? (props.fight.botB?.eloRating ?? 1200) : (props.fight.botA?.eloRating ?? 1200)
|
||||||
|
const isUpset = winnerElo < loserElo - 100
|
||||||
|
await scene.playVictoryCelebration(winningSide, isUpset)
|
||||||
|
|
||||||
glitching.value = true
|
glitching.value = true
|
||||||
sfxApplause()
|
sfxApplause()
|
||||||
sfxCrowdCheer()
|
sfxCrowdCheer()
|
||||||
await sleep(200)
|
await sleep(200)
|
||||||
glitching.value = false
|
glitching.value = false
|
||||||
await showOverlay(`${winnerName} WINS!`, '#00f0ff', 1800)
|
const upsetTag = isUpset ? ' UPSET!' : ''
|
||||||
|
await showOverlay(`${winnerName} WINS!${upsetTag}`, isUpset ? '#ff6600' : '#00f0ff', 1800)
|
||||||
|
|
||||||
logItems.value.push(
|
logItems.value.push(
|
||||||
{ type: 'divider', round: 99, text: '', color: '' },
|
{ type: 'divider', round: 99, text: '', color: '' },
|
||||||
|
|||||||
@@ -12118,6 +12118,48 @@ export async function createFightScene(config: FightSceneConfig) {
|
|||||||
await k.wait(0.7)
|
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() },
|
startMusic() { startMusic() },
|
||||||
stopMusic() { stopMusic() },
|
stopMusic() { stopMusic() },
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user