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:
@@ -143,7 +143,7 @@ export const SCENARIOS = {
|
||||
{ id: 'nature', label: 'Naturwert mind. 55 %', check: s => s.nature >= 55 },
|
||||
],
|
||||
intro: {
|
||||
id: 'intro', icon: '👋', title: 'Willkommen im Tal!',
|
||||
id: 'intro-pionierzeit', icon: '👋', title: 'Willkommen im Tal!',
|
||||
text: 'Sommer 1975. Du übernimmst die Entwicklung eines kleinen, stillen '
|
||||
+ 'Alpentals.\n\nFang klein an: Baue zuerst ein Gasthaus im Tal, in der '
|
||||
+ 'Nähe des Dorfes. Dann kommen die ersten Wanderer – und alles Weitere '
|
||||
@@ -179,7 +179,7 @@ export const SCENARIOS = {
|
||||
{ id: 'nature', label: 'Naturwert mind. 60 %', check: s => s.nature >= 60 },
|
||||
],
|
||||
intro: {
|
||||
id: 'intro', icon: '📈', title: 'Der Boom ist da!',
|
||||
id: 'intro-boom', icon: '📈', title: 'Der Boom ist da!',
|
||||
text: 'Sommer 2000. Das Tal ist längst kein Geheimtipp mehr – die Gäste '
|
||||
+ 'strömen, und alle wollen ausbauen.\n\nAber die Natur trägt schon '
|
||||
+ 'Spuren, und die Einheimischen murren. Schaffst du Wachstum, OHNE '
|
||||
@@ -218,7 +218,7 @@ export const SCENARIOS = {
|
||||
{ id: 'nature', label: 'Naturwert mind. 65 %', check: s => s.nature >= 65 },
|
||||
],
|
||||
intro: {
|
||||
id: 'intro', icon: '🌡️', title: 'Die Schneegrenze steigt',
|
||||
id: 'intro-klimawandel', icon: '🌡️', title: 'Die Schneegrenze steigt',
|
||||
text: 'Sommer 2035. Die Winter werden wärmer, Schnee ist keine '
|
||||
+ 'Selbstverständlichkeit mehr. Messdaten zeigen: In den Alpen '
|
||||
+ 'schmilzt der Schnee 5–9 Tage pro Jahrzehnt früher – seit 1970 ist '
|
||||
|
||||
@@ -141,7 +141,7 @@ function onTileTap(c, r) {
|
||||
const first = !state.firedEvents.has('mega-info');
|
||||
state.firedEvents.add('mega-info');
|
||||
ui.showCard({
|
||||
id: `mega-${res.merged.c}-${res.merged.r}`, icon: '🏛️', sound: 'milestone',
|
||||
id: `mega-${res.merged.c}-${res.merged.r}`, audio: 'mega', icon: '🏛️', sound: 'milestone',
|
||||
title: `Megabauwerk: ${res.merged.name}!`,
|
||||
text: `Vier gleiche Gebäude im Quadrat sind zu EINEM großen ${res.merged.name} verschmolzen.`
|
||||
+ '\n\nDas ist ein Skaleneffekt: Das Megabauwerk leistet so viel wie '
|
||||
|
||||
@@ -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