feat: style

This commit is contained in:
2026-06-17 08:04:10 +03:30
parent 32c0e543d9
commit 943ba13872
9 changed files with 145 additions and 19 deletions
+4
View File
@@ -0,0 +1,4 @@
صداهای بازی را اینجا بگذارید:
shuffle.mp3 — صدای بُر زدن/پخش کارت (هنگام شروع هر دست پخش می‌شود)
اگر این فایل نباشد، بازی بی‌صدا اجرا می‌شود (بدون خطا).
می‌توانید بعداً play.mp3 / win.mp3 و ... هم اضافه کنید.
Binary file not shown.
+2 -7
View File
@@ -5,9 +5,9 @@ import 'package:flutter/material.dart';
/// یک کارت روی میز؛ اگر تصویر `assets/images/cards/<code>.png` موجود باشد از آن
/// استفاده می‌کند، وگرنه نسخه‌ی برداری می‌کشد. برای پشت کارت `back.jpg`.
/// کارت‌های قابل‌بازی هم با tap و هم با کشیدن (drag) به سمت زمین بازی می‌شوند.
/// کارت‌های قابل‌بازی فقط با **کشیدن (drag)** به سمت زمین بازی می‌شوند (نه tap).
class CardComponent extends PositionComponent
with TapCallbacks, DragCallbacks, HasGameReference {
with DragCallbacks, HasGameReference {
final String code; // مثل "AS"؛ برای پشت کارت خالی
final bool faceUp;
VoidCallback? onPlay; // در صورت مجاز بودن، بازیِ این کارت
@@ -42,11 +42,6 @@ class CardComponent extends PositionComponent
}
}
@override
void onTapUp(TapUpEvent event) {
if (_playable) onPlay!.call();
}
@override
void onDragStart(DragStartEvent event) {
super.onDragStart(event);
+29 -8
View File
@@ -4,6 +4,7 @@ import 'dart:math' as math;
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/game.dart';
import 'package:flame_audio/flame_audio.dart';
import 'package:flutter/material.dart';
import '../game_cubit.dart';
@@ -40,6 +41,7 @@ class HokmGame extends FlameGame {
double _shake = 0;
double _t = 0;
List<String> _prevTrick = const [];
int _prevHandSize = 0; // برای تشخیصِ پخشِ کارت (دستِ جدید) جهت صدای بُر زدن
HokmGame(this.cubit);
@@ -49,19 +51,31 @@ class HokmGame extends FlameGame {
@override
Future<void> onLoad() async {
add(Felt());
// پیش‌بارگذاریِ صدای بُر زدن (در صورت نبودِ فایل، بی‌صدا رد می‌شود).
FlameAudio.audioCache.load('shuffle.mp3').catchError((_) => Uri());
_sub = cubit.stream.listen((ui) {
if (ui.state == null) return;
_checkCut(ui.state!);
_s = ui.state;
final s = ui.state!;
_checkCut(s);
// شروعِ دستِ جدید (پخشِ کارت): صدای بُر زدن.
if (_prevHandSize == 0 && s.yourHand.isNotEmpty) _playShuffle();
_prevHandSize = s.yourHand.length;
_s = s;
_relayout();
});
if (cubit.state.state != null) {
_prevTrick = cubit.state.state!.trick.map((t) => t.card).toList();
_prevHandSize = cubit.state.state!.yourHand.length;
_s = cubit.state.state;
_relayout();
}
}
void _playShuffle() {
// اگر فایل صدا نباشد، خطا نادیده گرفته می‌شود (بازی بی‌صدا).
FlameAudio.play('shuffle.mp3', volume: 0.7).then((_) {}, onError: (_) {});
}
@override
void onGameResize(Vector2 size) {
super.onGameResize(size);
@@ -173,16 +187,21 @@ class HokmGame extends FlameGame {
}
}
// کارت‌های دستِ خودتان بزرگ‌تر از کارت‌های روی میز.
// چیدمانِ بادبزنیِ منحنی: کارت‌ها چرخش و قوسِ ملایم دارند (مثل دستِ واقعی).
final hw = size.x * 0.225, hh = hw * kCardRatio;
final handW = size.x * 0.80;
final step = n > 1 ? (handW - hw) / (n - 1) : 0.0;
final startX = size.x / 2 - (step * (n - 1)) / 2;
final y = size.y * 0.85;
final handW = size.x * 0.86;
final stepX = n > 1 ? ((handW - hw) / (n - 1)).clamp(0.0, hw * 0.62) : 0.0;
final cx = size.x / 2;
final baseY = size.y * 0.86;
final tMax = (n - 1) / 2;
const edgeAngle = 0.34; // چرخشِ کارت‌های کناری (رادیان)
final dip = hh * 0.16; // افتِ عمودیِ کارت‌های کناری برای قوس
for (var i = 0; i < n; i++) {
final code = codes[i];
final target = Vector2(startX + i * step, y);
final t = i - tMax;
final norm = tMax > 0 ? t / tMax : 0.0;
final target = Vector2(cx + t * stepX, baseY + norm * norm * dip);
final legal = _legal(code);
var c = _hand[code];
if (c == null) {
@@ -193,6 +212,7 @@ class HokmGame extends FlameGame {
} else {
c.size = Vector2(hw, hh);
}
c.angle = norm * edgeAngle; // چرخشِ بادبزنی
c.onPlay = legal ? () => cubit.playCard(code) : null;
c.dimmed = s.phase == 'playing' && s.isMyTurn && !legal;
c.home = target;
@@ -240,6 +260,7 @@ class HokmGame extends FlameGame {
_trick[tc.card] = c;
}
c.size = Vector2(_cardW, _cardH);
c.angle = 0; // کارت‌های روی زمین صاف‌اند (چرخشِ بادبزنیِ دست حذف می‌شود)
c.priority = 5;
_moveTo(c, slot);
}
+108 -4
View File
@@ -9,6 +9,62 @@ packages:
url: "https://pub.myket.ir"
source: hosted
version: "2.12.0"
audioplayers:
dependency: transitive
description:
name: audioplayers
sha256: f16640453cc47487b7de72a2b28d37c7df1ac97999849f4a46d92b1d2b0f093d
url: "https://pub.myket.ir"
source: hosted
version: "6.7.1"
audioplayers_android:
dependency: transitive
description:
name: audioplayers_android
sha256: "60a6728277228413a85755bd3ffd6fab98f6555608923813ce383b190a360605"
url: "https://pub.myket.ir"
source: hosted
version: "5.2.1"
audioplayers_darwin:
dependency: transitive
description:
name: audioplayers_darwin
sha256: c994b3bb3a921e4904ac40e013fbc94488e824fd7c1de6326f549943b0b44a91
url: "https://pub.myket.ir"
source: hosted
version: "6.4.0"
audioplayers_linux:
dependency: transitive
description:
name: audioplayers_linux
sha256: f75bce1ce864170ef5e6a2c6a61cd3339e1a17ce11e99a25bae4474ea491d001
url: "https://pub.myket.ir"
source: hosted
version: "4.2.1"
audioplayers_platform_interface:
dependency: transitive
description:
name: audioplayers_platform_interface
sha256: "0e2f6a919ab56d0fec272e801abc07b26ae7f31980f912f24af4748763e5a656"
url: "https://pub.myket.ir"
source: hosted
version: "7.1.1"
audioplayers_web:
dependency: transitive
description:
name: audioplayers_web
sha256: "24a6f258062bd7da8cb2157e83fccb9816a08dd306cbaaa24f9813d071470545"
url: "https://pub.myket.ir"
source: hosted
version: "5.2.1"
audioplayers_windows:
dependency: transitive
description:
name: audioplayers_windows
sha256: "95f875a96c88c3dbbcb608d4f8288e300b0113d256a81d0b3197fcc18f0dc91a"
url: "https://pub.myket.ir"
source: hosted
version: "4.3.1"
bloc:
dependency: transitive
description:
@@ -105,14 +161,38 @@ packages:
url: "https://pub.myket.ir"
source: hosted
version: "2.2.0"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.myket.ir"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.myket.ir"
source: hosted
version: "1.1.1"
flame:
dependency: "direct main"
description:
name: flame
sha256: c50e2f39e118f5f6a6f3339ce6825ee803c7e5fada95ec50fb02eb27944e0e76
sha256: d084557523a45ad446d2480a4fa5a1c75aade5f8eb54251418e0ed4ee73ff3b8
url: "https://pub.myket.ir"
source: hosted
version: "1.30.1"
version: "1.35.1"
flame_audio:
dependency: "direct main"
description:
name: flame_audio
sha256: "47d414720769fd0f1e73b1f14f857018fe49509452fe6c412d05357c6285f987"
url: "https://pub.myket.ir"
source: hosted
version: "2.11.14"
flutter:
dependency: "direct main"
description: flutter
@@ -200,6 +280,14 @@ packages:
url: "https://pub.myket.ir"
source: hosted
version: "17.0.0"
http:
dependency: transitive
description:
name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.myket.ir"
source: hosted
version: "1.6.0"
http_parser:
dependency: transitive
description:
@@ -413,6 +501,14 @@ packages:
url: "https://pub.myket.ir"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
url: "https://pub.myket.ir"
source: hosted
version: "3.4.0"
term_glyph:
dependency: transitive
description:
@@ -437,6 +533,14 @@ packages:
url: "https://pub.myket.ir"
source: hosted
version: "1.4.0"
uuid:
dependency: transitive
description:
name: uuid
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
url: "https://pub.myket.ir"
source: hosted
version: "4.5.3"
vector_math:
dependency: transitive
description:
@@ -494,5 +598,5 @@ packages:
source: hosted
version: "1.1.0"
sdks:
dart: ">=3.8.0-0 <4.0.0"
flutter: ">=3.29.0"
dart: ">=3.8.0 <4.0.0"
flutter: ">=3.35.0"
+2
View File
@@ -41,6 +41,7 @@ dependencies:
flutter_secure_storage: ^10.3.1
equatable: ^2.0.8
flame: ^1.30.1
flame_audio: ^2.11.14
# path_provider_android نسخه‌ی 2.3.x به jni (کد native + NDK) وابسته شد و build را
# سنگین/کند می‌کند. تا نسخه‌ی پیش‌از‑jni پین می‌کنیم تا NDK لازم نباشد.
@@ -69,6 +70,7 @@ flutter:
assets:
- assets/images/
- assets/images/cards/
- assets/audio/
fonts:
- family: Dana