import { useEffect, useRef } from 'react'; import * as Haptics from 'expo-haptics'; /** * Snake-Sound + Haptic-Helper. * * Aktuell: nur Haptics (Apple Taptic-Engine, Android-Vibrator-Falls-Available). * Funktioniert SOFORT ohne weitere Setup-Schritte. * * UPGRADE-PFAD zu echtem 8-Bit-Retro-Sound: * * 1. 4 kurze Audio-Files in `apps/rebreak-native/assets/sounds/` ablegen: * - `snake-eat.mp3` — Apple-Pickup, ~80ms, tonale "blip"-Töne (chiptune) * - `snake-move.mp3` — Optional, Tick-Sound bei jeder Bewegung, ~30ms, dezent * - `snake-gameover.mp3` — Death, ~400ms, abfallende Töne * - `snake-record.mp3` — New-Record, ~600ms, aufsteigender Chime * * Free-Quellen (CC0): freesound.org, opengameart.org/content/8-bit-sound-pack, * sfxr.me (in-browser-Generator für klassische 8-Bit-Sounds). * * 2. `expo-av` (oder `expo-audio` nach SDK-54-Migration) installieren falls nicht da: * `pnpm add expo-av` (im rebreak-native-Workspace) * * 3. In dieser Datei oben einfügen: * ```ts * import { Audio } from 'expo-av'; * const eatSrc = require('../assets/sounds/snake-eat.mp3'); * const moveSrc = require('../assets/sounds/snake-move.mp3'); * const gameoverSrc = require('../assets/sounds/snake-gameover.mp3'); * const recordSrc = require('../assets/sounds/snake-record.mp3'); * ``` * * 4. Im Hook-useEffect die Sounds preloaden: * ```ts * Audio.Sound.createAsync(eatSrc, { volume: 0.5 }).then((r) => (eatRef.current = r.sound)); * // … analog für alle drei * ``` * * 5. In den `play*`-Funktionen `await ref.current?.replayAsync()` aufrufen. * * Wenn die Files fehlen aber expo-av da ist: keine Crashes — die createAsync-Calls * fangen den Error und der Hook läuft im Haptic-only-Mode weiter. */ export function useSnakeSounds(enabled: boolean = true) { const enabledRef = useRef(enabled); useEffect(() => { enabledRef.current = enabled; }, [enabled]); useEffect(() => { return () => { // Cleanup: bei späterer Audio-Integration unloadAsync() für alle Sounds. }; }, []); return { playEat: () => { if (!enabledRef.current) return; Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {}); // TODO Audio: eatRef.current?.replayAsync().catch(() => {}); }, playMove: () => { // Bewusst leer — sonst zu viel Vibration bei jedem Tick. // Nur via Audio (subtiler als Haptic). // TODO Audio: moveRef.current?.replayAsync().catch(() => {}); }, playGameOver: () => { if (!enabledRef.current) return; Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error).catch(() => {}); // TODO Audio: gameoverRef.current?.replayAsync().catch(() => {}); }, playNewRecord: () => { if (!enabledRef.current) return; Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {}); // TODO Audio: recordRef.current?.replayAsync().catch(() => {}); }, }; }