WIP: Moving most globals into gamestate

The gamestate object is now where most of the "state" of both the map
and the board live. The units load, just working on loading the colors
of the map ownership again.
This commit is contained in:
Tyler Marques 2025-03-13 11:33:30 -04:00
parent dff01db83f
commit ae2db39729
No known key found for this signature in database
GPG key ID: 7672EFD79378341C
13 changed files with 1363 additions and 1322 deletions

View file

@ -0,0 +1,73 @@
// --- NEW: ElevenLabs TTS helper function ---
const ELEVENLABS_API_KEY = import.meta.env.VITE_ELEVENLABS_API_KEY || "";
const VOICE_ID = "onwK4e9ZLuTAKqWW03F9";
const MODEL_ID = "eleven_multilingual_v2";
/**
* Call ElevenLabs TTS to speak out loud. Returns a promise that
* resolves only after the audio finishes playing (or fails).
* Now accepts only the first 100 characters for brevity.
*/
async function speakSummary(summaryText) {
if (!ELEVENLABS_API_KEY) {
console.warn("No ElevenLabs API key found. Skipping TTS.");
return;
}
// Set the speaking flag to block other animations/transitions
isSpeaking = true;
try {
// Truncate text to first 100 characters for ElevenLabs
const truncatedText = summaryText.substring(0, 100);
if (truncatedText.length < summaryText.length) {
console.log(`TTS text truncated from ${summaryText.length} to 100 characters`);
}
// Hit ElevenLabs TTS endpoint with the truncated text
const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}`, {
method: "POST",
headers: {
"xi-api-key": ELEVENLABS_API_KEY,
"Content-Type": "application/json",
"Accept": "audio/mpeg"
},
body: JSON.stringify({
text: truncatedText,
model_id: MODEL_ID,
// Optional fine-tuning parameters
// voice_settings: { stability: 0.3, similarity_boost: 0.8 },
})
});
if (!response.ok) {
throw new Error(`ElevenLabs TTS error: ${response.statusText}`);
}
// Convert response into a playable blob
const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
// Play the audio, pause until finished
return new Promise((resolve, reject) => {
const audio = new Audio(audioUrl);
audio.play().then(() => {
audio.onended = () => {
// Clear the speaking flag when audio finishes
isSpeaking = false;
resolve();
};
}).catch(err => {
console.error("Audio playback error", err);
// Make sure to clear the flag even if there's an error
isSpeaking = false;
reject(err);
});
});
} catch (err) {
console.error("Failed to generate TTS from ElevenLabs:", err);
// Make sure to clear the flag if there's any exception
isSpeaking = false;
}
}