feat: remove mock fight button for production, cleanup fight UI

- Remove "Fight a Classic Bot" button (mock endpoints blocked in prod)
- Simplify speech bubble rendering (remove glow/accent layers)
- Sound and FightViewer improvements
- BotProfilePage enhancements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-07 11:59:10 +00:00
co-authored by Claude Opus 4.6
parent bb9a966d4e
commit 56785cfdea
5 changed files with 155 additions and 135 deletions
+30 -6
View File
@@ -265,8 +265,9 @@ if (typeof speechSynthesis !== 'undefined') {
loadVoices()
}
function speak(text: string, profileName: string, cancelPrevious: boolean = true, _echo: boolean = false) {
function speak(text: string, profileName: string, cancelPrevious: boolean = false, _echo: boolean = false) {
if (typeof speechSynthesis === 'undefined') return
if (masterMuted) return
if (!voicesLoaded) loadVoices()
if (cancelPrevious) speechSynthesis.cancel()
const profile = voiceProfiles[profileName] || voiceProfiles.announcer
@@ -285,11 +286,10 @@ export function stopAllAudio() {
// Public voice functions
export function announce(text: string, pitch?: number, rate?: number) {
if (masterMuted) return
if (pitch !== undefined || rate !== undefined) {
// Custom params — use announcer voice with overrides
if (typeof speechSynthesis === 'undefined') return
if (!voicesLoaded) loadVoices()
speechSynthesis.cancel()
const utter = new SpeechSynthesisUtterance(text)
const profile = voiceProfiles.announcer
if (profile.voice) utter.voice = profile.voice
@@ -310,16 +310,16 @@ export function announceSmooth(text: string) { speak(text, 'smooth') }
// Pick a random voice profile for variety
const ALL_VOICE_KEYS = Object.keys(voiceProfiles)
export function announceRandom(text: string, echo: boolean = false) {
export function announceRandom(text: string) {
const key = ALL_VOICE_KEYS[Math.floor(Math.random() * ALL_VOICE_KEYS.length)]
speak(text, key, true, echo)
speak(text, key)
}
// Announce with a specific mood category
const DRAMATIC_VOICES = ['deep', 'boomer', 'movie', 'preacher', 'opera', 'demon_v', 'echo_v', 'final_boss', 'game_over', 'mainframe', 'hal', 'ancient', 'giant']
const HYPE_VOICES = ['hype', 'screamer', 'sportscaster', 'auctioneer', 'hyper', 'punk', 'drill', 'wrestler_v', 'karen', 'terrified', 'power_up', 'news', 'scottish']
const SILLY_VOICES = ['chipmunk', 'baby', 'surfer', 'valley', 'pirate_v', 'alien_v', 'glitch', 'drunk', 'stoner', 'fairy', 'tutorial', 'npc', 'dial_up', 'glitchbot', 'grandma', 'conspiracy']
const COOL_VOICES = ['smooth', 'wizard_v', 'ninja_v', 'cowboy_v', 'angel', 'whisper', 'posh', 'aussie', 'french', 'sensei', 'ai_core', 'android_v', 'siri', 'professor', 'texan', 'boss_taunt', 'sleepy']
export function announceDramatic(text: string) { speak(text, DRAMATIC_VOICES[Math.floor(Math.random() * DRAMATIC_VOICES.length)], true, true) }
export function announceDramatic(text: string) { speak(text, DRAMATIC_VOICES[Math.floor(Math.random() * DRAMATIC_VOICES.length)]) }
export function announceHype(text: string) { speak(text, HYPE_VOICES[Math.floor(Math.random() * HYPE_VOICES.length)]) }
export function announceSilly(text: string) { speak(text, SILLY_VOICES[Math.floor(Math.random() * SILLY_VOICES.length)]) }
export function announceCool(text: string) { speak(text, COOL_VOICES[Math.floor(Math.random() * COOL_VOICES.length)]) }
@@ -1794,6 +1794,30 @@ export function setSfxVolume(v: number) {
if (sfxGain) sfxGain.gain.value = Math.max(0, Math.min(1, v))
}
let masterMuted = false
const MUSIC_VOL = 0.12
const SFX_VOL = 0.25
export function setMasterMute(muted: boolean) {
masterMuted = muted
if (muted) {
if (musicGain) musicGain.gain.value = 0
if (sfxGain) sfxGain.gain.value = 0
if (typeof speechSynthesis !== 'undefined') speechSynthesis.cancel()
} else {
if (musicGain) musicGain.gain.value = MUSIC_VOL
if (sfxGain) sfxGain.gain.value = SFX_VOL
}
}
export function isMasterMuted(): boolean {
return masterMuted
}
export function ensureAudioContext() {
getCtx()
}
// === CROWD SOUNDS ===
// Procedural crowd reactions using layered noise + filtered tones