feat: add coin private table

This commit is contained in:
2026-07-04 12:45:52 +03:30
parent 7acd72c286
commit c2356b3242
4 changed files with 75 additions and 7 deletions
@@ -59,8 +59,10 @@ class GameBloc extends Bloc<GameEvent, GameUiState> {
emit(state.copyWith(chatBubbles: next)); emit(state.copyWith(chatBubbles: next));
}); });
on<LeaveGameEvent>((event, emit) => repository.send({'type': 'leave'})); on<LeaveGameEvent>((event, emit) => repository.send({'type': 'leave'}));
on<StartTableEvent>((event, emit) => on<StartTableEvent>((event, emit) => repository.send(
repository.send({'type': 'start_table', 'hands': event.hands})); {'type': 'start_table', 'hands': event.hands, 'stake': event.stake}));
on<SetStakeEvent>((event, emit) =>
repository.send({'type': 'set_stake', 'stake': event.stake}));
on<RotateTableEvent>((event, emit) => on<RotateTableEvent>((event, emit) =>
repository.send({'type': 'rotate_table', 'dir': event.dir})); repository.send({'type': 'rotate_table', 'dir': event.dir}));
on<LeaveTableEvent>((event, emit) => repository.send({'type': 'leave_table'})); on<LeaveTableEvent>((event, emit) => repository.send({'type': 'leave_table'}));
@@ -115,7 +117,9 @@ class GameBloc extends Bloc<GameEvent, GameUiState> {
void sendChatEmoji(String emoji) => add(SendChatEvent(emoji: emoji)); void sendChatEmoji(String emoji) => add(SendChatEvent(emoji: emoji));
void playCard(String card) => add(PlayCardEvent(card)); void playCard(String card) => add(PlayCardEvent(card));
void leave() => add(LeaveGameEvent()); 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 rotateTable(int dir) => add(RotateTableEvent(dir));
void leaveTable() => add(LeaveTableEvent()); void leaveTable() => add(LeaveTableEvent());
void clearNotice() => add(ClearNoticeEvent()); void clearNotice() => add(ClearNoticeEvent());
@@ -50,7 +50,13 @@ class LeaveGameEvent extends GameEvent {}
class StartTableEvent extends GameEvent { class StartTableEvent extends GameEvent {
final int hands; // تعداد دستِ انتخابی (۳/۵/۷) 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 { class RotateTableEvent extends GameEvent {
@@ -19,6 +19,7 @@ class TableLobby {
final int youSeat; // جایگاهِ مطلقِ خودِ کاربر (برای چیدمانِ صحنه) final int youSeat; // جایگاهِ مطلقِ خودِ کاربر (برای چیدمانِ صحنه)
final int remaining; final int remaining;
final bool unlimited; final bool unlimited;
final int stake; // شرطِ سکه‌ایِ هر بازیکن (۰ یعنی رایگان)
const TableLobby({ const TableLobby({
required this.code, required this.code,
required this.players, required this.players,
@@ -26,6 +27,7 @@ class TableLobby {
required this.youSeat, required this.youSeat,
required this.remaining, required this.remaining,
required this.unlimited, required this.unlimited,
required this.stake,
}); });
factory TableLobby.fromJson(Map<String, dynamic> j) => TableLobby( factory TableLobby.fromJson(Map<String, dynamic> j) => TableLobby(
@@ -41,9 +43,10 @@ class TableLobby {
youSeat: (j['you_seat'] ?? 0) as int, youSeat: (j['you_seat'] ?? 0) as int,
remaining: (j['remaining'] ?? 0) as int, remaining: (j['remaining'] ?? 0) as int,
unlimited: (j['unlimited'] ?? false) as bool, 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(',')}'; '${players.map((p) => '${p.name}${p.host ? '*' : ''}@${p.seat}').join(',')}';
} }
@@ -230,14 +230,30 @@ class _LobbyViewState extends State<_LobbyView> {
for (final h in [3, 5, 7]) _handChip(h, lobby.isHost), 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), const SizedBox(height: 20),
if (lobby.isHost) if (lobby.isHost)
GameButton( GameButton(
label: 'شروع بازی', label: lobby.stake > 0
? 'شروع بازی (${lobby.stake} سکه)'
: 'شروع بازی',
icon: Icons.play_arrow, icon: Icons.play_arrow,
width: double.infinity, width: double.infinity,
colors: const [AppColors.success, AppColors.successDark], colors: const [AppColors.success, AppColors.successDark],
onTap: () => context.read<GameBloc>().startTable(_hands), onTap: () =>
context.read<GameBloc>().startTable(_hands, stake: lobby.stake),
) )
else else
const Text( 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<GameBloc>().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,
),
),
],
),
),
);
}
} }
/// اوورلی شمارش معکوس ۳، ۲، ۱ پیش از شروع بازی. /// اوورلی شمارش معکوس ۳، ۲، ۱ پیش از شروع بازی.