feat: phase 1 of improve
This commit is contained in:
@@ -2,6 +2,7 @@ import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame/events.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show HapticFeedback;
|
||||
|
||||
import '../../../../../core/network/card_images.dart';
|
||||
import '../../../../../core/theme/app_theme.dart';
|
||||
@@ -17,6 +18,7 @@ class CardComponent extends PositionComponent
|
||||
VoidCallback? onPlay; // در صورت مجاز بودن، بازیِ این کارت
|
||||
bool dimmed; // کارت غیرمجاز/غیرفعال
|
||||
Vector2? home; // موقعیت اصلی در دست (برای برگشت پس از کشیدنِ ناقص)
|
||||
bool highlight = false; // کارتِ برندهی دست ⇒ درخششِ طلایی پیش از جمعشدن
|
||||
int restPriority = 0; // ترتیب لایهی اصلی در دست (برای بازگردانی پس از کشیدن)
|
||||
Rect? dropZone; // ناحیهی وسط میز؛ رهاکردن کارت در آن یعنی بازی
|
||||
Sprite? _front;
|
||||
@@ -76,6 +78,15 @@ class CardComponent extends PositionComponent
|
||||
scale = Vector2.all(1);
|
||||
}
|
||||
|
||||
/// یک «پاپِ» کوتاه (بزرگنماییِ رفتوبرگشتی) برای کارتِ برندهی دست.
|
||||
void pulse() {
|
||||
children.whereType<ScaleEffect>().toList().forEach((e) => e.removeFromParent());
|
||||
add(ScaleEffect.to(
|
||||
Vector2.all(1.16),
|
||||
EffectController(duration: 0.18, alternate: true, curve: Curves.easeOut),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void onDragStart(DragStartEvent event) {
|
||||
super.onDragStart(event);
|
||||
@@ -101,6 +112,7 @@ class CardComponent extends PositionComponent
|
||||
// وگرنه برگشت به جای مرتبِ خود.
|
||||
if (_inDropZone()) {
|
||||
resetScale();
|
||||
HapticFeedback.selectionClick(); // بازخوردِ لمسیِ بازیِ کارت
|
||||
onPlay?.call();
|
||||
} else {
|
||||
_scaleTo(1);
|
||||
@@ -184,6 +196,18 @@ class CardComponent extends PositionComponent
|
||||
..color = AppColors.suitDark,
|
||||
);
|
||||
|
||||
// درخششِ طلاییِ کارتِ برندهی دست (فقط یک کارت همزمان ⇒ blur ارزان).
|
||||
if (highlight) {
|
||||
canvas.drawRRect(
|
||||
rrect,
|
||||
Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = size.x * 0.07
|
||||
..color = AppColors.gold
|
||||
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6),
|
||||
);
|
||||
}
|
||||
|
||||
// هایلایت طلایی: هنگام کشیدن همیشه (انتخاب)، و پررنگتر روی ناحیهی انداختن.
|
||||
if (_dragging) {
|
||||
canvas.drawRRect(
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show HapticFeedback;
|
||||
|
||||
import '../../../../../core/service/app_sounds.dart';
|
||||
import '../../../../../core/theme/app_theme.dart';
|
||||
@@ -46,6 +47,7 @@ class HokmGame extends FlameGame {
|
||||
double _shake = 0;
|
||||
double _t = 0;
|
||||
List<String> _prevTrick = const [];
|
||||
bool _highlightedTrick = false; // آیا کارتِ برندهی دستِ کامل هایلایت شده (یکبار)
|
||||
int _bestTrumpRank = 0; // بالاترین رتبهی حکمِ بازیشده در دستِ جاری (برای افکتِ بریدن)
|
||||
int _prevHandSize = 0; // برای تشخیصِ پخشِ کارت (دستِ جدید) جهت صدای بُر زدن
|
||||
String _prevHandSig = ''; // امضای دست؛ برای تشخیصِ بُرِ مجدد (تغییرِ ۵ کارت در فازِ حکم)
|
||||
@@ -171,6 +173,7 @@ class HokmGame extends FlameGame {
|
||||
_shake = 1.0;
|
||||
add(Lightning());
|
||||
AppSounds.cut(); // بریدن با حکم (شمشیر)
|
||||
HapticFeedback.heavyImpact(); // ضربهی لمسیِ بُرِش با حکم
|
||||
} else {
|
||||
AppSounds.placeCard(); // کارت روی میز نشست
|
||||
}
|
||||
@@ -475,22 +478,30 @@ class HokmGame extends FlameGame {
|
||||
}
|
||||
final present = s.trick.map((t) => t.card).toSet();
|
||||
|
||||
// کارتهای trick که دیگر نیستند (دست جمع شد) ⇒ به سمت برنده برو و حذف شو.
|
||||
// کارتهای trick که دیگر نیستند (دست جمع شد) ⇒ برنده کارتها را «جمع میکند»:
|
||||
// به سمتِ جایگاهِ برنده (s.turn) پرواز میکنند، کوچک میشوند و محو (scoop).
|
||||
final winnerOrigin = _seatOrigin(_rel(s.turn));
|
||||
var i = 0;
|
||||
for (final code in _trick.keys.toList()) {
|
||||
if (present.contains(code)) continue;
|
||||
final c = _trick.remove(code)!;
|
||||
c.children.whereType<MoveToEffect>().toList().forEach(
|
||||
(e) => e.removeFromParent(),
|
||||
);
|
||||
c.add(
|
||||
SequenceEffect([
|
||||
MoveToEffect(
|
||||
_seatOrigin(_rel(s.turn)),
|
||||
EffectController(duration: 0.25, curve: Curves.easeIn),
|
||||
),
|
||||
RemoveEffect(),
|
||||
]),
|
||||
);
|
||||
c.highlight = false;
|
||||
c.priority = 30; // رویِ همه هنگام جمعشدن
|
||||
c.children.whereType<Effect>().toList().forEach((e) => e.removeFromParent());
|
||||
final delay = i * 0.05; // ترتیبِ کوتاه برای حسِ «جارو کردن»
|
||||
c.add(ScaleEffect.to(
|
||||
Vector2.all(0.5),
|
||||
EffectController(duration: 0.34, startDelay: delay, curve: Curves.easeIn),
|
||||
));
|
||||
c.add(SequenceEffect([
|
||||
MoveToEffect(
|
||||
winnerOrigin,
|
||||
EffectController(
|
||||
duration: 0.36, startDelay: delay, curve: Curves.easeInBack),
|
||||
),
|
||||
RemoveEffect(),
|
||||
]));
|
||||
i++;
|
||||
}
|
||||
|
||||
for (final tc in s.trick) {
|
||||
@@ -521,6 +532,45 @@ class HokmGame extends FlameGame {
|
||||
c.priority = 5;
|
||||
_moveTo(c, slot);
|
||||
}
|
||||
|
||||
// دستِ کامل (۴ کارت) ⇒ کارتِ برنده را طلایی هایلایت کن و یکبار «پاپ» بزن،
|
||||
// تا کاربر پیش از جمعشدنِ کارتها بفهمد چه کسی برد.
|
||||
if (s.trickDone && s.trick.length == 4) {
|
||||
final ws = _winnerSeat(s.trick, s.trump, s.leadSuit);
|
||||
for (final tc in s.trick) {
|
||||
final comp = _trick[tc.card];
|
||||
if (comp == null) continue;
|
||||
final win = tc.seat == ws;
|
||||
comp.highlight = win;
|
||||
if (win && !_highlightedTrick) comp.pulse();
|
||||
}
|
||||
_highlightedTrick = true;
|
||||
} else if (!s.trickDone) {
|
||||
_highlightedTrick = false;
|
||||
for (final c in _trick.values) {
|
||||
c.highlight = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// برندهی دستِ جاری را از روی کارتها + حکم + خالِ زمینه حساب میکند (سمتِ کلاینت).
|
||||
int _winnerSeat(List<TrickCard> trick, String? trump, String? lead) {
|
||||
var best = trick.first;
|
||||
for (final tc in trick.skip(1)) {
|
||||
if (_beats(tc.card, best.card, trump, lead)) best = tc;
|
||||
}
|
||||
return best.seat;
|
||||
}
|
||||
|
||||
bool _beats(String a, String b, String? trump, String? lead) {
|
||||
final aS = suitName(a), bS = suitName(b);
|
||||
final aT = aS == trump, bT = bS == trump;
|
||||
if (aT && !bT) return true;
|
||||
if (!aT && bT) return false;
|
||||
if (aT && bT) return rankValue(a) > rankValue(b);
|
||||
if (aS != lead) return false; // فقط کارتِ همخالِ زمینه میتواند ببرد
|
||||
if (bS != lead) return true;
|
||||
return rankValue(a) > rankValue(b);
|
||||
}
|
||||
|
||||
// مبدأِ نشستنِ هر جایگاه (برای پرتاب/جمعِ کارتها).
|
||||
|
||||
@@ -99,6 +99,21 @@ class Felt extends PositionComponent with HasGameReference {
|
||||
);
|
||||
}
|
||||
|
||||
// نورِ نرمِ مرکزی (اسپاتلایت) برای برجستهشدنِ کارتهای وسطِ میز.
|
||||
canvas.save();
|
||||
canvas.clipRRect(table);
|
||||
canvas.drawRect(
|
||||
rect,
|
||||
Paint()
|
||||
..shader = RadialGradient(
|
||||
center: Alignment.center,
|
||||
radius: 0.62,
|
||||
colors: const [Color(0x1FFFFFFF), Color(0x00FFFFFF)],
|
||||
stops: const [0.0, 1.0],
|
||||
).createShader(rect),
|
||||
);
|
||||
canvas.restore();
|
||||
|
||||
// خطِ طلاییِ نازکِ داخلی (قابِ دولایه).
|
||||
canvas.drawRRect(
|
||||
RRect.fromRectAndRadius(rect.deflate(w * 0.018), Radius.circular(h * 0.04)),
|
||||
|
||||
Reference in New Issue
Block a user