feat: 30 showboat/taunt animations during question & answer phases
Fighters now perform random showboating micro-animations while idle during question reading and opponent's answer — shadow boxing, backflips, flex poses, moonwalks, ground pounds, air guitar, sumo stomps, and more. Each showboat uses existing sprite anims (attack/kick/special/win) combined with Kaplay tweens for position, scale, rotation, sparks, and emote text. Showboating loops with random variety and pauses between moves. Flow: both showboat during question → stop the answering bot during their turn → stop all before fight animation begins. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
6561ef5d15
commit
2534cbc4cf
@@ -221,7 +221,7 @@ function addRoundToLog(round: Round, stagger: boolean): Promise<void> {
|
||||
const challenge = JSON.parse(round.challengeData)
|
||||
logItems.value.push(
|
||||
{ type: 'header', round: round.roundNumber, text: `ROUND ${round.roundNumber}: ${challengeLabel(round.challengeType)}`, color: 'neon-purple' },
|
||||
{ type: 'prompt', round: round.roundNumber, text: challenge.prompt, color: 'text-muted' },
|
||||
{ type: 'prompt', round: round.roundNumber, text: challenge.displayPrompt || challenge.prompt, color: 'text-muted' },
|
||||
{ type: 'responseA', round: round.roundNumber, text: `${props.fight.botA?.name}: ${round.botAResponse?.slice(0, 120) || '[NO RESPONSE]'} (${round.botATimeMs}ms)`, color: 'neon-cyan' },
|
||||
{ type: 'responseB', round: round.roundNumber, text: `${props.fight.botB?.name}: ${round.botBResponse?.slice(0, 120) || '[NO RESPONSE]'} (${round.botBTimeMs}ms)`, color: 'neon-pink' },
|
||||
)
|
||||
@@ -235,7 +235,7 @@ function addRoundToLog(round: Round, stagger: boolean): Promise<void> {
|
||||
const challenge = JSON.parse(round.challengeData)
|
||||
logItems.value.push({ type: 'header', round: round.roundNumber, text: `ROUND ${round.roundNumber}: ${challengeLabel(round.challengeType)}`, color: 'neon-purple' })
|
||||
scrollLog(); await sleep(150)
|
||||
logItems.value.push({ type: 'prompt', round: round.roundNumber, text: challenge.prompt, color: 'text-muted' })
|
||||
logItems.value.push({ type: 'prompt', round: round.roundNumber, text: challenge.displayPrompt || challenge.prompt, color: 'text-muted' })
|
||||
scrollLog(); await sleep(200)
|
||||
logItems.value.push({ type: 'responseA', round: round.roundNumber, text: `${props.fight.botA?.name}: ${round.botAResponse?.slice(0, 120) || '[NO RESPONSE]'}`, color: 'neon-cyan' })
|
||||
scrollLog(); await sleep(150)
|
||||
@@ -310,24 +310,32 @@ async function _doReplay() {
|
||||
const challenge = JSON.parse(round.challengeData)
|
||||
const doTTS = soundOn.value && !insanityMode.value
|
||||
|
||||
// Both fighters showboat while the question is being asked
|
||||
if (scene && !insanityMode.value) {
|
||||
scene.startShowboating('a')
|
||||
scene.startShowboating('b')
|
||||
}
|
||||
|
||||
// 1. Show question in log AND speak it
|
||||
logItems.value.push(
|
||||
{ type: 'header', round: round.roundNumber, text: `ROUND ${round.roundNumber}: ${challengeLabel(round.challengeType)}`, color: 'neon-purple' },
|
||||
)
|
||||
scrollLog(); await sleep(insanityMode.value ? 30 : 100)
|
||||
logItems.value.push(
|
||||
{ type: 'prompt', round: round.roundNumber, text: challenge.prompt, color: 'text-muted' },
|
||||
{ type: 'prompt', round: round.roundNumber, text: challenge.displayPrompt || challenge.prompt, color: 'text-muted' },
|
||||
)
|
||||
scrollLog()
|
||||
if (doTTS && challenge.prompt) {
|
||||
await speakQuestion(challenge.prompt)
|
||||
if (doTTS && (challenge.displayPrompt || challenge.prompt)) {
|
||||
await speakQuestion(challenge.displayPrompt || challenge.prompt)
|
||||
await sleep(150)
|
||||
} else {
|
||||
await sleep(insanityMode.value ? 50 : 600)
|
||||
}
|
||||
|
||||
// 2. Bot A: log + bubble + mouth + TTS (all synced)
|
||||
// Stop A's showboat when it's their turn to talk, B keeps showboating
|
||||
if (round.botAResponse) {
|
||||
scene?.stopShowboating('a')
|
||||
logItems.value.push({ type: 'responseA', round: round.roundNumber, text: `${props.fight.botA?.name}: ${round.botAResponse.slice(0, 120) || '[NO RESPONSE]'}`, color: 'neon-cyan' })
|
||||
logItems.value.push({ type: 'time', round: round.roundNumber, text: ` ${round.botATimeMs}ms | Score: ${round.botAScore}`, color: 'text-muted' })
|
||||
scrollLog()
|
||||
@@ -342,7 +350,9 @@ async function _doReplay() {
|
||||
}
|
||||
|
||||
// 3. Bot B: log + bubble + mouth + TTS
|
||||
// Stop B's showboat when it's their turn to talk
|
||||
if (round.botBResponse) {
|
||||
scene?.stopShowboating('b')
|
||||
logItems.value.push({ type: 'responseB', round: round.roundNumber, text: `${props.fight.botB?.name}: ${round.botBResponse.slice(0, 120) || '[NO RESPONSE]'}`, color: 'neon-pink' })
|
||||
logItems.value.push({ type: 'time', round: round.roundNumber, text: ` ${round.botBTimeMs}ms | Score: ${round.botBScore}`, color: 'text-muted' })
|
||||
scrollLog()
|
||||
@@ -356,6 +366,10 @@ async function _doReplay() {
|
||||
if (!insanityMode.value) await sleep(150)
|
||||
}
|
||||
|
||||
// Stop all showboating before fight animation
|
||||
scene?.stopShowboating('a')
|
||||
scene?.stopShowboating('b')
|
||||
|
||||
// Log is already populated above — no need for addRoundToLog stagger
|
||||
const logPromise = Promise.resolve()
|
||||
const isCritical = Math.abs((round.botAScore || 0) - (round.botBScore || 0)) > 4
|
||||
|
||||
Reference in New Issue
Block a user