This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

291 lines
8.7 KiB
C#

// ==============================================
// AudioManager.cs - Audio-Verwaltung
// ==============================================
// Verwaltet Musik, Sound-Effekte und
// Text-to-Speech fuer das Spiel.
using System.Collections.Generic;
using UnityEngine;
namespace BreakpilotDrive
{
[System.Serializable]
public class SoundEffect
{
public string name;
public AudioClip clip;
[Range(0f, 1f)]
public float volume = 1f;
[Range(0.5f, 1.5f)]
public float pitch = 1f;
public bool loop = false;
}
public class AudioManager : MonoBehaviour
{
public static AudioManager Instance { get; private set; }
[Header("Audio Sources")]
[SerializeField] private AudioSource musicSource;
[SerializeField] private AudioSource sfxSource;
[SerializeField] private AudioSource voiceSource;
[Header("Musik")]
[SerializeField] private AudioClip menuMusic;
[SerializeField] private AudioClip gameMusic;
[Header("Sound-Effekte")]
[SerializeField] private SoundEffect[] soundEffects;
[Header("Lautstaerke")]
[Range(0f, 1f)]
[SerializeField] private float masterVolume = 1f;
[Range(0f, 1f)]
[SerializeField] private float musicVolume = 0.5f;
[Range(0f, 1f)]
[SerializeField] private float sfxVolume = 1f;
[Range(0f, 1f)]
[SerializeField] private float voiceVolume = 1f;
// Sound-Dictionary fuer schnellen Zugriff
private Dictionary<string, SoundEffect> soundDict = new Dictionary<string, SoundEffect>();
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
InitializeSounds();
LoadVolumeSettings();
}
else
{
Destroy(gameObject);
}
}
private void InitializeSounds()
{
soundDict.Clear();
foreach (var sound in soundEffects)
{
if (!string.IsNullOrEmpty(sound.name) && sound.clip != null)
{
soundDict[sound.name.ToLower()] = sound;
}
}
}
// ==============================================
// Musik
// ==============================================
public void PlayMenuMusic()
{
PlayMusic(menuMusic);
}
public void PlayGameMusic()
{
PlayMusic(gameMusic);
}
public void PlayMusic(AudioClip clip)
{
if (musicSource == null || clip == null) return;
if (musicSource.clip == clip && musicSource.isPlaying)
return;
musicSource.clip = clip;
musicSource.volume = musicVolume * masterVolume;
musicSource.loop = true;
musicSource.Play();
}
public void StopMusic()
{
if (musicSource != null)
{
musicSource.Stop();
}
}
public void PauseMusic()
{
if (musicSource != null)
{
musicSource.Pause();
}
}
public void ResumeMusic()
{
if (musicSource != null)
{
musicSource.UnPause();
}
}
// ==============================================
// Sound-Effekte
// ==============================================
public void PlaySound(string soundName)
{
if (sfxSource == null) return;
string key = soundName.ToLower();
if (soundDict.TryGetValue(key, out SoundEffect sound))
{
sfxSource.pitch = sound.pitch;
sfxSource.PlayOneShot(sound.clip, sound.volume * sfxVolume * masterVolume);
}
else
{
Debug.LogWarning($"Sound '{soundName}' nicht gefunden!");
}
}
public void PlaySound(AudioClip clip, float volume = 1f)
{
if (sfxSource == null || clip == null) return;
sfxSource.PlayOneShot(clip, volume * sfxVolume * masterVolume);
}
// Vordefinierte Sound-Methoden
public void PlayCoinSound() => PlaySound("coin");
public void PlayCrashSound() => PlaySound("crash");
public void PlayCorrectSound() => PlaySound("correct");
public void PlayWrongSound() => PlaySound("wrong");
public void PlayButtonSound() => PlaySound("button");
// ==============================================
// Text-to-Speech (WebGL)
// ==============================================
public void Speak(string text)
{
if (string.IsNullOrEmpty(text)) return;
#if UNITY_WEBGL && !UNITY_EDITOR
SpeakWebGL(text);
#else
Debug.Log($"[TTS] {text}");
#endif
}
public void SpeakQuestion(QuizQuestion question)
{
if (question == null) return;
Speak(question.question_text);
// Optionen vorlesen (mit Verzoegerung)
for (int i = 0; i < question.options.Length; i++)
{
string optionText = $"Option {i + 1}: {question.options[i]}";
// In WebGL: Verzoegerung ueber JavaScript
#if UNITY_WEBGL && !UNITY_EDITOR
SpeakDelayedWebGL(optionText, 1.5f + (i * 1.5f));
#else
Debug.Log($"[TTS] {optionText}");
#endif
}
}
public void SpeakFeedback(bool correct)
{
string message = correct ?
"Richtig! Gut gemacht!" :
"Nicht ganz. Versuch es nochmal!";
Speak(message);
}
public void StopSpeaking()
{
#if UNITY_WEBGL && !UNITY_EDITOR
StopSpeakingWebGL();
#endif
}
// WebGL JavaScript Interop
#if UNITY_WEBGL && !UNITY_EDITOR
[System.Runtime.InteropServices.DllImport("__Internal")]
private static extern void SpeakWebGL(string text);
[System.Runtime.InteropServices.DllImport("__Internal")]
private static extern void SpeakDelayedWebGL(string text, float delaySeconds);
[System.Runtime.InteropServices.DllImport("__Internal")]
private static extern void StopSpeakingWebGL();
#endif
// ==============================================
// Lautstaerke-Einstellungen
// ==============================================
public void SetMasterVolume(float volume)
{
masterVolume = Mathf.Clamp01(volume);
UpdateAllVolumes();
SaveVolumeSettings();
}
public void SetMusicVolume(float volume)
{
musicVolume = Mathf.Clamp01(volume);
if (musicSource != null)
{
musicSource.volume = musicVolume * masterVolume;
}
SaveVolumeSettings();
}
public void SetSFXVolume(float volume)
{
sfxVolume = Mathf.Clamp01(volume);
SaveVolumeSettings();
}
public void SetVoiceVolume(float volume)
{
voiceVolume = Mathf.Clamp01(volume);
if (voiceSource != null)
{
voiceSource.volume = voiceVolume * masterVolume;
}
SaveVolumeSettings();
}
private void UpdateAllVolumes()
{
if (musicSource != null)
musicSource.volume = musicVolume * masterVolume;
if (voiceSource != null)
voiceSource.volume = voiceVolume * masterVolume;
}
private void SaveVolumeSettings()
{
PlayerPrefs.SetFloat("MasterVolume", masterVolume);
PlayerPrefs.SetFloat("MusicVolume", musicVolume);
PlayerPrefs.SetFloat("SFXVolume", sfxVolume);
PlayerPrefs.SetFloat("VoiceVolume", voiceVolume);
PlayerPrefs.Save();
}
private void LoadVolumeSettings()
{
masterVolume = PlayerPrefs.GetFloat("MasterVolume", 1f);
musicVolume = PlayerPrefs.GetFloat("MusicVolume", 0.5f);
sfxVolume = PlayerPrefs.GetFloat("SFXVolume", 1f);
voiceVolume = PlayerPrefs.GetFloat("VoiceVolume", 1f);
UpdateAllVolumes();
}
// Properties fuer UI-Sliders
public float MasterVolume => masterVolume;
public float MusicVolume => musicVolume;
public float SFXVolume => sfxVolume;
public float VoiceVolume => voiceVolume;
}
}