import 'dart:async'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../core/network/ws_client.dart'; import 'game_models.dart'; class GameUiState extends Equatable { final WsStatus connection; final GameState? state; final HandResult? handResult; // اوورلی نتیجه‌ی هَند (گذرا) final GameOver? gameOver; // اوورلی پایان بازی final String? notice; // پیام گذرا (خطا/خروج بازیکن) const GameUiState({ this.connection = WsStatus.connecting, this.state, this.handResult, this.gameOver, this.notice, }); GameUiState copyWith({ WsStatus? connection, GameState? state, HandResult? handResult, GameOver? gameOver, String? notice, bool clearHandResult = false, bool clearNotice = false, }) => GameUiState( connection: connection ?? this.connection, state: state ?? this.state, handResult: clearHandResult ? null : (handResult ?? this.handResult), gameOver: gameOver ?? this.gameOver, notice: clearNotice ? null : (notice ?? this.notice), ); @override List get props => [connection, state, handResult, gameOver, notice]; } class GameCubit extends Cubit { final WsClient _ws; final String tier; late final StreamSubscription _msgSub; late final StreamSubscription _statusSub; GameCubit(this._ws, this.tier) : super(const GameUiState()) { _msgSub = _ws.messages.listen(_onMessage); _statusSub = _ws.status.listen(_onStatus); _ws.connect(); } void _onStatus(WsStatus s) { emit(state.copyWith(connection: s)); // پس از برقراری اتصال، درخواست ورود به صف؛ در صورت reconnect سرور خودش // بازیکن را به میز برمی‌گرداند (این پیام را نادیده می‌گیرد). if (s == WsStatus.connected) { _ws.send({'type': 'join_queue', 'tier': tier}); } } void _onMessage(Map msg) { switch (msg['type']) { case 'state': final gs = GameState.fromJson(msg); // با شروع دست/هَند جدید، اوورلی نتیجه پاک می‌شود. final clear = gs.phase == 'choose_trump' || gs.phase == 'playing'; emit(state.copyWith(state: gs, clearHandResult: clear)); case 'hand_over': emit(state.copyWith(handResult: HandResult.fromJson(msg))); case 'game_over': emit(state.copyWith(gameOver: GameOver.fromJson(msg))); case 'player_disconnected': emit(state.copyWith(notice: 'یک بازیکن قطع شد')); case 'player_reconnected': emit(state.copyWith(notice: 'بازیکن بازگشت')); case 'player_left': emit(state.copyWith(notice: 'یک بازیکن میز را ترک کرد')); case 'error': emit(state.copyWith(notice: (msg['message'] ?? 'خطا').toString())); } } void chooseTrump(String suit) => _ws.send({'type': 'choose_trump', 'suit': suit}); void playCard(String card) => _ws.send({'type': 'play_card', 'card': card}); void leave() => _ws.send({'type': 'leave'}); void clearNotice() => emit(state.copyWith(clearNotice: true)); @override Future close() { _msgSub.cancel(); _statusSub.cancel(); _ws.dispose(); return super.close(); } }