From c2356b3242168ba4733323465d84a3af8a39229e Mon Sep 17 00:00:00 2001 From: Amirmahdi Nourkazemi Date: Sat, 4 Jul 2026 12:45:52 +0330 Subject: [PATCH] feat: add coin private table --- .../game/presentation/bloc/game_bloc.dart | 10 +++- .../game/presentation/bloc/game_event.dart | 8 ++- .../game/presentation/bloc/game_state.dart | 5 +- .../screen/private_table_screen.dart | 59 ++++++++++++++++++- 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/lib/feature/game/presentation/bloc/game_bloc.dart b/lib/feature/game/presentation/bloc/game_bloc.dart index 7138203..289be49 100644 --- a/lib/feature/game/presentation/bloc/game_bloc.dart +++ b/lib/feature/game/presentation/bloc/game_bloc.dart @@ -59,8 +59,10 @@ class GameBloc extends Bloc { emit(state.copyWith(chatBubbles: next)); }); on((event, emit) => repository.send({'type': 'leave'})); - on((event, emit) => - repository.send({'type': 'start_table', 'hands': event.hands})); + on((event, emit) => repository.send( + {'type': 'start_table', 'hands': event.hands, 'stake': event.stake})); + on((event, emit) => + repository.send({'type': 'set_stake', 'stake': event.stake})); on((event, emit) => repository.send({'type': 'rotate_table', 'dir': event.dir})); on((event, emit) => repository.send({'type': 'leave_table'})); @@ -115,7 +117,9 @@ class GameBloc extends Bloc { void sendChatEmoji(String emoji) => add(SendChatEvent(emoji: emoji)); void playCard(String card) => add(PlayCardEvent(card)); void leave() => add(LeaveGameEvent()); - void startTable(int hands) => add(StartTableEvent(hands)); + void startTable(int hands, {int stake = 0}) => + add(StartTableEvent(hands, stake: stake)); + void setStake(int stake) => add(SetStakeEvent(stake)); void rotateTable(int dir) => add(RotateTableEvent(dir)); void leaveTable() => add(LeaveTableEvent()); void clearNotice() => add(ClearNoticeEvent()); diff --git a/lib/feature/game/presentation/bloc/game_event.dart b/lib/feature/game/presentation/bloc/game_event.dart index b4a9b38..3f89f25 100644 --- a/lib/feature/game/presentation/bloc/game_event.dart +++ b/lib/feature/game/presentation/bloc/game_event.dart @@ -50,7 +50,13 @@ class LeaveGameEvent extends GameEvent {} class StartTableEvent extends GameEvent { final int hands; // تعداد دستِ انتخابی (۳/۵/۷) - StartTableEvent(this.hands); + final int stake; // شرطِ سکه‌ایِ هر بازیکن + StartTableEvent(this.hands, {this.stake = 0}); +} + +class SetStakeEvent extends GameEvent { + final int stake; // شرطِ سکه‌ایِ میز (تنظیمِ زنده‌ی میزبان) + SetStakeEvent(this.stake); } class RotateTableEvent extends GameEvent { diff --git a/lib/feature/game/presentation/bloc/game_state.dart b/lib/feature/game/presentation/bloc/game_state.dart index 75890fd..76d7546 100644 --- a/lib/feature/game/presentation/bloc/game_state.dart +++ b/lib/feature/game/presentation/bloc/game_state.dart @@ -19,6 +19,7 @@ class TableLobby { final int youSeat; // جایگاهِ مطلقِ خودِ کاربر (برای چیدمانِ صحنه) final int remaining; final bool unlimited; + final int stake; // شرطِ سکه‌ایِ هر بازیکن (۰ یعنی رایگان) const TableLobby({ required this.code, required this.players, @@ -26,6 +27,7 @@ class TableLobby { required this.youSeat, required this.remaining, required this.unlimited, + required this.stake, }); factory TableLobby.fromJson(Map j) => TableLobby( @@ -41,9 +43,10 @@ class TableLobby { youSeat: (j['you_seat'] ?? 0) as int, remaining: (j['remaining'] ?? 0) as int, unlimited: (j['unlimited'] ?? false) as bool, + stake: (j['stake'] ?? 0) as int, ); - String get sig => '$code|$isHost|$youSeat|$remaining|$unlimited|' + String get sig => '$code|$isHost|$youSeat|$remaining|$unlimited|$stake|' '${players.map((p) => '${p.name}${p.host ? '*' : ''}@${p.seat}').join(',')}'; } diff --git a/lib/feature/game/presentation/screen/private_table_screen.dart b/lib/feature/game/presentation/screen/private_table_screen.dart index e8e7c79..64167cc 100644 --- a/lib/feature/game/presentation/screen/private_table_screen.dart +++ b/lib/feature/game/presentation/screen/private_table_screen.dart @@ -230,14 +230,30 @@ class _LobbyViewState extends State<_LobbyView> { for (final h in [3, 5, 7]) _handChip(h, lobby.isHost), ], ), + const SizedBox(height: 14), + const Text('ورودی (سکه) — برنده کل را می‌برد', + style: TextStyle(color: Colors.white70)), + const SizedBox(height: 10), + Wrap( + alignment: WrapAlignment.center, + spacing: 8, + runSpacing: 8, + children: [ + for (final v in const [0, 500, 1000, 5000, 20000]) + _stakeChip(v, lobby.isHost, lobby.stake), + ], + ), const SizedBox(height: 20), if (lobby.isHost) GameButton( - label: 'شروع بازی', + label: lobby.stake > 0 + ? 'شروع بازی (${lobby.stake} سکه)' + : 'شروع بازی', icon: Icons.play_arrow, width: double.infinity, colors: const [AppColors.success, AppColors.successDark], - onTap: () => context.read().startTable(_hands), + onTap: () => + context.read().startTable(_hands, stake: lobby.stake), ) else const Text( @@ -374,6 +390,45 @@ class _LobbyViewState extends State<_LobbyView> { ), ); } + + // چیپِ شرطِ سکه؛ میزبان با انتخاب، setStake را زنده برای همه پخش می‌کند. + Widget _stakeChip(int value, bool editable, int current) { + final selected = current == value; + final label = value == 0 ? 'رایگان' : '$value'; + return GestureDetector( + onTap: editable ? () => context.read().setStake(value) : null, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: selected + ? const LinearGradient(colors: [AppColors.gold, AppColors.goldDark]) + : null, + color: selected ? null : AppColors.panel, + border: Border.all( + color: selected ? AppColors.gold : AppColors.goldDark, + width: 1.6, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (value > 0) + Icon(Icons.monetization_on, + size: 16, color: selected ? AppColors.bg : AppColors.gold), + if (value > 0) const SizedBox(width: 4), + Text( + label, + style: TextStyle( + color: selected ? AppColors.bg : Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ); + } } /// اوورلی شمارش معکوس ۳، ۲، ۱ پیش از شروع بازی.