feat: add carpet
This commit is contained in:
@@ -31,6 +31,7 @@ class GameScreen extends StatefulWidget {
|
||||
|
||||
class _GameScreenState extends State<GameScreen> {
|
||||
late final HokmGame _game;
|
||||
String _carpet = 'classic'; // فرشِ انتخابی (برای پسزمینهی پشتِ صحنه)
|
||||
Timer? _introTimer;
|
||||
bool _introHidden = false;
|
||||
int _countdown = 0; // شمارشِ معکوسِ شروعِ بازی پس از یافتنِ حریفان (۳…۲…۱)
|
||||
@@ -40,9 +41,11 @@ class _GameScreenState extends State<GameScreen> {
|
||||
super.initState();
|
||||
final ws = context.read<WalletBloc>().state.walletStatus;
|
||||
final skin = ws is WalletLoaded ? ws.wallet.selectedCard : 'simple';
|
||||
_carpet = ws is WalletLoaded ? ws.wallet.selectedCarpet : 'classic';
|
||||
_game = HokmGame(
|
||||
context.read<GameBloc>(),
|
||||
skin: skin.isEmpty ? 'simple' : skin,
|
||||
carpet: _carpet.isEmpty ? 'classic' : _carpet,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -114,6 +117,7 @@ class _GameScreenState extends State<GameScreen> {
|
||||
}
|
||||
return Stack(
|
||||
children: [
|
||||
// فرش بهعنوانِ سطحِ میز داخلِ صحنهی Flame (Felt) کشیده میشود.
|
||||
GameWidget(game: _game),
|
||||
if (!_showSearch(state) && state.state != null)
|
||||
TableHud(
|
||||
|
||||
@@ -65,25 +65,11 @@ class _PrivateEntryScreenState extends State<PrivateEntryScreen> {
|
||||
final canCreate = unlimited || remaining > 0;
|
||||
return Column(
|
||||
children: [
|
||||
Align(
|
||||
const Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: GestureDetector(
|
||||
onTap: () => context.pop(),
|
||||
child: Container(
|
||||
width: 46,
|
||||
height: 46,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.panel,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border:
|
||||
Border.all(color: AppColors.gold, width: 1.5),
|
||||
),
|
||||
child: const Icon(Icons.arrow_back,
|
||||
color: AppColors.gold),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.all(10),
|
||||
child: AppBackButton(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@@ -20,18 +20,22 @@ class PrivateTableScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocConsumer<GameBloc, GameUiState>(
|
||||
listenWhen: (a, b) =>
|
||||
(a.notice != b.notice && b.notice != null) ||
|
||||
(!a.tableClosed && b.tableClosed),
|
||||
listenWhen:
|
||||
(a, b) =>
|
||||
(a.notice != b.notice && b.notice != null) ||
|
||||
(!a.tableClosed && b.tableClosed),
|
||||
listener: (context, state) {
|
||||
if (state.tableClosed) {
|
||||
if (context.canPop()) context.pop();
|
||||
return;
|
||||
}
|
||||
if (state.notice != null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.notice!),
|
||||
duration: const Duration(seconds: 2)));
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
context.read<GameBloc>().clearNotice();
|
||||
}
|
||||
},
|
||||
@@ -81,11 +85,14 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
children: [
|
||||
_topBar(context, lobby),
|
||||
Expanded(
|
||||
child: connecting
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.gold))
|
||||
: _content(context, lobby),
|
||||
child:
|
||||
connecting
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.gold,
|
||||
),
|
||||
)
|
||||
: _content(context, lobby),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -102,18 +109,18 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
// نوار بالا: بازگشت (چپ) و اشتراکگذاریِ کد (راست).
|
||||
Widget _topBar(BuildContext context, TableLobby? lobby) {
|
||||
Widget btn(IconData icon, VoidCallback onTap) => GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 46,
|
||||
height: 46,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.panel,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.gold, width: 1.5),
|
||||
),
|
||||
child: Icon(icon, color: AppColors.gold),
|
||||
),
|
||||
);
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 46,
|
||||
height: 46,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.panel,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.gold, width: 1.5),
|
||||
),
|
||||
child: Icon(icon, color: AppColors.gold),
|
||||
),
|
||||
);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Directionality(
|
||||
@@ -121,13 +128,16 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
btn(Icons.arrow_back, _leave),
|
||||
if (lobby != null)
|
||||
btn(Icons.share, () {
|
||||
Clipboard.setData(ClipboardData(text: lobby.code));
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||
content: Text('کد میز کپی شد؛ برای دوستانت بفرست')));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('کد میز کپی شد؛ برای دوستانت بفرست'),
|
||||
),
|
||||
);
|
||||
}),
|
||||
btn(Icons.arrow_forward, _leave),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -160,7 +170,9 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 28, vertical: 36),
|
||||
horizontal: 28,
|
||||
vertical: 36,
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: const RadialGradient(
|
||||
@@ -168,29 +180,42 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
radius: 0.9,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(160),
|
||||
border: Border.all(color: const Color(0xFF8A5A12), width: 6),
|
||||
),
|
||||
child: const Center(
|
||||
child: GlowText('سلطان حکم', size: 18),
|
||||
border: Border.all(
|
||||
color: const Color(0xFF8A5A12),
|
||||
width: 6,
|
||||
),
|
||||
),
|
||||
child: const Center(child: GlowText('سلطان حکم', size: 18)),
|
||||
),
|
||||
),
|
||||
Align(alignment: Alignment.topCenter, child: _seat(top, false)),
|
||||
Align(alignment: Alignment.centerLeft, child: _seat(left, false)),
|
||||
Align(
|
||||
alignment: Alignment.centerRight, child: _seat(right, false)),
|
||||
Align(alignment: Alignment.bottomCenter, child: _seat(me, true)),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: _seat(left, false),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: _seat(right, false),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _seat(me, true),
|
||||
),
|
||||
// چرخشِ جایگاهها (فقط میزبان) — تغییرِ تیمبندی.
|
||||
if (lobby.isHost) ...[
|
||||
Align(
|
||||
alignment: const Alignment(-1, -0.35),
|
||||
child: _rotateBtn(Icons.rotate_left,
|
||||
() => context.read<GameBloc>().rotateTable(-1)),
|
||||
child: _rotateBtn(
|
||||
Icons.rotate_left,
|
||||
() => context.read<GameBloc>().rotateTable(-1),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: const Alignment(1, -0.35),
|
||||
child: _rotateBtn(Icons.rotate_right,
|
||||
() => context.read<GameBloc>().rotateTable(1)),
|
||||
child: _rotateBtn(
|
||||
Icons.rotate_right,
|
||||
() => context.read<GameBloc>().rotateTable(1),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
@@ -215,12 +240,16 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
onTap: () => context.read<GameBloc>().startTable(_hands),
|
||||
)
|
||||
else
|
||||
const Text('در انتظار شروع توسط میزبان…',
|
||||
style: TextStyle(color: AppColors.gold, fontSize: 15)),
|
||||
const Text(
|
||||
'در انتظار شروع توسط میزبان…',
|
||||
style: TextStyle(color: AppColors.gold, fontSize: 15),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
if (lobby.isHost)
|
||||
const Text('جایهای خالی با ربات پر میشوند',
|
||||
style: TextStyle(color: Colors.white54, fontSize: 12)),
|
||||
const Text(
|
||||
'جایهای خالی با ربات پر میشوند',
|
||||
style: TextStyle(color: Colors.white54, fontSize: 12),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
@@ -244,15 +273,20 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
color: AppColors.bgDark,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isYou ? AppColors.gold : AppColors.goldDark,
|
||||
width: 2),
|
||||
color: isYou ? AppColors.gold : AppColors.goldDark,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: empty
|
||||
? const Icon(Icons.person_add_alt_1, color: Colors.white24)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
child: RandomAvatar(p.name),
|
||||
),
|
||||
child:
|
||||
empty
|
||||
? const Icon(
|
||||
Icons.person_add_alt_1,
|
||||
color: Colors.white24,
|
||||
)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
child: RandomAvatar(p.name),
|
||||
),
|
||||
),
|
||||
if (!empty)
|
||||
const Positioned(
|
||||
@@ -274,8 +308,10 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
),
|
||||
),
|
||||
if (p?.host == true)
|
||||
const Text('میزبان',
|
||||
style: TextStyle(color: AppColors.gold, fontSize: 10)),
|
||||
const Text(
|
||||
'میزبان',
|
||||
style: TextStyle(color: AppColors.gold, fontSize: 10),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -314,14 +350,17 @@ class _LobbyViewState extends State<_LobbyView> {
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: selected
|
||||
? const LinearGradient(
|
||||
colors: [AppColors.gold, AppColors.goldDark])
|
||||
: null,
|
||||
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: 2),
|
||||
color: selected ? AppColors.gold : AppColors.goldDark,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
fa[h]!,
|
||||
@@ -377,8 +416,9 @@ class _CountdownOverlayState extends State<_CountdownOverlay> {
|
||||
tween: Tween(begin: 0.4, end: 1.2),
|
||||
duration: const Duration(milliseconds: 700),
|
||||
curve: Curves.easeOut,
|
||||
builder: (context, scale, child) =>
|
||||
Transform.scale(scale: scale, child: child),
|
||||
builder:
|
||||
(context, scale, child) =>
|
||||
Transform.scale(scale: scale, child: child),
|
||||
child: GlowText(_n > 0 ? '$_n' : 'شروع!', size: 96),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@ class TierListScreen extends StatelessWidget {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(children: [
|
||||
_BackButton(onTap: () => context.pop()),
|
||||
const AppBackButton(),
|
||||
const Spacer(),
|
||||
const GlowText('انتخاب میز', size: 26),
|
||||
const Spacer(),
|
||||
@@ -64,25 +64,6 @@ class TierListScreen extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _BackButton extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
const _BackButton({required this.onTap});
|
||||
@override
|
||||
Widget build(BuildContext context) => GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.panel,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColors.gold, width: 1.5),
|
||||
),
|
||||
child: const Icon(Icons.arrow_back, color: AppColors.gold),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _TierCard extends StatelessWidget {
|
||||
final TableTier tier;
|
||||
final int index;
|
||||
|
||||
Reference in New Issue
Block a user