import 'dart:math' as math; import 'package:flame/components.dart'; import 'package:flutter/material.dart'; // رنگ‌های مشترکِ میز. const _gold = Color(0xFFE9B949); const _goldDark = Color(0xFFB8860B); /// نمدِ سبزِ بیضی‌شکلِ میز با عمق: لبه‌ی برجسته، گرادیانِ شعاعی و وینیت. 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); final rect = Rect.fromCenter(center: center, width: w * 0.86, height: h * 0.62); final radius = Radius.circular(h * 0.3); final felt = RRect.fromRectAndRadius(rect, radius); // ۱) لبه‌ی چوبیِ بیرونی (ریل) با گرادیان — حسِ برجستگی بدون drawShadowِ هر-فریمی. final rail = RRect.fromRectAndRadius( rect.inflate(w * 0.03), Radius.circular(h * 0.33)); canvas.drawRRect( rail, Paint() ..shader = const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFF5A2E12), Color(0xFF2E1608)], ).createShader(rail.outerRect), ); // ۲) حلقه‌ی طلایی بین ریل و نمد. canvas.drawRRect( RRect.fromRectAndRadius(rect.inflate(w * 0.008), radius), Paint() ..style = PaintingStyle.stroke ..strokeWidth = w * 0.012 ..color = _gold, ); // ۳) نمدِ سبز با گرادیانِ شعاعی (مرکز روشن، لبه‌ها تیره) — خودش حسِ وینیت/عمق می‌دهد. canvas.drawRRect( felt, Paint() ..shader = RadialGradient( center: Alignment.center, radius: 0.95, colors: const [Color(0xFF34A03F), Color(0xFF0E3614)], stops: const [0.45, 1.0], ).createShader(rect), ); } } /// دیسکِ فلزیِ شمارش (امتیاز تیم) با عددِ وسط؛ از ۰ تا ۷. 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); } } }