77 lines
3.0 KiB
Dart
77 lines
3.0 KiB
Dart
import 'dart:math' as math;
|
|
|
|
import '../../../../../core/service/app_sounds.dart';
|
|
import '../../../domain/entities/game_entities.dart';
|
|
|
|
/// ضربانِ قلبِ نوبتِ کاربر: در ثانیههای پایانیِ نوبتِ خودِ کاربر صدای ضربان پخش
|
|
/// میکند و با پایانِ نوبت بلافاصله قطع میشود. مستقل از چیدمانِ صحنه است.
|
|
///
|
|
/// زمانسنجِ نوبت دقیقاً همزمان با «حلقهی زمانِ نوبت» در HUD شروع میشود؛ آن حلقه
|
|
/// با تغییرِ امضای نوبت (نوبت|تعدادِ کارتِ زمین|فاز) ریست میشود و به انیمیشنِ بُر/
|
|
/// پخش کاری ندارد. اینجا هم شمارش به همان امضا بسته میشود و فقط «پخشِ صدا» در حینِ
|
|
/// انیمیشن/اینترو خاموش است تا صدا و تصویر یکی شوند.
|
|
class TurnHeartbeat {
|
|
double _elapsed = 0;
|
|
bool _beatPlayed = false;
|
|
double _accum = 0;
|
|
bool _wasMyTurn = false;
|
|
String _sig = '';
|
|
|
|
/// هر فریم صدا زده میشود. [busy] یعنی اینترو/بُر زدن/پخشِ کارت در جریان است
|
|
/// (شمارش ادامه دارد اما صدایی پخش نمیشود).
|
|
void update(
|
|
double dt,
|
|
GameState? s, {
|
|
required bool silent,
|
|
required bool busy,
|
|
}) {
|
|
final isMyTurn = s != null &&
|
|
((s.phase == 'playing' && s.turn == s.yourSeat) ||
|
|
(s.phase == 'choose_trump' && s.hakem == s.yourSeat));
|
|
|
|
if (!isMyTurn || silent) {
|
|
if (_wasMyTurn) {
|
|
AppSounds.stopHeartBeat(); // نوبت تمام شد ⇒ توقفِ فوریِ صدا
|
|
_wasMyTurn = false;
|
|
}
|
|
_sig = s == null ? '' : '${s.turn}|${s.trick.length}|${s.phase}';
|
|
return;
|
|
}
|
|
|
|
final sig = '${s.turn}|${s.trick.length}|${s.phase}';
|
|
if (!_wasMyTurn || sig != _sig) {
|
|
// شروعِ نوبتِ من (یا نوبتِ جدید) ⇒ شمارش از صفر، همزمان با حلقهی HUD.
|
|
_wasMyTurn = true;
|
|
_sig = sig;
|
|
_elapsed = 0;
|
|
_beatPlayed = false;
|
|
_accum = 0;
|
|
if (s.phase == 'playing' && !busy) AppSounds.turn();
|
|
}
|
|
_elapsed += dt;
|
|
|
|
if (busy) return;
|
|
|
|
final dur = (s.turnTimeoutMs > 0 ? s.turnTimeoutMs / 1000.0 : 30.0);
|
|
final remaining = dur - _elapsed;
|
|
final warn = math.min(7.0, dur * 0.4);
|
|
// در پنجرهی پایانیِ نوبت، ضربان با فاصلهی منظم تکرار میشود تا تا لحظهی آخر
|
|
// شنیده شود (نه فقط یک ضربانِ زودهنگام که در سکوتِ پایانی گم میشود).
|
|
if (remaining > 0 && remaining <= warn) {
|
|
if (!_beatPlayed) {
|
|
_beatPlayed = true;
|
|
_accum = 0;
|
|
AppSounds.heartBeat();
|
|
} else {
|
|
_accum += dt;
|
|
if (_accum >= 0.95) {
|
|
_accum = 0;
|
|
AppSounds.heartBeat();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void stop() => AppSounds.stopHeartBeat();
|
|
}
|