feat: style
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
صداهای بازی را اینجا بگذارید:
|
||||||
|
shuffle.mp3 — صدای بُر زدن/پخش کارت (هنگام شروع هر دست پخش میشود)
|
||||||
|
اگر این فایل نباشد، بازی بیصدا اجرا میشود (بدون خطا).
|
||||||
|
میتوانید بعداً play.mp3 / win.mp3 و ... هم اضافه کنید.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5,9 +5,9 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
/// یک کارت روی میز؛ اگر تصویر `assets/images/cards/<code>.png` موجود باشد از آن
|
/// یک کارت روی میز؛ اگر تصویر `assets/images/cards/<code>.png` موجود باشد از آن
|
||||||
/// استفاده میکند، وگرنه نسخهی برداری میکشد. برای پشت کارت `back.jpg`.
|
/// استفاده میکند، وگرنه نسخهی برداری میکشد. برای پشت کارت `back.jpg`.
|
||||||
/// کارتهای قابلبازی هم با tap و هم با کشیدن (drag) به سمت زمین بازی میشوند.
|
/// کارتهای قابلبازی فقط با **کشیدن (drag)** به سمت زمین بازی میشوند (نه tap).
|
||||||
class CardComponent extends PositionComponent
|
class CardComponent extends PositionComponent
|
||||||
with TapCallbacks, DragCallbacks, HasGameReference {
|
with DragCallbacks, HasGameReference {
|
||||||
final String code; // مثل "AS"؛ برای پشت کارت خالی
|
final String code; // مثل "AS"؛ برای پشت کارت خالی
|
||||||
final bool faceUp;
|
final bool faceUp;
|
||||||
VoidCallback? onPlay; // در صورت مجاز بودن، بازیِ این کارت
|
VoidCallback? onPlay; // در صورت مجاز بودن، بازیِ این کارت
|
||||||
@@ -42,11 +42,6 @@ class CardComponent extends PositionComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void onTapUp(TapUpEvent event) {
|
|
||||||
if (_playable) onPlay!.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onDragStart(DragStartEvent event) {
|
void onDragStart(DragStartEvent event) {
|
||||||
super.onDragStart(event);
|
super.onDragStart(event);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'dart:math' as math;
|
|||||||
import 'package:flame/components.dart';
|
import 'package:flame/components.dart';
|
||||||
import 'package:flame/effects.dart';
|
import 'package:flame/effects.dart';
|
||||||
import 'package:flame/game.dart';
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:flame_audio/flame_audio.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../game_cubit.dart';
|
import '../game_cubit.dart';
|
||||||
@@ -40,6 +41,7 @@ class HokmGame extends FlameGame {
|
|||||||
double _shake = 0;
|
double _shake = 0;
|
||||||
double _t = 0;
|
double _t = 0;
|
||||||
List<String> _prevTrick = const [];
|
List<String> _prevTrick = const [];
|
||||||
|
int _prevHandSize = 0; // برای تشخیصِ پخشِ کارت (دستِ جدید) جهت صدای بُر زدن
|
||||||
|
|
||||||
HokmGame(this.cubit);
|
HokmGame(this.cubit);
|
||||||
|
|
||||||
@@ -49,19 +51,31 @@ class HokmGame extends FlameGame {
|
|||||||
@override
|
@override
|
||||||
Future<void> onLoad() async {
|
Future<void> onLoad() async {
|
||||||
add(Felt());
|
add(Felt());
|
||||||
|
// پیشبارگذاریِ صدای بُر زدن (در صورت نبودِ فایل، بیصدا رد میشود).
|
||||||
|
FlameAudio.audioCache.load('shuffle.mp3').catchError((_) => Uri());
|
||||||
_sub = cubit.stream.listen((ui) {
|
_sub = cubit.stream.listen((ui) {
|
||||||
if (ui.state == null) return;
|
if (ui.state == null) return;
|
||||||
_checkCut(ui.state!);
|
final s = ui.state!;
|
||||||
_s = ui.state;
|
_checkCut(s);
|
||||||
|
// شروعِ دستِ جدید (پخشِ کارت): صدای بُر زدن.
|
||||||
|
if (_prevHandSize == 0 && s.yourHand.isNotEmpty) _playShuffle();
|
||||||
|
_prevHandSize = s.yourHand.length;
|
||||||
|
_s = s;
|
||||||
_relayout();
|
_relayout();
|
||||||
});
|
});
|
||||||
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;
|
||||||
_s = cubit.state.state;
|
_s = cubit.state.state;
|
||||||
_relayout();
|
_relayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _playShuffle() {
|
||||||
|
// اگر فایل صدا نباشد، خطا نادیده گرفته میشود (بازی بیصدا).
|
||||||
|
FlameAudio.play('shuffle.mp3', volume: 0.7).then((_) {}, onError: (_) {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onGameResize(Vector2 size) {
|
void onGameResize(Vector2 size) {
|
||||||
super.onGameResize(size);
|
super.onGameResize(size);
|
||||||
@@ -173,16 +187,21 @@ class HokmGame extends FlameGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// کارتهای دستِ خودتان بزرگتر از کارتهای روی میز.
|
// چیدمانِ بادبزنیِ منحنی: کارتها چرخش و قوسِ ملایم دارند (مثل دستِ واقعی).
|
||||||
final hw = size.x * 0.225, hh = hw * kCardRatio;
|
final hw = size.x * 0.225, hh = hw * kCardRatio;
|
||||||
final handW = size.x * 0.80;
|
final handW = size.x * 0.86;
|
||||||
final step = n > 1 ? (handW - hw) / (n - 1) : 0.0;
|
final stepX = n > 1 ? ((handW - hw) / (n - 1)).clamp(0.0, hw * 0.62) : 0.0;
|
||||||
final startX = size.x / 2 - (step * (n - 1)) / 2;
|
final cx = size.x / 2;
|
||||||
final y = size.y * 0.85;
|
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++) {
|
for (var i = 0; i < n; i++) {
|
||||||
final code = codes[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);
|
final legal = _legal(code);
|
||||||
var c = _hand[code];
|
var c = _hand[code];
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
@@ -193,6 +212,7 @@ class HokmGame extends FlameGame {
|
|||||||
} else {
|
} else {
|
||||||
c.size = Vector2(hw, hh);
|
c.size = Vector2(hw, hh);
|
||||||
}
|
}
|
||||||
|
c.angle = norm * edgeAngle; // چرخشِ بادبزنی
|
||||||
c.onPlay = legal ? () => cubit.playCard(code) : null;
|
c.onPlay = legal ? () => cubit.playCard(code) : null;
|
||||||
c.dimmed = s.phase == 'playing' && s.isMyTurn && !legal;
|
c.dimmed = s.phase == 'playing' && s.isMyTurn && !legal;
|
||||||
c.home = target;
|
c.home = target;
|
||||||
@@ -240,6 +260,7 @@ class HokmGame extends FlameGame {
|
|||||||
_trick[tc.card] = c;
|
_trick[tc.card] = c;
|
||||||
}
|
}
|
||||||
c.size = Vector2(_cardW, _cardH);
|
c.size = Vector2(_cardW, _cardH);
|
||||||
|
c.angle = 0; // کارتهای روی زمین صافاند (چرخشِ بادبزنیِ دست حذف میشود)
|
||||||
c.priority = 5;
|
c.priority = 5;
|
||||||
_moveTo(c, slot);
|
_moveTo(c, slot);
|
||||||
}
|
}
|
||||||
|
|||||||
+108
-4
@@ -9,6 +9,62 @@ packages:
|
|||||||
url: "https://pub.myket.ir"
|
url: "https://pub.myket.ir"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.0"
|
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:
|
bloc:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -105,14 +161,38 @@ packages:
|
|||||||
url: "https://pub.myket.ir"
|
url: "https://pub.myket.ir"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
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:
|
flame:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flame
|
name: flame
|
||||||
sha256: c50e2f39e118f5f6a6f3339ce6825ee803c7e5fada95ec50fb02eb27944e0e76
|
sha256: d084557523a45ad446d2480a4fa5a1c75aade5f8eb54251418e0ed4ee73ff3b8
|
||||||
url: "https://pub.myket.ir"
|
url: "https://pub.myket.ir"
|
||||||
source: hosted
|
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:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -200,6 +280,14 @@ packages:
|
|||||||
url: "https://pub.myket.ir"
|
url: "https://pub.myket.ir"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "17.0.0"
|
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:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -413,6 +501,14 @@ packages:
|
|||||||
url: "https://pub.myket.ir"
|
url: "https://pub.myket.ir"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
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:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -437,6 +533,14 @@ packages:
|
|||||||
url: "https://pub.myket.ir"
|
url: "https://pub.myket.ir"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
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:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -494,5 +598,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.8.0-0 <4.0.0"
|
dart: ">=3.8.0 <4.0.0"
|
||||||
flutter: ">=3.29.0"
|
flutter: ">=3.35.0"
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ dependencies:
|
|||||||
flutter_secure_storage: ^10.3.1
|
flutter_secure_storage: ^10.3.1
|
||||||
equatable: ^2.0.8
|
equatable: ^2.0.8
|
||||||
flame: ^1.30.1
|
flame: ^1.30.1
|
||||||
|
flame_audio: ^2.11.14
|
||||||
|
|
||||||
# path_provider_android نسخهی 2.3.x به jni (کد native + NDK) وابسته شد و build را
|
# path_provider_android نسخهی 2.3.x به jni (کد native + NDK) وابسته شد و build را
|
||||||
# سنگین/کند میکند. تا نسخهی پیشاز‑jni پین میکنیم تا NDK لازم نباشد.
|
# سنگین/کند میکند. تا نسخهی پیشاز‑jni پین میکنیم تا NDK لازم نباشد.
|
||||||
@@ -69,6 +70,7 @@ flutter:
|
|||||||
assets:
|
assets:
|
||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/images/cards/
|
- assets/images/cards/
|
||||||
|
- assets/audio/
|
||||||
|
|
||||||
fonts:
|
fonts:
|
||||||
- family: Dana
|
- family: Dana
|
||||||
|
|||||||
Reference in New Issue
Block a user