Tourismustal: Vorproduzierte Sprach-Audios statt Browser-TTS
52 MP3s (OpenAI gpt-4o-mini-tts, Stimme coral, warme deutsche Erzaehlstimme) fuer alle Info-Karten: Erst-Bau-Erklaerungen, Meilensteine, Events, Szenen-Intros, Auftraege, Megabauwerk. Texte automatisch aus data.js gezogen (Generator im Scratchpad-Muster). Abspiel-Logik: assets/audio-info/<id>.mp3, Browser-TTS nur Not-Fallback. Intro-IDs je Szene eindeutig, Onboarding bleibt immer modal. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -146,7 +146,7 @@ export class UI {
|
||||
this.infoMode = this.infoMode === 'audio' ? 'read' : 'audio';
|
||||
localStorage.setItem('tt_infomode', this.infoMode);
|
||||
this._syncInfoModeBtn(e.currentTarget);
|
||||
this._speak(null);
|
||||
this._stopVoice();
|
||||
this.toast(this.infoMode === 'audio'
|
||||
? '🔊 Audio-Modus: Meldungen werden vorgelesen, das Spiel läuft weiter. Entscheidungen pausieren weiterhin.'
|
||||
: '📖 Lese-Modus: Meldungen öffnen sich als Karte, das Spiel pausiert.', 5000);
|
||||
@@ -486,12 +486,31 @@ export class UI {
|
||||
el.classList.remove('hidden');
|
||||
el.onclick = () => {
|
||||
el.classList.add('hidden');
|
||||
this._speak(null); // Vorlesen stoppen
|
||||
this._stopVoice();
|
||||
this.showCard({ ...card, transient: true, forceModal: true });
|
||||
};
|
||||
clearTimeout(this._bannerTimer);
|
||||
this._bannerTimer = setTimeout(() => el.classList.add('hidden'), 9000);
|
||||
this._speak(`${card.title}. ${card.text}`);
|
||||
this._speakCard(card);
|
||||
}
|
||||
|
||||
// Vorproduzierte Sprach-Audios (assets/audio-info/<id>.mp3, OpenAI-TTS).
|
||||
// Browser-TTS nur als Not-Fallback, falls eine Datei fehlt.
|
||||
_stopVoice() {
|
||||
if (this._voice) { this._voice.pause(); this._voice = null; }
|
||||
if ('speechSynthesis' in window) speechSynthesis.cancel();
|
||||
}
|
||||
|
||||
_speakCard(card) {
|
||||
this._stopVoice();
|
||||
const id = card.audio || card.id;
|
||||
if (!id) { this._speak(`${card.title}. ${card.text}`); return; }
|
||||
const a = new Audio(`assets/audio-info/${id}.mp3`);
|
||||
a.volume = 0.95;
|
||||
this._voice = a;
|
||||
const fallback = () => { if (this._voice === a) this._speak(`${card.title}. ${card.text}`); };
|
||||
a.onerror = fallback;
|
||||
a.play().catch(fallback);
|
||||
}
|
||||
|
||||
// Sprachausgabe (Browser-TTS, deutsche Stimme). null = stoppen.
|
||||
@@ -686,7 +705,7 @@ export class UI {
|
||||
// ---------- Onboarding ----------
|
||||
showOnboarding() {
|
||||
this.showCard({
|
||||
icon: '🏔️', transient: true,
|
||||
icon: '🏔️', transient: true, forceModal: true,
|
||||
title: 'Willkommen! (1/3) – So spielst du',
|
||||
text: 'Rechts ein Gebäude wählen, dann auf die Karte tippen – fertig. '
|
||||
+ 'Nach dem Bau legst du das Werkzeug automatisch ab.\n\n'
|
||||
@@ -696,7 +715,7 @@ export class UI {
|
||||
+ 'Schritt für Schritt frei.',
|
||||
});
|
||||
this.showCard({
|
||||
icon: '🚶', transient: true,
|
||||
icon: '🚶', transient: true, forceModal: true,
|
||||
title: 'Die Gäste (2/3) – dein Kreislauf',
|
||||
text: 'Jede Figur steht für etwa 10 Gäste. Sie kommen mit dem Bus, '
|
||||
+ 'wandern, kehren ein und reisen wieder ab.\n\n'
|
||||
@@ -707,7 +726,7 @@ export class UI {
|
||||
+ 'Wer konsumiert, bringt Geld – wer nicht kommen kann, keins.',
|
||||
});
|
||||
this.showCard({
|
||||
icon: '🔍', transient: true,
|
||||
icon: '🔍', transient: true, forceModal: true,
|
||||
title: 'Durchblick (3/3) – warum läuft es (nicht)?',
|
||||
text: 'Links stehen deine Kennzahlen. Kommen weniger Gäste als möglich, '
|
||||
+ 'erscheint dort der GRUND (z.B. „Engpass: Anreise").\n\n'
|
||||
|
||||
Reference in New Issue
Block a user