import 'package:flame_audio/flame_audio.dart'; /// سرویسِ متمرکزِ افکت‌های صوتی (فایل‌ها در assets/audio/). /// /// از AudioPool استفاده می‌شود: برای هر صدا یک مجموعه‌ی محدود از پخش‌کننده‌ها /// ساخته و بازاستفاده می‌شود. این کار از نشتِ AudioPlayerها (که روی اندروید /// باعثِ قطعِ کاملِ صدا و پرشِ فریم می‌شد) جلوگیری می‌کند. class AppSounds { AppSounds._(); static const _shuffle = 'shuffle.mp3'; // بُر زدن/پخشِ کارت‌ها (شروع دست) static const _placeCard = 'place_card.mp3'; // گذاشتنِ کارت روی میز (همه‌ی بازیکنان) static const _takeCard = 'take_card.mp3'; // جمع‌شدنِ دستِ کامل static const _cut = 'cut.mp3'; // بریدن با حکم (شمشیر) static const _win = 'win.mp3'; // برد بازی static const _button = 'button.mp3'; // فشردنِ دکمه‌ها static const _selectHokm = 'select_hokm.mp3'; // انتخابِ حکم static const _heartBeat = 'heart_beat.mp3'; // ثانیه‌های پایانیِ نوبت static const _turn = 'turn.mp3'; // شروعِ نوبتِ خودِ کاربر static const _all = [ _shuffle, _placeCard, _takeCard, _cut, _win, _button, _selectHokm, _heartBeat, _turn, ]; static final Map _pools = {}; static bool muted = false; /// ساختِ Poolها (یک‌بار، در main). تا آماده‌شدن، پخش‌ها بی‌اثرند. static Future preload() async { for (final f in _all) { try { _pools[f] = await FlameAudio.createPool(f, maxPlayers: 3); } catch (_) {/* نبودِ فایل ⇒ بی‌صدا */} } } /// پخش از Pool؛ اگر cutoffMs داده شود، صدا پس از آن مدت قطع می‌شود (کلیکِ کوتاه). static void _play(String file, double volume, {int? cutoffMs}) { if (muted) return; final pool = _pools[file]; if (pool == null) return; pool.start(volume: volume).then((stop) { if (cutoffMs != null) { Future.delayed(Duration(milliseconds: cutoffMs), () => stop()); } }).catchError((_) {}); } /// صدای بُر زدن؛ cutoffMs را برابرِ مدتِ انیمیشنِ بُر بده تا هم‌زمان تمام شوند. static void shuffle({int? cutoffMs}) => _play(_shuffle, 0.7, cutoffMs: cutoffMs); static void placeCard() => _play(_placeCard, 0.8); static void takeCard() => _play(_takeCard, 0.7); static void cut() => _play(_cut, 0.9); static void win() => _play(_win, 0.9); static void selectHokm() => _play(_selectHokm, 0.9); static void turn() => _play(_turn, 0.9); // نوبتِ کاربر برای بازی // شمارشِ معکوسِ شروعِ بازی (۳…۲…۱) — کلیکِ کوتاه برای هر عدد. static void tick() => _play(_button, 0.7, cutoffMs: 200); // کنترلِ ضربانِ قلب: همیشه حداکثر یک ضربان هم‌زمان (بدون انباشت/تکرارِ سریع) // و قابلِ توقف وقتی نوبت تمام می‌شود. static Function? _hbStop; static void heartBeat() { final pool = _pools[_heartBeat]; if (muted || pool == null) return; _hbStop?.call(); // قطعِ ضربانِ قبلی پیش از پخشِ جدید _hbStop = null; pool.start(volume: 0.8).then((stop) { _hbStop = stop; }, onError: (_) {}); } static void stopHeartBeat() { try { _hbStop?.call(); } catch (_) {} _hbStop = null; } static void button() => _play(_button, 0.5, cutoffMs: 130); // کلیکِ کوتاه }