mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 19:12:31 +08:00
This adds `DisplacementEvent` to fix delta coordinate transformations
for drag events, to be used instead of `PositionEvent`.
Drag Events now expose the start and end position, as well as the delta,
correctly transformed by the camera and zoom.
This also ensures that drag events, once starts, do not get lost if the
drag update leaves the component bounds.
* if you are using `DragUpdateEvent` events, the `devicePosition`,
`canvasPosition`, `localPosition`, and `delta` are deprecated as they
are unclear.
* use `xStartPosition` to get the position at the start of the drag
event ("from")
* use `xEndPosition` to get the position at the end of the drag event
("to")
* if you want the delta, use `localDelta`. it now already considers the
camera zoom. no need to manually account for that
* now you keep receiving drag events for the same component even if the
mouse leaves the component (breaking)
---------
Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com>
280 lines
9.1 KiB
Dart
280 lines
9.1 KiB
Dart
import 'dart:math';
|
|
import 'dart:ui';
|
|
|
|
import 'package:flame/components.dart';
|
|
import 'package:flame/events.dart';
|
|
import '../klondike_game.dart';
|
|
import '../pile.dart';
|
|
import '../rank.dart';
|
|
import '../suit.dart';
|
|
import 'tableau_pile.dart';
|
|
|
|
class Card extends PositionComponent with DragCallbacks {
|
|
Card(int intRank, int intSuit)
|
|
: rank = Rank.fromInt(intRank),
|
|
suit = Suit.fromInt(intSuit),
|
|
super(size: KlondikeGame.cardSize);
|
|
|
|
final Rank rank;
|
|
final Suit suit;
|
|
Pile? pile;
|
|
bool _faceUp = false;
|
|
bool _isDragging = false;
|
|
final List<Card> attachedCards = [];
|
|
|
|
bool get isFaceUp => _faceUp;
|
|
bool get isFaceDown => !_faceUp;
|
|
void flip() => _faceUp = !_faceUp;
|
|
|
|
@override
|
|
String toString() => rank.label + suit.label; // e.g. "Q♠" or "10♦"
|
|
|
|
//#region Rendering
|
|
|
|
@override
|
|
void render(Canvas canvas) {
|
|
if (_faceUp) {
|
|
_renderFront(canvas);
|
|
} else {
|
|
_renderBack(canvas);
|
|
}
|
|
}
|
|
|
|
static final Paint backBackgroundPaint = Paint()
|
|
..color = const Color(0xff380c02);
|
|
static final Paint backBorderPaint1 = Paint()
|
|
..color = const Color(0xffdbaf58)
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 10;
|
|
static final Paint backBorderPaint2 = Paint()
|
|
..color = const Color(0x5CEF971B)
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 35;
|
|
static final RRect cardRRect = RRect.fromRectAndRadius(
|
|
KlondikeGame.cardSize.toRect(),
|
|
const Radius.circular(KlondikeGame.cardRadius),
|
|
);
|
|
static final RRect backRRectInner = cardRRect.deflate(40);
|
|
static final Sprite flameSprite = klondikeSprite(1367, 6, 357, 501);
|
|
|
|
void _renderBack(Canvas canvas) {
|
|
canvas.drawRRect(cardRRect, backBackgroundPaint);
|
|
canvas.drawRRect(cardRRect, backBorderPaint1);
|
|
canvas.drawRRect(backRRectInner, backBorderPaint2);
|
|
flameSprite.render(canvas, position: size / 2, anchor: Anchor.center);
|
|
}
|
|
|
|
static final Paint frontBackgroundPaint = Paint()
|
|
..color = const Color(0xff000000);
|
|
static final Paint redBorderPaint = Paint()
|
|
..color = const Color(0xffece8a3)
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 10;
|
|
static final Paint blackBorderPaint = Paint()
|
|
..color = const Color(0xff7ab2e8)
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 10;
|
|
static final blueFilter = Paint()
|
|
..colorFilter = const ColorFilter.mode(
|
|
Color(0x880d8bff),
|
|
BlendMode.srcATop,
|
|
);
|
|
static final Sprite redJack = klondikeSprite(81, 565, 562, 488);
|
|
static final Sprite redQueen = klondikeSprite(717, 541, 486, 515);
|
|
static final Sprite redKing = klondikeSprite(1305, 532, 407, 549);
|
|
static final Sprite blackJack = klondikeSprite(81, 565, 562, 488)
|
|
..paint = blueFilter;
|
|
static final Sprite blackQueen = klondikeSprite(717, 541, 486, 515)
|
|
..paint = blueFilter;
|
|
static final Sprite blackKing = klondikeSprite(1305, 532, 407, 549)
|
|
..paint = blueFilter;
|
|
|
|
void _renderFront(Canvas canvas) {
|
|
canvas.drawRRect(cardRRect, frontBackgroundPaint);
|
|
canvas.drawRRect(
|
|
cardRRect,
|
|
suit.isRed ? redBorderPaint : blackBorderPaint,
|
|
);
|
|
|
|
final rankSprite = suit.isBlack ? rank.blackSprite : rank.redSprite;
|
|
final suitSprite = suit.sprite;
|
|
_drawSprite(canvas, rankSprite, 0.1, 0.08);
|
|
_drawSprite(canvas, suitSprite, 0.1, 0.18, scale: 0.5);
|
|
_drawSprite(canvas, rankSprite, 0.1, 0.08, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.1, 0.18, scale: 0.5, rotate: true);
|
|
switch (rank.value) {
|
|
case 1:
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.5, scale: 2.5);
|
|
break;
|
|
case 2:
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.25);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.25, rotate: true);
|
|
break;
|
|
case 3:
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.5);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.2, rotate: true);
|
|
break;
|
|
case 4:
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.25);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.25);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.25, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.25, rotate: true);
|
|
break;
|
|
case 5:
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.25);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.25);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.25, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.25, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.5);
|
|
break;
|
|
case 6:
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.25);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.25);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.5);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.5);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.25, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.25, rotate: true);
|
|
break;
|
|
case 7:
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.35);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.5);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.5);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.2, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.2, rotate: true);
|
|
break;
|
|
case 8:
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.35);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.5);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.5);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.2, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.2, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.35, rotate: true);
|
|
break;
|
|
case 9:
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.3);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.4);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.4);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.2, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.2, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.4, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.4, rotate: true);
|
|
break;
|
|
case 10:
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.2);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.3);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.4);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.4);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.2, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.2, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.5, 0.3, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.3, 0.4, rotate: true);
|
|
_drawSprite(canvas, suitSprite, 0.7, 0.4, rotate: true);
|
|
break;
|
|
case 11:
|
|
_drawSprite(canvas, suit.isRed ? redJack : blackJack, 0.5, 0.5);
|
|
break;
|
|
case 12:
|
|
_drawSprite(canvas, suit.isRed ? redQueen : blackQueen, 0.5, 0.5);
|
|
break;
|
|
case 13:
|
|
_drawSprite(canvas, suit.isRed ? redKing : blackKing, 0.5, 0.5);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void _drawSprite(
|
|
Canvas canvas,
|
|
Sprite sprite,
|
|
double relativeX,
|
|
double relativeY, {
|
|
double scale = 1,
|
|
bool rotate = false,
|
|
}) {
|
|
if (rotate) {
|
|
canvas.save();
|
|
canvas.translate(size.x / 2, size.y / 2);
|
|
canvas.rotate(pi);
|
|
canvas.translate(-size.x / 2, -size.y / 2);
|
|
}
|
|
sprite.render(
|
|
canvas,
|
|
position: Vector2(relativeX * size.x, relativeY * size.y),
|
|
anchor: Anchor.center,
|
|
size: sprite.srcSize.scaled(scale),
|
|
);
|
|
if (rotate) {
|
|
canvas.restore();
|
|
}
|
|
}
|
|
|
|
//#endregion
|
|
|
|
//#region Dragging
|
|
|
|
@override
|
|
void onDragStart(DragStartEvent event) {
|
|
super.onDragStart(event);
|
|
if (pile?.canMoveCard(this) ?? false) {
|
|
_isDragging = true;
|
|
priority = 100;
|
|
if (pile is TableauPile) {
|
|
attachedCards.clear();
|
|
final extraCards = (pile! as TableauPile).cardsOnTop(this);
|
|
for (final card in extraCards) {
|
|
card.priority = attachedCards.length + 101;
|
|
attachedCards.add(card);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onDragUpdate(DragUpdateEvent event) {
|
|
if (!_isDragging) {
|
|
return;
|
|
}
|
|
final delta = event.localDelta;
|
|
position.add(delta);
|
|
attachedCards.forEach((card) => card.position.add(delta));
|
|
}
|
|
|
|
@override
|
|
void onDragEnd(DragEndEvent event) {
|
|
super.onDragEnd(event);
|
|
if (!_isDragging) {
|
|
return;
|
|
}
|
|
_isDragging = false;
|
|
final dropPiles = parent!
|
|
.componentsAtPoint(position + size / 2)
|
|
.whereType<Pile>()
|
|
.toList();
|
|
if (dropPiles.isNotEmpty) {
|
|
if (dropPiles.first.canAcceptCard(this)) {
|
|
pile!.removeCard(this);
|
|
dropPiles.first.acquireCard(this);
|
|
if (attachedCards.isNotEmpty) {
|
|
attachedCards.forEach((card) => dropPiles.first.acquireCard(card));
|
|
attachedCards.clear();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
pile!.returnCard(this);
|
|
if (attachedCards.isNotEmpty) {
|
|
attachedCards.forEach((card) => pile!.returnCard(card));
|
|
attachedCards.clear();
|
|
}
|
|
}
|
|
|
|
//#endregion
|
|
}
|