Add playable core UI: board painter, drag-and-drop, HUD, result overlay
CustomPainter board with gems/ghost/clear-flash, finger-lifted drag with snap preview, combo text effect, HUD chips, phase overlays with rescue stubs, demo stage. E2E widget test drives a real drag gesture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui';
|
||||
|
||||
import '../../game/models/grid.dart';
|
||||
import '../../game/models/piece.dart';
|
||||
|
||||
/// Pure mapping between board-local pixels and grid cells.
|
||||
class BoardGeometry {
|
||||
const BoardGeometry({required this.boardSize});
|
||||
|
||||
final double boardSize;
|
||||
|
||||
double get cellSize => boardSize / GridState.size;
|
||||
|
||||
Rect cellRect(int x, int y) =>
|
||||
Rect.fromLTWH(x * cellSize, y * cellSize, cellSize, cellSize);
|
||||
|
||||
/// Cell under a local point, or null outside the board.
|
||||
(int, int)? cellAt(Offset local) {
|
||||
if (local.dx < 0 || local.dy < 0) return null;
|
||||
if (local.dx >= boardSize || local.dy >= boardSize) return null;
|
||||
return (local.dx ~/ cellSize, local.dy ~/ cellSize);
|
||||
}
|
||||
|
||||
/// Nearest anchor for a piece whose visual top-left sits at [pieceTopLeft],
|
||||
/// clamped so the piece's bounding box stays on the board.
|
||||
(int, int) snapAnchor(Piece piece, Offset pieceTopLeft) {
|
||||
var maxDx = 0;
|
||||
var maxDy = 0;
|
||||
for (final (dx, dy) in piece.offsets) {
|
||||
maxDx = math.max(maxDx, dx);
|
||||
maxDy = math.max(maxDy, dy);
|
||||
}
|
||||
final x = (pieceTopLeft.dx / cellSize).round();
|
||||
final y = (pieceTopLeft.dy / cellSize).round();
|
||||
return (
|
||||
x.clamp(0, GridState.size - 1 - maxDx),
|
||||
y.clamp(0, GridState.size - 1 - maxDy),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user