Skip to content

Commit

Permalink
Add basic hand deck and figure support
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDoctorDE committed Jul 24, 2024
1 parent 85307a7 commit 1663f9d
Show file tree
Hide file tree
Showing 23 changed files with 869 additions and 535 deletions.
2 changes: 1 addition & 1 deletion app/lib/game/board/background.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:quokka/game/board/game.dart';
class GameBoardBackground extends SpriteComponent
with HasGameReference<BoardGame> {
GameBoardBackground({super.size})
: super(paint: Paint()..isAntiAlias = true);
: super(paint: Paint()..isAntiAlias = false);
@override
void onLoad() {
sprite = game.gridSprite;
Expand Down
3 changes: 1 addition & 2 deletions app/lib/game/board/cell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class GameCell extends PositionComponent
Future<void> onLoad() async {
add(GameBoardBackground(size: size));
_selectionComponent = SpriteComponent(
sprite: await Sprite.load('selection.png'),
sprite: game.selectionSprite,
size: size,
);
add(_selectionComponent..opacity = 0);
Expand Down Expand Up @@ -72,7 +72,6 @@ class GameCell extends PositionComponent
@override
void update(double dt) {
if (_selected == (game.selectedCell == position)) return;

_selected = !_selected;
final controller = EffectController(
duration: 0.1,
Expand Down
4 changes: 3 additions & 1 deletion app/lib/game/board/game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class BoardGame extends FlameGame with ScrollDetector, ScaleDetector {
final GameTable table;
final Map<String, PackData> _loadedPacks = {};
Vector2? selectedCell;
late final Sprite gridSprite;
late final Sprite gridSprite, selectionSprite;
late final GameHand _hand;

BoardGame({
Expand All @@ -36,6 +36,7 @@ class BoardGame extends FlameGame with ScrollDetector, ScaleDetector {
}
final image = await decodeImageFromList(data);
gridSprite = Sprite(image);
selectionSprite = await Sprite.load('selection.png');
_hand = GameHand();
camera.viewport.add(_hand);
world.add(BoardGrid(cellSize: Vector2.all(256), createCell: GameCell.new));
Expand Down Expand Up @@ -91,5 +92,6 @@ class BoardGame extends FlameGame with ScrollDetector, ScaleDetector {

void showAdd() {
selectedCell = null;
_hand.show();
}
}
17 changes: 16 additions & 1 deletion app/lib/game/board/hand/deck.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import 'package:flame/components.dart';
import 'package:quokka/game/board/game.dart';
import 'package:quokka/game/board/hand/item.dart';
import 'package:quokka/game/board/hand/view.dart';
import 'package:quokka/models/definitions/deck.dart';
import 'package:quokka/models/definitions/pack.dart';

class DeckDefinitionHandItem extends HandItem<PackItem<DeckDefinition>> {
class DeckDefinitionHandItem extends HandItem<PackItem<DeckDefinition>>
with HasGameRef<BoardGame> {
DeckDefinitionHandItem({required super.item});

@override
String get label => item.id;

@override
void onTapUp(event) {
final hand = parent;
if (hand is GameHand) {
hand.selectDeck(item);
}
}
}
6 changes: 5 additions & 1 deletion app/lib/game/board/hand/figure.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import 'package:quokka/game/board/hand/item.dart';
import 'package:quokka/models/definitions/object.dart';
import 'package:quokka/models/definitions/pack.dart';

class FigureDefinitionHandItem extends HandItem<PackItem<FigureDefinition>> {
class FigureDefinitionHandItem
extends HandItem<(PackItem<FigureDefinition>, String?)> {
FigureDefinitionHandItem({required super.item});

@override
String get label => item.$2 ?? item.$1.id;
}
17 changes: 15 additions & 2 deletions app/lib/game/board/hand/item.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import 'package:flame/components.dart';
import 'package:flame/events.dart';

abstract class HandItem<T> extends PositionComponent {
abstract class HandItem<T> extends PositionComponent with TapCallbacks {
final T item;

HandItem({required this.item});
HandItem({required this.item}) : super(size: Vector2(100, 0));

String get label;

@override
void onLoad() {
add(TextComponent(text: label));
}

@override
void onParentResize(Vector2 maxSize) {
height = maxSize.y;
}
}
3 changes: 3 additions & 0 deletions app/lib/game/board/hand/object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ import 'package:quokka/models/table.dart';

class GameObjectHandItem extends HandItem<MapEntry<GridLocation, GameObject>> {
GameObjectHandItem({required super.item});

@override
String get label => item.value.asset.id;
}
55 changes: 44 additions & 11 deletions app/lib/game/board/hand/view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:math';

import 'package:collection/collection.dart';
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:quokka/game/board/game.dart';
import 'package:quokka/game/board/hand/deck.dart';
import 'package:quokka/game/board/hand/figure.dart';
Expand All @@ -12,15 +13,32 @@ import 'package:quokka/models/definitions/object.dart';
import 'package:quokka/models/definitions/pack.dart';
import 'package:quokka/models/table.dart';

class GameHand extends PositionComponent with HasGameRef<BoardGame> {
class GameHandCustomPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.fill
..strokeWidth = 2;
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
}

@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}

class GameHand extends CustomPainterComponent with HasGameRef<BoardGame> {
Vector2? _lastPosition;
double _delta = 0, _nextItemPos = 0;
bool _showing = false, _lastShowing = false;
PackItem<DeckDefinition>? _selectedDeck;

GameHand()
: super(
anchor: Anchor.bottomCenter,
anchor: Anchor.topLeft,
painter: GameHandCustomPainter(),
);

@override
Expand All @@ -39,17 +57,20 @@ class GameHand extends PositionComponent with HasGameRef<BoardGame> {
return;
}
_lastPosition = game.selectedCell;
removeAll(children);
_buildHand();
_repositionChildren();
}

@override
void onParentResize(Vector2 maxSize) {
_updatePosition();
}

void _updatePosition() {
final game = gameRef;
final size = game.canvasSize;
width = size.x;
height = min(size.y / 3, 256);
position = Vector2(0, -height * (_showing ? 1 : 0.5));
height = min(size.y / 3, 256) * (_showing ? 1 : 0.5);
position = Vector2(0, size.y - height);
}

void _repositionChildren() {
Expand All @@ -64,6 +85,7 @@ class GameHand extends PositionComponent with HasGameRef<BoardGame> {
}

void _buildHand() {
removeAll(children.whereType<HandItem>());
if (_lastPosition == null) {
if (_selectedDeck != null) {
_buildDeckHand(_selectedDeck!);
Expand All @@ -73,42 +95,48 @@ class GameHand extends PositionComponent with HasGameRef<BoardGame> {
} else {
_buildCellHand(_lastPosition!);
}
_repositionChildren();
}

void _buildFreeHand() {
final game = gameRef;
final decks = game.packs.expand((e) => e.value.getDeckItems());
_nextItemPos = 0;
for (final deck in decks) {
_addChild(DeckDefinitionHandItem(item: deck));
}
}

void _addFigures(Iterable<PackItem<FigureDefinition>> figures) {
void _addFigures(Iterable<(PackItem<FigureDefinition>, String?)> figures) {
_nextItemPos = 0;
for (final figure in figures) {
_addChild(FigureDefinitionHandItem(item: figure));
}
}

void _buildDeckHand(PackItem<DeckDefinition> deck) {
final deckFigures = deck.item.figures;
final figures = deckFigures
.map((e) => deck.pack.getFigureItem(e.name, deck.namespace))
.whereNotNull();
final figures = deckFigures.map((e) {
final figure = deck.pack.getFigureItem(e.name, deck.namespace);
if (figure == null) return null;
return (figure, e.variation);
}).whereNotNull();
_addFigures(figures);
}

void _buildCellHand(Vector2 cell) {
final location = GridLocation(cell.x.toInt(), cell.y.toInt());
final tableCell = game.table.cells[location];
_nextItemPos = 0;
for (final object in tableCell?.objects ?? <GameObject>[]) {
_addChild(GameObjectHandItem(item: MapEntry(location, object)));
}
}

void _addChild(HandItem item) {
add(item);
item.position = Vector2(_nextItemPos, 0);
_nextItemPos += item.size.x;
add(item);
}

void show() {
Expand All @@ -120,4 +148,9 @@ class GameHand extends PositionComponent with HasGameRef<BoardGame> {
void hide() {
_showing = false;
}

void selectDeck(PackItem<DeckDefinition> item) {
_selectedDeck = item;
_buildHand();
}
}
2 changes: 2 additions & 0 deletions app/lib/models/definitions/deck.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ class DeckDefinition with DeckDefinitionMappable {
@MappableClass()
class FigureDeckDefinition with FigureDeckDefinitionMappable {
final String name;
final String? variation;
final VectorDefinition location;

FigureDeckDefinition({
required this.name,
this.variation,
this.location = VectorDefinition.zero,
});
}
17 changes: 14 additions & 3 deletions app/lib/models/definitions/deck.mapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -164,19 +164,25 @@ class FigureDeckDefinitionMapper extends ClassMapperBase<FigureDeckDefinition> {
static String _$name(FigureDeckDefinition v) => v.name;
static const Field<FigureDeckDefinition, String> _f$name =
Field('name', _$name);
static String? _$variation(FigureDeckDefinition v) => v.variation;
static const Field<FigureDeckDefinition, String> _f$variation =
Field('variation', _$variation, opt: true);
static VectorDefinition _$location(FigureDeckDefinition v) => v.location;
static const Field<FigureDeckDefinition, VectorDefinition> _f$location =
Field('location', _$location, opt: true, def: VectorDefinition.zero);

@override
final MappableFields<FigureDeckDefinition> fields = const {
#name: _f$name,
#variation: _f$variation,
#location: _f$location,
};

static FigureDeckDefinition _instantiate(DecodingData data) {
return FigureDeckDefinition(
name: data.dec(_f$name), location: data.dec(_f$location));
name: data.dec(_f$name),
variation: data.dec(_f$variation),
location: data.dec(_f$location));
}

@override
Expand Down Expand Up @@ -237,7 +243,7 @@ abstract class FigureDeckDefinitionCopyWith<
$In extends FigureDeckDefinition,
$Out> implements ClassCopyWith<$R, $In, $Out> {
VectorDefinitionCopyWith<$R, VectorDefinition, VectorDefinition> get location;
$R call({String? name, VectorDefinition? location});
$R call({String? name, String? variation, VectorDefinition? location});
FigureDeckDefinitionCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(
Then<$Out2, $R2> t);
}
Expand All @@ -254,14 +260,19 @@ class _FigureDeckDefinitionCopyWithImpl<$R, $Out>
VectorDefinitionCopyWith<$R, VectorDefinition, VectorDefinition>
get location => $value.location.copyWith.$chain((v) => call(location: v));
@override
$R call({String? name, VectorDefinition? location}) =>
$R call(
{String? name,
Object? variation = $none,
VectorDefinition? location}) =>
$apply(FieldCopyWithData({
if (name != null) #name: name,
if (variation != $none) #variation: variation,
if (location != null) #location: location
}));
@override
FigureDeckDefinition $make(CopyWithData data) => FigureDeckDefinition(
name: data.get(#name, or: $value.name),
variation: data.get(#variation, or: $value.variation),
location: data.get(#location, or: $value.location));

@override
Expand Down
2 changes: 1 addition & 1 deletion app/lib/models/definitions/pack.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class PackData {
return e.substring(0, startExtension);
});

Iterable<String> getDecks() => getAssets(kPackDecksPath);
Iterable<String> getDecks() => getAssets(kPackDecksPath, true);

DeckDefinition? getDeck(String id) {
final data = getAsset('$kPackDecksPath/$id.json');
Expand Down
1 change: 1 addition & 0 deletions app/lib/pages/game/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class GamePage extends StatelessWidget {
return Scaffold(
appBar: WindowTitleBar<SettingsCubit, QuokkaSettings>(
title: Text(AppLocalizations.of(context).game),
height: 50,
actions: [
IconButton(
icon: const PhosphorIcon(PhosphorIconsLight.plusCircle),
Expand Down
Loading

0 comments on commit 1663f9d

Please sign in to comment.