fix: human fight timing, creative timer, TTS reliability, add sweary/vibe narrations

- Fix invisible characters in human mode: init live scene BEFORE starting
  challenge polling so entrance plays before first question appears
- Cap creative writing timer to 10s for multiple choice (just tapping buttons)
- Fix TTS reliability: precache priority phrases (Round 1-7, Fight!, K.O.)
  all at once instead of in slow batches; prevent duplicate precache runs
- Add 10 vibe-coded narrations (~20% chance): "I was vibe coded into existence"
- Add 15 sweary narrations (~30% chance): raw unhinged fight commentary
- Add sweary draw and retro narrations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-09 13:50:24 +00:00
co-authored by Claude Opus 4.6
parent 3ba05a66b4
commit a2416bbe19
5 changed files with 85 additions and 9 deletions
+24 -4
View File
@@ -325,12 +325,30 @@ export async function initKokoro(onProgress?: (pct: number) => void): Promise<vo
}
}
// Preload all static audio files into cache on startup (parallel fetch, no worker needed)
// Preload all static audio files into cache on startup (no worker needed)
let _precacheRunning = false
let _precacheDone = false
async function _precacheCommon() {
if (_precacheRunning || _precacheDone) return
_precacheRunning = true
const entries = Object.entries(STATIC_AUDIO)
// Load in batches of 6 to avoid hammering the network
for (let i = 0; i < entries.length; i += 6) {
const batch = entries.slice(i, i + 6)
// Priority: load Round 1-7, Fight!, K.O., Finish it, Flawless, Devastating, Critical FIRST (all at once)
const priorityIds = new Set(['round_1','round_2','round_3','round_4','round_5','round_6','round_7','fight','ko','finish_it','flawless_victory','devastating','critical_hit'])
const priority = entries.filter(([, url]) => {
const id = url.split('/').pop()?.replace('.wav', '') || ''
return priorityIds.has(id)
})
const rest = entries.filter(e => !priority.includes(e))
// Load priority batch all at once (13 small wav files)
await Promise.all(priority.map(async ([key, url]) => {
try {
const buf = await _loadStaticAudio(url)
if (buf) audioCache.set(key, { buf, lastAccess: Date.now() })
} catch { /* non-critical */ }
}))
// Load the rest in batches of 8
for (let i = 0; i < rest.length; i += 8) {
const batch = rest.slice(i, i + 8)
await Promise.all(batch.map(async ([key, url]) => {
try {
const buf = await _loadStaticAudio(url)
@@ -338,6 +356,8 @@ async function _precacheCommon() {
} catch { /* non-critical */ }
}))
}
_precacheDone = true
_precacheRunning = false
}
function _cacheKey(text: string, profile: string): string {