174 lines
5.8 KiB
Dart
174 lines
5.8 KiB
Dart
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<List<Offset>> _bolts = [];
|
|
|
|
@override
|
|
Future<void> 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 = <Offset>[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);
|
|
}
|
|
}
|
|
}
|