feat: add reshuffle for ticket

This commit is contained in:
2026-06-24 21:00:18 +03:30
parent 500c61a409
commit e73333344f
4 changed files with 66 additions and 0 deletions
@@ -44,6 +44,7 @@ class GameBloc extends Bloc<GameEvent, GameUiState> {
(event, emit) => repository.send({'type': 'choose_trump', 'suit': event.suit})); (event, emit) => repository.send({'type': 'choose_trump', 'suit': event.suit}));
on<PlayCardEvent>( on<PlayCardEvent>(
(event, emit) => repository.send({'type': 'play_card', 'card': event.card})); (event, emit) => repository.send({'type': 'play_card', 'card': event.card}));
on<ReshuffleEvent>((event, emit) => repository.send({'type': 'reshuffle'}));
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({'type': 'start_table', 'hands': event.hands})); repository.send({'type': 'start_table', 'hands': event.hands}));
@@ -83,6 +84,7 @@ class GameBloc extends Bloc<GameEvent, GameUiState> {
// --- متدهای کمکی برای موتور Flame و صفحه‌ها --- // --- متدهای کمکی برای موتور Flame و صفحه‌ها ---
void chooseTrump(String suit) => add(ChooseTrumpEvent(suit)); void chooseTrump(String suit) => add(ChooseTrumpEvent(suit));
void reshuffle() => add(ReshuffleEvent());
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) => add(StartTableEvent(hands));
@@ -30,6 +30,8 @@ class PlayCardEvent extends GameEvent {
PlayCardEvent(this.card); PlayCardEvent(this.card);
} }
class ReshuffleEvent extends GameEvent {} // بُرِ مجدد با مصرفِ یک بلیت
class LeaveGameEvent extends GameEvent {} class LeaveGameEvent extends GameEvent {}
class StartTableEvent extends GameEvent { class StartTableEvent extends GameEvent {
@@ -11,6 +11,7 @@ import '../../../../core/service/app_sounds.dart';
import '../../../../core/theme/app_theme.dart'; import '../../../../core/theme/app_theme.dart';
import '../../../wallet/presentation/bloc/wallet_bloc.dart'; import '../../../wallet/presentation/bloc/wallet_bloc.dart';
import '../../../wallet/presentation/bloc/wallet_event.dart'; import '../../../wallet/presentation/bloc/wallet_event.dart';
import '../../../wallet/presentation/bloc/wallet_state.dart';
import '../../../wallet/presentation/bloc/wallet_status.dart'; import '../../../wallet/presentation/bloc/wallet_status.dart';
import '../../domain/entities/game_entities.dart'; import '../../domain/entities/game_entities.dart';
import '../bloc/game_bloc.dart'; import '../bloc/game_bloc.dart';
@@ -446,6 +447,53 @@ class _GameScreenState extends State<GameScreen> {
), ),
], ],
), ),
const SizedBox(height: 16),
const Divider(color: Color(0x33E9B949), height: 1),
const SizedBox(height: 12),
// بُرِ مجدد با مصرفِ یک بلیت (۵ کارتِ تازه، سپس باز هم انتخابِ حکم).
BlocBuilder<WalletBloc, WalletBlocState>(
builder: (context, ws) {
final tickets = ws.walletStatus is WalletLoaded
? (ws.walletStatus as WalletLoaded).wallet.tickets
: 0;
final canReshuffle = tickets > 0;
return Column(
children: [
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8E1B7A),
disabledBackgroundColor: const Color(0xFF3A2233),
minimumSize: const Size(220, 48),
),
onPressed: canReshuffle
? () {
AppSounds.button();
context.read<GameBloc>().reshuffle();
// پس از مصرفِ بلیت، موجودی را تازه کن (بدون نگه‌داشتنِ context).
final wallet = context.read<WalletBloc>();
Future.delayed(const Duration(milliseconds: 500),
() => wallet.add(LoadWalletEvent()));
}
: null,
icon: const Icon(Icons.casino, color: Colors.white),
label: const Text(
'بُر مجدد (۱ بلیت)',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 4),
Text(
canReshuffle
? 'بلیت‌های شما: $tickets'
: 'بلیت کافی ندارید',
style: const TextStyle(
color: Colors.white60, fontSize: 12),
),
],
);
},
),
], ],
), ),
), ),
@@ -47,6 +47,7 @@ class HokmGame extends FlameGame {
List<String> _prevTrick = const []; List<String> _prevTrick = const [];
int _bestTrumpRank = 0; // بالاترین رتبه‌ی حکمِ بازی‌شده در دستِ جاری (برای افکتِ بریدن) int _bestTrumpRank = 0; // بالاترین رتبه‌ی حکمِ بازی‌شده در دستِ جاری (برای افکتِ بریدن)
int _prevHandSize = 0; // برای تشخیصِ پخشِ کارت (دستِ جدید) جهت صدای بُر زدن int _prevHandSize = 0; // برای تشخیصِ پخشِ کارت (دستِ جدید) جهت صدای بُر زدن
String _prevHandSig = ''; // امضای دست؛ برای تشخیصِ بُرِ مجدد (تغییرِ ۵ کارت در فازِ حکم)
bool _silent = false; // هنگام خروج از میز، دیگر صدایی پخش نشود bool _silent = false; // هنگام خروج از میز، دیگر صدایی پخش نشود
bool _introActive = true; // تا پایانِ نمایشِ «جستجوی حریف» bool _introActive = true; // تا پایانِ نمایشِ «جستجوی حریف»
bool _pendingShuffle = bool _pendingShuffle =
@@ -89,6 +90,18 @@ class HokmGame extends FlameGame {
_shuffleNow(); _shuffleNow();
} }
} }
// بُرِ مجدد: هنوز در فازِ حکم هستیم اما ۵ کارتِ حاکم عوض شده ⇒ انیمیشنِ بُر دوباره.
final sig = (s.yourHand.toList()..sort()).join(',');
if (!_silent &&
!_introActive &&
s.phase == 'choose_trump' &&
s.trump == null &&
_prevHandSize > 0 &&
_prevHandSig.isNotEmpty &&
sig != _prevHandSig) {
_shuffleNow();
}
_prevHandSig = sig;
// لحظه‌ی انتخابِ حکم: صدا + نمایشِ بزرگِ حکم در مرکز. // لحظه‌ی انتخابِ حکم: صدا + نمایشِ بزرگِ حکم در مرکز.
if (_prevTrump == null && s.trump != null && !_silent) { if (_prevTrump == null && s.trump != null && !_silent) {
AppSounds.selectHokm(); AppSounds.selectHokm();
@@ -103,6 +116,7 @@ class HokmGame extends FlameGame {
if (cubit.state.state != null) { if (cubit.state.state != null) {
_prevTrick = cubit.state.state!.trick.map((t) => t.card).toList(); _prevTrick = cubit.state.state!.trick.map((t) => t.card).toList();
_prevHandSize = cubit.state.state!.yourHand.length; _prevHandSize = cubit.state.state!.yourHand.length;
_prevHandSig = (cubit.state.state!.yourHand.toList()..sort()).join(',');
_prevTrump = cubit.state.state!.trump; _prevTrump = cubit.state.state!.trump;
_s = cubit.state.state; _s = cubit.state.state;
_relayout(); _relayout();