import 'dart:math' as math; import 'package:flame/components.dart'; import 'package:flutter/material.dart'; import '../../../../../core/theme/app_theme.dart'; // رنگ‌های مشترکِ میز (منبعِ واحد در AppColors). const _gold = AppColors.gold; const _goldDark = AppColors.goldDark; /// میز: زمینِ سبزِ نمدی در کلِ صفحه + یک «میزِ» مرکزیِ سرمه‌ای با قابِ طلاییِ /// دولایه (مطابقِ تصویرِ مرجع). کارت‌های زمین داخلِ ناحیه‌ی سرمه‌ای انداخته می‌شوند /// و پشت‌کارتِ حریفان روی نمدِ سبزِ دورِ آن می‌نشیند. class Felt extends PositionComponent with HasGameReference { @override void render(Canvas canvas) { final w = game.size.x, h = game.size.y; final center = Offset(w / 2, h / 2); // ۱) زمینِ لاجوردیِ نمدی (کلِ صفحه) با وینیتِ شعاعی برای عمق. canvas.drawRect( Rect.fromLTWH(0, 0, w, h), Paint() ..shader = RadialGradient( center: Alignment.center, radius: 1.0, colors: const [AppColors.lapisHi, AppColors.lapisNight], stops: const [0.5, 1.0], ).createShader(Rect.fromLTWH(0, 0, w, h)), ); // ۲) میزِ مرکزیِ سرمه‌ای با قابِ طلایی. final rect = Rect.fromCenter( center: center, width: w * 0.72, height: h * 0.46); final radius = Radius.circular(h * 0.05); final table = RRect.fromRectAndRadius(rect, radius); // سایه‌ی نرمِ زیرِ میز (یک‌بار، نه هر-فریمی سنگین). canvas.drawRRect( table.shift(const Offset(0, 6)), Paint() ..color = const Color(0x66000000) ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 12), ); // قابِ طلاییِ بیرونی. canvas.drawRRect( RRect.fromRectAndRadius(rect.inflate(w * 0.012), radius), Paint() ..shader = const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [_gold, _goldDark], ).createShader(rect), ); // سطحِ سرمه‌ایِ میز با گرادیانِ شعاعی (مرکز روشن‌تر). canvas.drawRRect( table, Paint() ..shader = RadialGradient( center: Alignment.center, radius: 0.9, colors: const [AppColors.lapisLo, AppColors.lapisInk], stops: const [0.4, 1.0], ).createShader(rect), ); // خطِ طلاییِ نازکِ داخلی (قابِ دولایه). canvas.drawRRect( RRect.fromRectAndRadius(rect.deflate(w * 0.018), Radius.circular(h * 0.04)), Paint() ..style = PaintingStyle.stroke ..strokeWidth = w * 0.004 ..color = _gold.withValues(alpha: 0.65), ); } } /// دیسکِ فلزیِ شمارش (امتیاز تیم) با عددِ وسط؛ از ۰ تا ۷. class ScorePuck extends PositionComponent { final int count; ScorePuck(this.count, {required double radius, super.position}) : super(size: Vector2.all(radius * 2), anchor: Anchor.center); @override void render(Canvas canvas) { final r = size.x / 2; final c = Offset(r, r); canvas.drawCircle(c, r, Paint()..color = const Color(0xFF14110F)); canvas.drawCircle( c, r * 0.92, Paint() ..style = PaintingStyle.stroke ..strokeWidth = r * 0.22 ..color = _goldDark, ); final tp = TextPainter( text: TextSpan( text: '$count', style: TextStyle( fontFamily: 'Dana', color: _gold, fontSize: r * 1.05, fontWeight: FontWeight.bold, ), ), textDirection: TextDirection.ltr, )..layout(); tp.paint(canvas, c - Offset(tp.width / 2, tp.height / 2)); } } /// افکت رعد روی زمین هنگام «بریدن با حکم»؛ پس از مدت کوتاهی خودش حذف می‌شود. class Lightning extends PositionComponent with HasGameReference { static const _max = 0.55; double _life = _max; final _rng = math.Random(); final List> _bolts = []; @override Future onLoad() async { size = game.size; final center = Offset(size.x / 2, size.y / 2); for (var i = 0; i < 3; i++) { // هر رعد یک خط شکسته از یک نقطه‌ی تصادفیِ بالا به سمت مرکز. final start = Offset(_rng.nextDouble() * size.x, _rng.nextDouble() * size.y * 0.4); final pts = [start]; const segs = 6; for (var s = 1; s <= segs; s++) { final t = s / segs; final base = Offset.lerp(start, center, t)!; final jitter = (1 - t) * size.x * 0.06; pts.add(base + Offset((_rng.nextDouble() - 0.5) * jitter, (_rng.nextDouble() - 0.5) * jitter)); } _bolts.add(pts); } } @override void update(double dt) { _life -= dt; if (_life <= 0) removeFromParent(); } @override void render(Canvas canvas) { final op = (_life / _max).clamp(0.0, 1.0); final glow = Paint() ..color = const Color(0xFFFFE082).withValues(alpha: op * 0.5) ..style = PaintingStyle.stroke ..strokeWidth = size.x * 0.02 ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6); final core = Paint() ..color = const Color(0xFFFFFDE7).withValues(alpha: op) ..style = PaintingStyle.stroke ..strokeWidth = size.x * 0.006 ..strokeCap = StrokeCap.round; for (final bolt in _bolts) { final path = Path()..moveTo(bolt.first.dx, bolt.first.dy); for (final p in bolt.skip(1)) { path.lineTo(p.dx, p.dy); } canvas.drawPath(path, glow); canvas.drawPath(path, core); } } }