fix: human vs AI fight bugs — CSP for TTS, invisible sprites, fight end sequence
- Allow huggingface.co in CSP connect-src (fixes Kokoro TTS model download) - Add registerSW.js route (fixes PWA service worker 404) - Add _resetPositions() safety after entrance (fixes invisible fighters) - Fight end sequence works without canvas scene (KO/overlays/log always play) - Pre-fight instructions in battle log for human players - NIP-55 visibility sync and cleanup handlers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
63cc00fcb6
commit
150ce7447d
@@ -396,6 +396,8 @@ async function _doReplay() {
|
||||
// Entrance animations
|
||||
if (scene) {
|
||||
await scene.playEntrance()
|
||||
// Safety: ensure fighters are visible after entrance (prevents invisible characters if entrance times out)
|
||||
scene._resetPositions()
|
||||
await sleep(300)
|
||||
}
|
||||
|
||||
@@ -616,58 +618,72 @@ async function _doReplay() {
|
||||
if (typeof speechSynthesis !== 'undefined') speechSynthesis.cancel()
|
||||
|
||||
// Always end with a dramatic death/KO sequence
|
||||
if (scene) {
|
||||
if (props.fight.winnerId) {
|
||||
const winningSide = props.fight.winnerId === props.fight.botA!.id ? 'a' : 'b'
|
||||
const winnerHp = winningSide === 'a' ? props.fight.botAHp : props.fight.botBHp
|
||||
const isPerfect = winnerHp >= 200
|
||||
const winnerName = winningSide === 'a' ? props.fight.botA!.name : props.fight.botB!.name
|
||||
if (props.fight.winnerId) {
|
||||
const winningSide = props.fight.winnerId === props.fight.botA!.id ? 'a' : 'b'
|
||||
const winnerHp = winningSide === 'a' ? props.fight.botAHp : props.fight.botBHp
|
||||
const isPerfect = winnerHp >= 200
|
||||
const winnerName = winningSide === 'a' ? props.fight.botA!.name : props.fight.botB!.name
|
||||
|
||||
// "FINISH HIM!" moment before KO
|
||||
sfxDrumRoll()
|
||||
await sleep(500)
|
||||
announceFinishHim()
|
||||
await showOverlay('FINISH IT!', '#ff2d2d', 900)
|
||||
await sleep(150)
|
||||
// "FINISH HIM!" moment before KO
|
||||
sfxDrumRoll()
|
||||
await sleep(500)
|
||||
announceFinishHim()
|
||||
await showOverlay('FINISH IT!', '#ff2d2d', 900)
|
||||
await sleep(150)
|
||||
|
||||
if (isPerfect) {
|
||||
await scene.playPerfect(winningSide, winnerName)
|
||||
announceFlawlessVictory()
|
||||
await sleep(600) // let "flawless victory" voice land
|
||||
} else {
|
||||
await scene.playKO(winningSide, winnerName)
|
||||
await sleep(300) // let fatality voice finish
|
||||
if (scene) {
|
||||
try {
|
||||
if (isPerfect) {
|
||||
await scene.playPerfect(winningSide, winnerName)
|
||||
announceFlawlessVictory()
|
||||
await sleep(600) // let "flawless victory" voice land
|
||||
} else {
|
||||
await scene.playKO(winningSide, winnerName)
|
||||
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)
|
||||
} catch (err) {
|
||||
console.warn('[FightViewer] KO animation failed:', err)
|
||||
}
|
||||
|
||||
// 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
|
||||
sfxApplause()
|
||||
sfxCrowdCheer()
|
||||
await sleep(200)
|
||||
glitching.value = false
|
||||
const upsetTag = isUpset ? ' UPSET!' : ''
|
||||
await showOverlay(`${winnerName} WINS!${upsetTag}`, isUpset ? '#ff6600' : '#00f0ff', 1800)
|
||||
|
||||
logItems.value.push(
|
||||
{ type: 'divider', round: 99, text: '', color: '' },
|
||||
{ type: 'result', round: 99, text: `${winnerName.toUpperCase()} WINS!${isPerfect ? ' PERFECT!' : ''}`, color: winningSide === 'a' ? 'neon-cyan' : 'neon-pink' },
|
||||
)
|
||||
scrollLog()
|
||||
} else {
|
||||
// Draws get a dramatic double-KO
|
||||
await scene.playKO('a', 'NOBODY')
|
||||
await showOverlay('DOUBLE K.O.!', '#ff2d2d', 1500)
|
||||
logItems.value.push(
|
||||
{ type: 'divider', round: 99, text: '', color: '' },
|
||||
{ type: 'result', round: 99, text: 'DOUBLE K.O.! DRAW!', color: 'neon-purple' },
|
||||
)
|
||||
scrollLog()
|
||||
// Scene unavailable — still announce vocally
|
||||
if (isPerfect) announceFlawlessVictory()
|
||||
await sleep(800)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
glitching.value = true
|
||||
sfxApplause()
|
||||
sfxCrowdCheer()
|
||||
await sleep(200)
|
||||
glitching.value = false
|
||||
const upsetTag = isUpset ? ' UPSET!' : ''
|
||||
await showOverlay(`${winnerName} WINS!${upsetTag}`, isUpset ? '#ff6600' : '#00f0ff', 1800)
|
||||
|
||||
logItems.value.push(
|
||||
{ type: 'divider', round: 99, text: '', color: '' },
|
||||
{ type: 'result', round: 99, text: `${winnerName.toUpperCase()} WINS!${isPerfect ? ' PERFECT!' : ''}`, color: winningSide === 'a' ? 'neon-cyan' : 'neon-pink' },
|
||||
)
|
||||
scrollLog()
|
||||
} else {
|
||||
// Draws get a dramatic double-KO
|
||||
if (scene) {
|
||||
try { await scene.playKO('a', 'NOBODY') } catch { /* continue */ }
|
||||
}
|
||||
await showOverlay('DOUBLE K.O.!', '#ff2d2d', 1500)
|
||||
logItems.value.push(
|
||||
{ type: 'divider', round: 99, text: '', color: '' },
|
||||
{ type: 'result', round: 99, text: 'DOUBLE K.O.! DRAW!', color: 'neon-purple' },
|
||||
)
|
||||
scrollLog()
|
||||
}
|
||||
|
||||
// Kill any remaining queued speech — fight is over
|
||||
|
||||
Reference in New Issue
Block a user