import 'dart:async'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../../../../core/network/ws_client.dart'; import '../../../../core/theme/app_theme.dart'; import '../../../wallet/presentation/bloc/wallet_bloc.dart'; import '../../../wallet/presentation/bloc/wallet_event.dart'; import '../../domain/entities/game_entities.dart'; import '../bloc/game_bloc.dart'; import '../bloc/game_state.dart'; import '../widgets/flame/hokm_game.dart'; /// صفحه‌ی میز بازی: صحنه‌ی Flame + اوورلی‌های وضعیت. class GameScreen extends StatefulWidget { final int prize; const GameScreen({super.key, this.prize = 0}); @override State createState() => _GameScreenState(); } class _GameScreenState extends State { late final HokmGame _game; Timer? _introTimer; bool _introHidden = false; @override void initState() { super.initState(); _game = HokmGame(context.read()); } @override void dispose() { _introTimer?.cancel(); super.dispose(); } bool _showSearch(GameUiState s) => s.state == null || !_introHidden; @override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (didPop, _) { if (!didPop) _confirmLeave(context); }, child: Scaffold( body: BlocConsumer( listenWhen: (a, b) => a.notice != b.notice && b.notice != null, listener: (context, state) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.notice!), duration: const Duration(seconds: 2)), ); context.read().clearNotice(); }, builder: (context, state) { if (state.state != null && _introTimer == null) { _introTimer = Timer(const Duration(milliseconds: 1600), () { if (mounted) setState(() => _introHidden = true); }); } return Stack( children: [ GameWidget(game: _game), _backButton(context), if (state.connection == WsStatus.disconnected) _connBanner(), if (_showSearch(state)) _searchPanel(state), if (!_showSearch(state) && _showTrumpPicker(state)) _trumpPicker(context), if (state.handResult != null && state.gameOver == null) _handResult(state), if (state.gameOver != null) _gameOver(context, state), ], ); }, ), ), ); } bool _showTrumpPicker(GameUiState s) => s.state != null && s.state!.phase == 'choose_trump' && s.state!.amHakem && s.gameOver == null; Widget _backButton(BuildContext context) => Positioned( top: 8, right: 8, child: SafeArea( child: IconButton( icon: const Icon(Icons.arrow_back, color: AppColors.gold), onPressed: () => _confirmLeave(context), ), ), ); Future _confirmLeave(BuildContext context) async { if (context.read().state.gameOver != null) { _exitToLobby(context); return; } final yes = await showDialog( context: context, builder: (_) => AlertDialog( backgroundColor: AppColors.panel, title: const Text('خروج از میز'), content: const Text('از میز خارج می‌شوید؟ ورودی بازگردانده نمی‌شود.'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('ماندن')), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('خروج')), ], ), ); if (yes == true && context.mounted) { context.read().leave(); _exitToLobby(context); } } void _exitToLobby(BuildContext context) { context.read().add(LoadWalletEvent()); context.go('/lobby'); } Widget _connBanner() => Positioned( top: 0, left: 0, right: 0, child: Material( color: Colors.orange.shade900, child: const SafeArea( bottom: false, child: Padding( padding: EdgeInsets.all(8), child: Text('ارتباط با سرور قطع شد، در حال تلاش برای اتصال مجدد…', textAlign: TextAlign.center, style: TextStyle(color: Colors.white)), ), ), ), ); Widget _searchPanel(GameUiState state) { final players = state.state?.players ?? const []; final mySeat = state.state?.yourSeat ?? -1; final searching = state.state == null; GamePlayer? at(int seat) { for (final p in players) { if (p.seat == seat) return p; } return null; } return Container( color: Colors.black.withValues(alpha: 0.78), alignment: Alignment.center, child: Container( margin: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.fromLTRB(16, 14, 16, 18), decoration: BoxDecoration( color: AppColors.panel, borderRadius: BorderRadius.circular(20), border: Border.all(color: AppColors.gold, width: 2.5), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text('جستجوی حریف', style: TextStyle( color: AppColors.gold, fontSize: 22, fontWeight: FontWeight.bold)), const SizedBox(height: 18), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ for (var seat = 0; seat < 4; seat++) _searchSlot(at(seat), seat == mySeat), ], ), const SizedBox(height: 18), Container( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 8), decoration: BoxDecoration( color: AppColors.bgDark, borderRadius: BorderRadius.circular(24), border: Border.all(color: AppColors.goldDark), ), child: Row(mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.monetization_on, color: AppColors.gold), const SizedBox(width: 8), Text('${widget.prize}', style: const TextStyle( color: AppColors.gold, fontSize: 18, fontWeight: FontWeight.bold)), ]), ), if (searching) ...[ const SizedBox(height: 16), const SizedBox( height: 22, width: 22, child: CircularProgressIndicator( strokeWidth: 2, color: AppColors.gold), ), ], ], ), ), ); } Widget _searchSlot(GamePlayer? p, bool isYou) { final found = p != null; return Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Column(mainAxisSize: MainAxisSize.min, children: [ Text(found ? p.name : '...', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( color: isYou ? AppColors.gold : Colors.white70, fontSize: 12)), const SizedBox(height: 4), Container( width: 58, height: 58, decoration: BoxDecoration( color: AppColors.bgDark, borderRadius: BorderRadius.circular(10), border: Border.all( color: isYou ? AppColors.gold : AppColors.goldDark, width: 1.5), ), child: Icon( found ? (p.bot ? Icons.smart_toy : Icons.person) : Icons.help_outline, color: found ? AppColors.gold : Colors.white24, size: 30, ), ), const SizedBox(height: 2), Text(found ? (p.bot ? 'ربات' : (isYou ? 'شما' : 'حریف')) : '', style: const TextStyle(color: Colors.white38, fontSize: 10)), ]), ); } Widget _trumpPicker(BuildContext context) { const suits = [ ('spades', '♠', 'پیک', Colors.white), ('hearts', '♥', 'دل', Color(0xFFD32F2F)), ('diamonds', '♦', 'خشت', Color(0xFFD32F2F)), ('clubs', '♣', 'گشنیز', Colors.white), ]; return Container( color: Colors.black54, alignment: Alignment.center, child: Container( margin: const EdgeInsets.all(24), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppColors.panel, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.gold, width: 2), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text('حکم را انتخاب کن', style: TextStyle(color: AppColors.gold, fontSize: 20)), const SizedBox(height: 16), Wrap( spacing: 12, runSpacing: 12, alignment: WrapAlignment.center, children: [ for (final (id, sym, name, color) in suits) ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: AppColors.bgDark, minimumSize: const Size(120, 64), ), onPressed: () => context.read().chooseTrump(id), child: Row(mainAxisSize: MainAxisSize.min, children: [ Text(sym, style: TextStyle(fontSize: 26, color: color)), const SizedBox(width: 8), Text(name, style: const TextStyle(color: AppColors.text)), ]), ), ], ), ], ), ), ); } Widget _handResult(GameUiState s) { final r = s.handResult!; final mySeat = s.state?.yourSeat ?? 0; final myTeam = mySeat % 2; final won = r.winnerTeam == myTeam; return IgnorePointer( child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 18), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(16), border: Border.all(color: AppColors.gold), ), child: Column(mainAxisSize: MainAxisSize.min, children: [ Text(won ? 'این دست را بردید!' : 'این دست را باختید', style: TextStyle( color: won ? AppColors.green : Colors.redAccent, fontSize: 20, fontWeight: FontWeight.bold)), if (r.kot) Padding( padding: const EdgeInsets.only(top: 6), child: Text( r.hakemKot ? 'حاکم‌کُت! (${r.points} امتیاز)' : 'کُت! (${r.points} امتیاز)', style: const TextStyle(color: AppColors.gold)), ), const SizedBox(height: 6), Text( 'امتیاز: ${r.scores.isNotEmpty ? r.scores[0] : 0} - ${r.scores.length > 1 ? r.scores[1] : 0}', style: const TextStyle(color: Colors.white70)), ]), ), ), ); } Widget _gameOver(BuildContext context, GameUiState s) { final g = s.gameOver!; final mySeat = s.state?.yourSeat ?? 0; final won = g.winnerTeam == mySeat % 2; return Container( color: Colors.black87, alignment: Alignment.center, child: Column(mainAxisSize: MainAxisSize.min, children: [ Text(won ? 'بردید! 🎉' : 'باختید', style: TextStyle( color: won ? AppColors.gold : Colors.redAccent, fontSize: 32, fontWeight: FontWeight.bold)), const SizedBox(height: 12), Text( 'نتیجه نهایی: ${g.scores.isNotEmpty ? g.scores[0] : 0} - ${g.scores.length > 1 ? g.scores[1] : 0}', style: const TextStyle(color: Colors.white70, fontSize: 18)), const SizedBox(height: 28), SizedBox( width: 220, child: ElevatedButton( onPressed: () => _exitToLobby(context), child: const Text('بازگشت به لابی'), ), ), ]), ); } }