From a4cf2b5f39872c52026956f96047f65c5f61564d Mon Sep 17 00:00:00 2001 From: Amirmahdi Nourkazemi Date: Fri, 19 Jun 2026 00:45:30 +0330 Subject: [PATCH] fix: some buggs --- assets/audio/select_hokm.mp3 | Bin 0 -> 24621 bytes lib/core/service/app_sounds.dart | 12 ++- .../game/domain/entities/game_entities.dart | 3 + .../game/presentation/screen/game_screen.dart | 36 ++++++- .../presentation/widgets/flame/badges.dart | 91 ++++++++++++++++++ .../presentation/widgets/flame/hokm_game.dart | 78 +++++++++++++-- .../widgets/flame/turn_timer.dart | 56 +++++++++++ 7 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 assets/audio/select_hokm.mp3 create mode 100644 lib/feature/game/presentation/widgets/flame/badges.dart create mode 100644 lib/feature/game/presentation/widgets/flame/turn_timer.dart diff --git a/assets/audio/select_hokm.mp3 b/assets/audio/select_hokm.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..89e260eb38189d11a899355720ae44d2b2c3f725 GIT binary patch literal 24621 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 _pools = {}; static bool muted = false; @@ -47,5 +56,6 @@ class AppSounds { 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 button() => _play(_button, 0.5, cutoffMs: 130); // کلیکِ کوتاه } diff --git a/lib/feature/game/domain/entities/game_entities.dart b/lib/feature/game/domain/entities/game_entities.dart index a4c8bfe..1c90a21 100644 --- a/lib/feature/game/domain/entities/game_entities.dart +++ b/lib/feature/game/domain/entities/game_entities.dart @@ -37,6 +37,7 @@ class GameState { final List tricksWon; final List scores; final int targetScore; + final int turnTimeoutMs; // مهلتِ نوبتِ انسان (برای شمارنده‌ی نوبت) final List players; GameState({ @@ -54,6 +55,7 @@ class GameState { required this.tricksWon, required this.scores, required this.targetScore, + required this.turnTimeoutMs, required this.players, }); @@ -78,6 +80,7 @@ class GameState { tricksWon: ints(j['tricks_won']), scores: ints(j['scores']), targetScore: (j['target_score'] ?? 7) as int, + turnTimeoutMs: (j['turn_timeout_ms'] ?? 0) as int, players: ((j['players'] as List?) ?? []) .map((e) => GamePlayer.fromJson(Map.from(e as Map))) .toList(), diff --git a/lib/feature/game/presentation/screen/game_screen.dart b/lib/feature/game/presentation/screen/game_screen.dart index f59255b..1301b54 100644 --- a/lib/feature/game/presentation/screen/game_screen.dart +++ b/lib/feature/game/presentation/screen/game_screen.dart @@ -68,13 +68,18 @@ class _GameScreenState extends State { duration: const Duration(seconds: 2)), ); context.read().clearNotice(); + // خطا پیش از شروعِ بازی (مثلاً سکه‌ی ناکافی) ⇒ کاربر در دیالوگِ + // «جستجوی حریف» گیر نکند؛ به لابی برگردد. + if (state.state == null && state.gameOver == null) { + _exitToLobby(context); + } } }, builder: (context, state) { if (state.state != null && _introTimer == null) { // کمی «حریفان پیدا شد» نشان بده، بعد اوورلی را کنار بزن و همان // لحظه انیمیشنِ بُر زدن (با صدا) را اجرا کن. - _introTimer = Timer(const Duration(milliseconds: 700), () { + _introTimer = Timer(const Duration(milliseconds: 1000), () { if (!mounted) return; setState(() => _introHidden = true); _game.endIntro(); @@ -90,6 +95,7 @@ class _GameScreenState extends State { _trumpPicker(context), if (state.handResult != null && state.gameOver == null) _handResult(state), + if (!_showSearch(state) && _isMyPlayTurn(state)) _turnBanner(), if (state.gameOver != null) _gameOver(context, state), ], ); @@ -99,6 +105,34 @@ class _GameScreenState extends State { ); } + bool _isMyPlayTurn(GameUiState s) => + s.state != null && + s.gameOver == null && + s.state!.phase == 'playing' && + s.state!.isMyTurn && + !s.state!.trickDone; + + // بنرِ «نوبت شماست» وقتی کاربر باید کارت بیندازد. + Widget _turnBanner() => Align( + alignment: const Alignment(0, 0.46), + child: IgnorePointer( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 8), + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFFE9B949), Color(0xFFB8860B)]), + borderRadius: BorderRadius.circular(20), + boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 8)], + ), + child: const Text('نوبت شماست', + style: TextStyle( + color: Color(0xFF3A0A12), + fontWeight: FontWeight.bold, + fontSize: 16)), + ), + ), + ); + bool _showTrumpPicker(GameUiState s) => s.state != null && s.state!.phase == 'choose_trump' && diff --git a/lib/feature/game/presentation/widgets/flame/badges.dart b/lib/feature/game/presentation/widgets/flame/badges.dart new file mode 100644 index 0000000..068a8fc --- /dev/null +++ b/lib/feature/game/presentation/widgets/flame/badges.dart @@ -0,0 +1,91 @@ +import 'dart:math' as math; + +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flutter/material.dart'; + +const _gold = Color(0xFFE9B949); +const _goldDark = Color(0xFF8A5A00); + +/// ستاره‌ی طلایی روی بازیکنِ نوبت‌دار (مطابقِ تصویرِ مرجع). +class StarBadge extends PositionComponent { + final double r; + StarBadge(this.r, Vector2 pos) + : super(position: pos, size: Vector2.all(r * 2), anchor: Anchor.center); + + static final _fill = Paint()..color = _gold; + static final _stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1.5 + ..color = _goldDark; + + @override + void render(Canvas canvas) { + final path = _starPath(Offset(r, r), r, r * 0.45, 5); + canvas.drawPath(path, _fill); + canvas.drawPath(path, _stroke); + } +} + +/// تاجِ طلایی روی حاکم. +class CrownBadge extends PositionComponent { + final double w; + CrownBadge(this.w, Vector2 pos) + : super( + position: pos, + size: Vector2(w, w * 0.8), + anchor: Anchor.center, + ); + + static final _fill = Paint()..color = _gold; + static final _stroke = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1.5 + ..color = _goldDark; + + @override + void render(Canvas canvas) { + final h = size.y; + final p = Path() + ..moveTo(0, h) // پایین‌چپ + ..lineTo(0, h * 0.35) // نوکِ چپ + ..lineTo(w * 0.25, h * 0.62) // فرورفتگی + ..lineTo(w * 0.5, h * 0.18) // نوکِ میانی (بلندترین) + ..lineTo(w * 0.75, h * 0.62) + ..lineTo(w, h * 0.35) // نوکِ راست + ..lineTo(w, h) // پایین‌راست + ..close(); + canvas.drawPath(p, _fill); + canvas.drawPath(p, _stroke); + // نگین‌های نوکِ تاج + final gem = Paint()..color = const Color(0xFFB71C1C); + canvas.drawCircle(Offset(w * 0.5, h * 0.14), w * 0.05, gem); + } +} + +Path _starPath(Offset c, double outer, double inner, int points) { + final path = Path(); + for (var i = 0; i < points * 2; i++) { + final rad = i.isEven ? outer : inner; + final ang = -math.pi / 2 + i * math.pi / points; + final pt = Offset(c.dx + rad * math.cos(ang), c.dy + rad * math.sin(ang)); + i == 0 ? path.moveTo(pt.dx, pt.dy) : path.lineTo(pt.dx, pt.dy); + } + return path..close(); +} + +/// افکتِ ضربان (برای جلبِ توجه؛ مثلاً ستاره‌ی نوبت). +PulseEffect pulse() => PulseEffect(); + +class PulseEffect extends ScaleEffect { + PulseEffect() + : super.by( + Vector2.all(0.25), + EffectController( + duration: 0.5, + reverseDuration: 0.5, + infinite: true, + curve: Curves.easeInOut, + ), + ); +} diff --git a/lib/feature/game/presentation/widgets/flame/hokm_game.dart b/lib/feature/game/presentation/widgets/flame/hokm_game.dart index 360d818..6660c94 100644 --- a/lib/feature/game/presentation/widgets/flame/hokm_game.dart +++ b/lib/feature/game/presentation/widgets/flame/hokm_game.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import '../../../../../core/service/app_sounds.dart'; import '../../../domain/entities/game_entities.dart'; import '../../bloc/game_bloc.dart'; +import 'badges.dart'; import 'card_codes.dart'; import 'card_component.dart'; import 'shuffle_animation.dart'; import 'table_pieces.dart'; +import 'turn_timer.dart'; /// صحنه‌ی میز حکم با انیمیشن. شما همیشه پایین میز (rel=0) هستید. /// کامپوننت‌های دست و trick ماندگارند و با افکت حرکت جابه‌جا می‌شوند. @@ -46,6 +48,8 @@ class HokmGame extends FlameGame { bool _silent = false; // هنگام خروج از میز، دیگر صدایی پخش نشود bool _introActive = true; // تا پایانِ نمایشِ «جستجوی حریف» bool _pendingShuffle = false; // بُرِ دستِ اول که تا پایانِ اینترو به تعویق افتاده + bool _shuffling = false; // در حالِ پخشِ انیمیشنِ بُر زدن (بازیِ کارت ممنوع) + String? _prevTrump; // برای تشخیصِ لحظه‌ی انتخابِ حکم HokmGame(this.cubit); @@ -69,6 +73,12 @@ class HokmGame extends FlameGame { _shuffleNow(); } } + // لحظه‌ی انتخابِ حکم: صدا + نمایشِ بزرگِ حکم در مرکز. + if (_prevTrump == null && s.trump != null && !_silent) { + AppSounds.selectHokm(); + _spawnTrumpReveal(s.trump!); + } + _prevTrump = s.trump; _prevHandSize = s.yourHand.length; _s = s; _relayout(); @@ -76,6 +86,7 @@ class HokmGame extends FlameGame { if (cubit.state.state != null) { _prevTrick = cubit.state.state!.trick.map((t) => t.card).toList(); _prevHandSize = cubit.state.state!.yourHand.length; + _prevTrump = cubit.state.state!.trump; _s = cubit.state.state; _relayout(); } @@ -138,10 +149,41 @@ class HokmGame extends FlameGame { } /// اجرای هم‌زمانِ انیمیشن و صدای بُر زدن (صدا با پایانِ انیمیشن قطع می‌شود). + /// در طولِ انیمیشن، بازیِ کارت قفل است تا کارتی وسطِ بُر زدن انداخته نشود. void _shuffleNow() { if (_silent) return; AppSounds.shuffle(cutoffMs: ShuffleAnimation.totalMs); add(ShuffleAnimation(size)); + _shuffling = true; + Future.delayed(const Duration(milliseconds: ShuffleAnimation.totalMs), () { + _shuffling = false; + if (_s != null && !_silent) _relayout(); // فعال‌سازیِ دوباره‌ی کارت‌ها + }); + } + + /// نمایشِ بزرگ و گذرای حکمِ انتخاب‌شده در مرکزِ میز (با پاپ). + void _spawnTrumpReveal(String trump) { + final (sym, col) = _trumpGlyph(trump); + final reveal = TextComponent( + text: 'حکم $sym', + anchor: Anchor.center, + position: size / 2 - Vector2(0, size.y * 0.10), + priority: 7000, + scale: Vector2.all(0.4), + textRenderer: TextPaint( + style: TextStyle( + fontFamily: 'Dana', + color: col, + fontSize: size.x * 0.11, + fontWeight: FontWeight.bold, + shadows: const [Shadow(color: Colors.black, blurRadius: 6)], + ), + ), + ); + reveal.add(ScaleEffect.to( + Vector2.all(1.0), EffectController(duration: 0.35, curve: Curves.easeOutBack))); + reveal.add(RemoveEffect(delay: 1.3)); + add(reveal); } static bool _isPrefix(List a, List b) { @@ -191,6 +233,8 @@ class HokmGame extends FlameGame { // آیا این کارت در نوبتِ فعلی قابل بازی است (با رعایت follow-suit)؟ bool _legal(String code) { final s = _s!; + // هنگامِ نمایشِ اینترو یا انیمیشنِ بُر زدن، بازیِ کارت مجاز نیست. + if (_introActive || _shuffling) return false; if (s.phase != 'playing' || s.trickDone || !s.isMyTurn) return false; final lead = s.leadSuit; if (lead == null || lead.isEmpty) return true; @@ -441,15 +485,33 @@ class HokmGame extends FlameGame { color: isTurn ? const Color(0xFFE9B949) : Colors.white70, bold: isTurn, ); + // تاجِ حاکم (روی همه‌ی فازها نشان داده می‌شود تا حاکم مشخص باشد). + if (s.hakem == p.seat) { + final crown = CrownBadge(size.x * 0.06, pos - Vector2(0, size.y * 0.066)) + ..priority = 23; + _info.add(crown); + add(crown); + } if (isTurn) { - final dot = CircleComponent( - radius: size.x * 0.012, - anchor: Anchor.center, - position: pos - Vector2(0, size.y * 0.03), - paint: Paint()..color = const Color(0xFFE9B949), - )..priority = 20; - _info.add(dot); - add(dot); + final tpos = pos - Vector2(0, size.y * 0.03); + final human = !p.bot && p.connected; + // حلقه‌ی شمارشِ نوبت برای بازیکنِ انسان. + if (human && + s.turnTimeoutMs > 0 && + (s.phase == 'playing' || s.phase == 'choose_trump')) { + final timer = TurnTimer( + radius: size.x * 0.03, + durationSeconds: s.turnTimeoutMs / 1000.0, + position: tpos, + )..priority = 20; + _info.add(timer); + add(timer); + } + // ستاره‌ی نوبت (مطابقِ تصویرِ مرجع) با ضربان. + final star = StarBadge(size.x * 0.015, tpos)..priority = 24; + star.add(pulse()); + _info.add(star); + add(star); } } } diff --git a/lib/feature/game/presentation/widgets/flame/turn_timer.dart b/lib/feature/game/presentation/widgets/flame/turn_timer.dart new file mode 100644 index 0000000..fb45b59 --- /dev/null +++ b/lib/feature/game/presentation/widgets/flame/turn_timer.dart @@ -0,0 +1,56 @@ +import 'dart:math' as math; + +import 'package:flame/components.dart'; +import 'package:flutter/material.dart'; + +/// حلقه‌ی شمارشِ معکوسِ نوبت: کمانی که در طولِ مهلتِ نوبت خالی می‌شود. +/// رنگ از طلایی به قرمز می‌رود و در ثانیه‌های پایانی هشدار می‌دهد. +class TurnTimer extends PositionComponent { + final double radius; + final double durationSeconds; + double _elapsed = 0; + + TurnTimer({ + required this.radius, + required this.durationSeconds, + required Vector2 position, + }) : super( + position: position, + size: Vector2.all(radius * 2), + anchor: Anchor.center, + ); + + static final _bg = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 3 + ..color = Colors.black54; + + @override + void update(double dt) { + _elapsed += dt; + } + + @override + void render(Canvas canvas) { + final center = Offset(radius, radius); + canvas.drawCircle(center, radius, _bg); + + final frac = + durationSeconds <= 0 ? 0.0 : (1 - _elapsed / durationSeconds).clamp(0.0, 1.0); + if (frac <= 0) return; + + final color = frac > 0.3 ? const Color(0xFFE9B949) : const Color(0xFFE53935); + final arc = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 4 + ..strokeCap = StrokeCap.round + ..color = color; + canvas.drawArc( + Rect.fromCircle(center: center, radius: radius), + -math.pi / 2, // شروع از بالا + 2 * math.pi * frac, + false, + arc, + ); + } +}