Merge "croupier: Add Player Split View"
diff --git a/images/avatars/Club.png b/images/avatars/Club.png
index d09abc2..d91ded6 100644
--- a/images/avatars/Club.png
+++ b/images/avatars/Club.png
Binary files differ
diff --git a/images/avatars/Diamond.png b/images/avatars/Diamond.png
index 83733ff..4c541c6 100644
--- a/images/avatars/Diamond.png
+++ b/images/avatars/Diamond.png
Binary files differ
diff --git a/images/avatars/Heart.png b/images/avatars/Heart.png
index 2212ebb..e98084a 100644
--- a/images/avatars/Heart.png
+++ b/images/avatars/Heart.png
Binary files differ
diff --git a/images/avatars/Spade.png b/images/avatars/Spade.png
index 13358da..2956888 100644
--- a/images/avatars/Spade.png
+++ b/images/avatars/Spade.png
Binary files differ
diff --git a/images/avatars/player1.jpeg b/images/avatars/player1.jpeg
index 34d57b0..e1c08d8 100644
--- a/images/avatars/player1.jpeg
+++ b/images/avatars/player1.jpeg
Binary files differ
diff --git a/images/avatars/player3.jpeg b/images/avatars/player3.jpeg
index 4d0ce50..bff779a 100644
--- a/images/avatars/player3.jpeg
+++ b/images/avatars/player3.jpeg
Binary files differ
diff --git a/lib/components/board.dart b/lib/components/board.dart
index 1ee925c..6491981 100644
--- a/lib/components/board.dart
+++ b/lib/components/board.dart
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-import 'dart:async';
-
import 'package:flutter/material.dart';
import '../logic/card.dart' as logic_card;
@@ -11,9 +9,10 @@
import '../logic/croupier_settings.dart' show CroupierSettings;
import '../logic/game/game.dart' show Game, GameType, NoArgCb;
import '../logic/hearts/hearts.dart' show HeartsGame;
+import '../styles/common.dart' as style;
import 'card.dart' as component_card;
import 'card_collection.dart'
- show CardCollectionComponent, CardCollectionOrientation;
+ show CardCollectionComponent, CardCollectionOrientation, DropType, AcceptCb;
import 'croupier_profile.dart' show CroupierProfileComponent;
const double defaultBoardHeight = 400.0;
@@ -49,10 +48,20 @@
/// cards each player has, and the cards they are currently playing.
class HeartsBoard extends Board {
final Croupier croupier;
- final NoArgCb trueSetState;
+ final bool isMini;
+ final AcceptCb gameAcceptCallback;
+ final bool trickTaking;
+ final List<List<logic_card.Card>> playedCards;
- HeartsBoard(Croupier croupier, this.trueSetState,
- {double height, double width, double cardHeight, double cardWidth})
+ HeartsBoard(Croupier croupier,
+ {double height,
+ double width,
+ double cardHeight,
+ double cardWidth,
+ this.isMini: false,
+ this.gameAcceptCallback,
+ this.trickTaking,
+ this.playedCards})
: super(croupier.game,
height: height,
width: width,
@@ -66,97 +75,122 @@
}
class HeartsBoardState extends State<HeartsBoard> {
- bool trickTaking = false;
- List<List<logic_card.Card>> playedCards = new List<List<logic_card.Card>>(4);
-
- static const int SHOW_TRICK_DURATION = 2000; // ms
-
- @override
- void initState() {
- super.initState();
-
- _fillPlayedCards();
- }
-
- // Make copies of the played cards.
- void _fillPlayedCards() {
- for (int i = 0; i < 4; i++) {
- playedCards[i] = new List<logic_card.Card>.from(
- config.game.cardCollections[i + HeartsGame.OFFSET_PLAY]);
- }
- }
-
- // If there were 3 played cards before and now there are 0...
- bool _detectTrick() {
- HeartsGame game = config.game;
- int lastNumPlayed = playedCards.where((List<logic_card.Card> list) {
- return list.length > 0;
- }).length;
- return lastNumPlayed == 3 && game.numPlayed == 0;
- }
-
- // Make a copy of the missing played card.
- void _fillMissingPlayedCard() {
- HeartsGame game = config.game;
- List<logic_card.Card> trickPile =
- game.cardCollections[game.lastTrickTaker + HeartsGame.OFFSET_TRICK];
-
- // Find the index of the missing play card.
- int missing;
- for (int j = 0; j < 4; j++) {
- if (playedCards[j].length == 0) {
- missing = j;
- break;
- }
- }
-
- // Use the trickPile to get this card.
- playedCards[missing] = <logic_card.Card>[
- trickPile[trickPile.length - 4 + missing]
- ];
- }
-
Widget build(BuildContext context) {
- if (!trickTaking) {
- if (_detectTrick()) {
- trickTaking = true;
- _fillMissingPlayedCard();
- // Unfortunately, ZCards are drawn on the game layer,
- // so instead of setState, we must use trueSetState.
- new Future.delayed(const Duration(milliseconds: SHOW_TRICK_DURATION),
- () {
- trickTaking = false;
- config.trueSetState();
- });
- } else {
- _fillPlayedCards();
- }
- }
-
return new Container(
height: config.height,
width: config.width,
child: new Stack([
- new Positioned(top: 0.0, left: 0.0, child: _buildBoardLayout()),
new Positioned(
- top: config.height * 1.5,
+ top: 0.0,
+ left: 0.0,
+ child: config.isMini
+ ? _buildMiniBoardLayout()
+ : _buildBoardLayout()),
+ new Positioned(
+ top: config.height * 5.5,
left: (config.width - config.cardWidth) / 2,
- child: _buildTrick(0)), // bottom
+ child: _buildTrick(
+ config.isMini ? rotateByGamePlayerNumber(0) : 0)), // bottom
new Positioned(
top: (config.height - config.cardHeight) / 2,
- left: config.width * -0.5,
- child: _buildTrick(1)), // left
+ left: config.width * -4.5,
+ child: _buildTrick(
+ config.isMini ? rotateByGamePlayerNumber(1) : 1)), // left
new Positioned(
- top: config.height * -0.5,
+ top: config.height * -4.5,
left: (config.width - config.cardWidth) / 2,
- child: _buildTrick(2)), // top
+ child: _buildTrick(
+ config.isMini ? rotateByGamePlayerNumber(2) : 2)), // top
new Positioned(
top: (config.height - config.cardHeight) / 2,
- left: config.width * 1.5,
- child: _buildTrick(3)) // right
+ left: config.width * 5.5,
+ child: _buildTrick(
+ config.isMini ? rotateByGamePlayerNumber(3) : 3)) // right
]));
}
+ int rotateByGamePlayerNumber(int i) {
+ return (i + config.game.playerNumber) % 4;
+ }
+
+ Widget _buildMiniBoardLayout() {
+ return new Container(
+ height: config.height,
+ width: config.width,
+ child: new Center(
+ child: new Row([
+ new Flexible(
+ flex: 1,
+ child: new Center(
+ child: _buildAvatarSlotCombo(rotateByGamePlayerNumber(1)))),
+ new Flexible(
+ flex: 1,
+ child: new Column([
+ new Flexible(
+ flex: 1,
+ child: _buildAvatarSlotCombo(rotateByGamePlayerNumber(2))),
+ new Flexible(
+ flex: 1,
+ child: _buildAvatarSlotCombo(rotateByGamePlayerNumber(0)))
+ ])),
+ new Flexible(
+ flex: 1,
+ child: new Center(
+ child: _buildAvatarSlotCombo(rotateByGamePlayerNumber(3))))
+ ])));
+ }
+
+ Widget _buildAvatarSlotCombo(int playerNumber) {
+ HeartsGame game = config.game as HeartsGame;
+ int p = game.playerNumber;
+
+ List<Widget> items = new List<Widget>();
+ bool isMe = playerNumber == p;
+ bool isPlayerTurn = playerNumber == game.whoseTurn && !config.trickTaking;
+
+ List<logic_card.Card> showCard =
+ game.cardCollections[playerNumber + HeartsGame.OFFSET_PLAY];
+
+ if (config.trickTaking) {
+ showCard = config.playedCards[playerNumber];
+ }
+
+ items.add(new Positioned(
+ top: 0.0,
+ left: 0.0,
+ child: new CardCollectionComponent(
+ showCard, true, CardCollectionOrientation.show1,
+ useKeys: true,
+ animationType: component_card.CardAnimationType.NONE,
+ acceptCallback: config.gameAcceptCallback,
+ acceptType: isMe && isPlayerTurn ? DropType.card : DropType.none,
+ widthCard: config.cardWidth - 6.0,
+ heightCard: config.cardHeight - 6.0,
+ backgroundColor:
+ isPlayerTurn ? style.theme.accentColor : Colors.grey[500],
+ altColor: isPlayerTurn ? Colors.grey[200] : Colors.grey[600])));
+
+ bool hasPlayed =
+ game.cardCollections[playerNumber + HeartsGame.OFFSET_PLAY].length > 0;
+ if (!hasPlayed) {
+ items.add(new Positioned(
+ top: 0.0,
+ left: 0.0,
+ child: new IgnorePointer(
+ child: new CroupierProfileComponent(
+ settings:
+ config.croupier.settingsFromPlayerNumber(playerNumber),
+ height: config.cardHeight,
+ width: config.cardWidth,
+ isMini: true))));
+ }
+
+ return new Container(
+ width: config.cardWidth,
+ height: config.cardHeight,
+ child: new Stack(items));
+ }
+
Widget _buildBoardLayout() {
return new Container(
height: config.height,
@@ -269,32 +303,35 @@
HeartsGame game = config.game;
List<logic_card.Card> cards =
game.cardCollections[playerNumber + HeartsGame.OFFSET_PLAY];
- if (trickTaking) {
- cards = playedCards[playerNumber];
+ if (config.trickTaking) {
+ cards = config.playedCards[playerNumber];
}
- return new CardCollectionComponent(
- cards, true, CardCollectionOrientation.show1,
- widthCard: config.cardWidth * 1.25,
- heightCard: config.cardHeight * 1.25,
- backgroundColor:
- game.whoseTurn == playerNumber ? Colors.blue[500] : null,
- useKeys: true);
+ return new Container(
+ decoration: game.whoseTurn == playerNumber ? style.Box.liveNow : null,
+ child: new CardCollectionComponent(
+ cards, true, CardCollectionOrientation.show1,
+ widthCard: config.cardWidth * 2,
+ heightCard: config.cardHeight * 2,
+ useKeys: true));
}
Widget _buildTrick(int playerNumber) {
HeartsGame game = config.game;
+
List<logic_card.Card> cards =
game.cardCollections[playerNumber + HeartsGame.OFFSET_TRICK];
// If took trick, exclude the last 4 cards for the trick taking animation.
- if (trickTaking && playerNumber == game.lastTrickTaker) {
+ if (config.trickTaking && playerNumber == game.lastTrickTaker) {
cards = new List.from(cards.sublist(0, cards.length - 4));
}
+ double sizeFactor = config.isMini ? 1.0 : 2.0;
+
return new CardCollectionComponent(
cards, true, CardCollectionOrientation.show1,
- widthCard: config.cardWidth,
- heightCard: config.cardHeight,
+ widthCard: config.cardWidth * sizeFactor,
+ heightCard: config.cardHeight * sizeFactor,
useKeys: true,
animationType: component_card.CardAnimationType.LONG);
}
diff --git a/lib/components/card.dart b/lib/components/card.dart
index c222020..68f683a 100644
--- a/lib/components/card.dart
+++ b/lib/components/card.dart
@@ -182,7 +182,7 @@
case CardAnimationType.NORMAL:
return const Duration(milliseconds: 250);
case CardAnimationType.LONG:
- return const Duration(milliseconds: 750);
+ return const Duration(milliseconds: 1500);
default:
print("Unexpected animation type: ${config.animationType}");
assert(false);
@@ -216,6 +216,7 @@
..value = endingLocation
..end = endingLocation;
_performance.progress = 0.0;
+ _animationIndex = _pointQueue.length - 1;
return;
}
diff --git a/lib/components/croupier.dart b/lib/components/croupier.dart
index 8c151fb..71c59e1 100644
--- a/lib/components/croupier.dart
+++ b/lib/components/croupier.dart
@@ -17,6 +17,8 @@
typedef void NoArgCb();
+GlobalObjectKey _gameKey = new GlobalObjectKey("CroupierGameKey");
+
class CroupierComponent extends StatefulComponent {
final logic_croupier.Croupier croupier;
@@ -117,7 +119,8 @@
makeSetStateCallback(logic_croupier.CroupierState.Welcome)();
},
width: ui.window.size.width,
- height: ui.window.size.height - ui.window.padding.top));
+ height: ui.window.size.height - ui.window.padding.top,
+ key: _gameKey));
default:
assert(false);
return null;
diff --git a/lib/components/croupier_profile.dart b/lib/components/croupier_profile.dart
index 3e2a369..ffd0869 100644
--- a/lib/components/croupier_profile.dart
+++ b/lib/components/croupier_profile.dart
@@ -5,7 +5,6 @@
import 'package:flutter/material.dart';
import '../logic/croupier_settings.dart' show CroupierSettings;
-import '../styles/common.dart' as style;
class CroupierProfileComponent extends StatelessComponent {
final CroupierSettings settings;
@@ -21,32 +20,29 @@
Widget build(BuildContext context) {
if (!isMini) {
- return new Card(
- color: new Color(settings.color),
- child: new Container(
- height: this.height,
- width: this.width,
- padding: const EdgeDims.all(padAmount),
+ return new Container(
+ height: this.height,
+ width: this.width,
+ padding: const EdgeDims.all(padAmount),
+ child: new Card(
+ color: new Color(settings.color),
child: new Column([
new AssetImage(
name: CroupierSettings.makeAvatarUrl(settings.avatar)),
- new Text(settings.name, style: style.Text.liveNow)
+ new Text(settings.name)
], justifyContent: FlexJustifyContent.spaceAround)));
} else {
- return new Card(
- color: new Color(settings.color),
- child: new Container(
- width: this.width,
- height: this.height,
- padding: const EdgeDims.all(padAmount),
+ return new Container(
+ width: this.width,
+ height: this.height,
+ padding: const EdgeDims.all(padAmount),
+ child: new Card(
+ color: new Color(settings.color),
child: new Row([
new AssetImage(
name: CroupierSettings.makeAvatarUrl(settings.avatar),
- width: this.width != null ? this.width - padAmount : null,
- height:
- this.height != null ? this.height - padAmount : null,
fit: ImageFit.scaleDown)
- ], justifyContent: FlexJustifyContent.collapse)));
+ ], justifyContent: FlexJustifyContent.spaceAround)));
}
}
}
diff --git a/lib/components/game.dart b/lib/components/game.dart
index 6049c6c..b9f844e 100644
--- a/lib/components/game.dart
+++ b/lib/components/game.dart
@@ -5,6 +5,7 @@
library game_component;
import 'dart:math' as math;
+import 'dart:async';
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
@@ -35,7 +36,9 @@
final double width;
final double height;
- GameComponent(this.croupier, this.gameEndCallback, {this.width, this.height});
+ GameComponent(this.croupier, this.gameEndCallback,
+ {Key key, this.width, this.height})
+ : super(key: key);
}
abstract class GameComponentState<T extends GameComponent> extends State<T> {
@@ -167,6 +170,7 @@
// Don't show a card if it isn't part of a visible collection.
if (!visibleCardCollectionIndexes.contains(config.game.findCard(c))) {
cardLevelMap.remove(c); // It is an old card, which we can clean up.
+ assert(!cardLevelMap.containsKey(c));
return;
}
@@ -196,17 +200,17 @@
}
GameComponent createGameComponent(Croupier croupier, NoArgCb gameEndCallback,
- {double width, double height}) {
+ {Key key, double width, double height}) {
switch (croupier.game.gameType) {
case GameType.Proto:
return new ProtoGameComponent(croupier, gameEndCallback,
- width: width, height: height);
+ key: key, width: width, height: height);
case GameType.Hearts:
return new HeartsGameComponent(croupier, gameEndCallback,
- width: width, height: height);
+ key: key, width: width, height: height);
case GameType.Solitaire:
return new SolitaireGameComponent(croupier, gameEndCallback,
- width: width, height: height);
+ key: key, width: width, height: height);
default:
// We're probably not ready to serve the other games yet.
assert(false);
diff --git a/lib/components/hearts/hearts.part.dart b/lib/components/hearts/hearts.part.dart
index 20ed05f..8cd0689 100644
--- a/lib/components/hearts/hearts.part.dart
+++ b/lib/components/hearts/hearts.part.dart
@@ -6,8 +6,8 @@
class HeartsGameComponent extends GameComponent {
HeartsGameComponent(Croupier croupier, NoArgCb cb,
- {double width, double height})
- : super(croupier, cb, width: width, height: height);
+ {Key key, double width, double height})
+ : super(croupier, cb, key: key, width: width, height: height);
HeartsGameComponentState createState() => new HeartsGameComponentState();
}
@@ -18,11 +18,62 @@
List<logic_card.Card> passingCards3 = new List<logic_card.Card>();
HeartsType _lastViewType;
+ bool _showSplitView = false;
+ bool trickTaking = false;
+ List<List<logic_card.Card>> playedCards = new List<List<logic_card.Card>>(4);
+
+ static const int SHOW_TRICK_DURATION = 2000; // ms
@override
void initState() {
super.initState();
+
+ // If someone sat at the table, they would have the value 4.
+ // If nobody sat at the table, then we should show the split view.
+ if (!config.croupier.players_found.values.contains(4)) {
+ _showSplitView = true;
+ }
_reset();
+
+ _fillPlayedCards();
+ }
+
+ // Make copies of the played cards.
+ void _fillPlayedCards() {
+ for (int i = 0; i < 4; i++) {
+ playedCards[i] = new List<logic_card.Card>.from(
+ config.game.cardCollections[i + HeartsGame.OFFSET_PLAY]);
+ }
+ }
+
+ // If there were 3 played cards before and now there are 0...
+ bool _detectTrick() {
+ HeartsGame game = config.game;
+ int lastNumPlayed = playedCards.where((List<logic_card.Card> list) {
+ return list.length > 0;
+ }).length;
+ return lastNumPlayed == 3 && game.numPlayed == 0;
+ }
+
+ // Make a copy of the missing played card.
+ void _fillMissingPlayedCard() {
+ HeartsGame game = config.game;
+ List<logic_card.Card> trickPile =
+ game.cardCollections[game.lastTrickTaker + HeartsGame.OFFSET_TRICK];
+
+ // Find the index of the missing play card.
+ int missing;
+ for (int j = 0; j < 4; j++) {
+ if (playedCards[j].length == 0) {
+ missing = j;
+ break;
+ }
+ }
+
+ // Use the trickPile to get this card.
+ playedCards[missing] = <logic_card.Card>[
+ trickPile[trickPile.length - 4 + missing]
+ ];
}
@override
@@ -72,10 +123,17 @@
.add(HeartsGame.OFFSET_HAND + playerNum);
break;
case HeartsPhase.Play:
- visibleCardCollectionIndexes
- .add(HeartsGame.OFFSET_HAND + playerNum);
- visibleCardCollectionIndexes
- .add(HeartsGame.OFFSET_PLAY + playerNum);
+ for (int i = 0; i < 4; i++) {
+ if (_showSplitView || i == playerNum) {
+ visibleCardCollectionIndexes.add(HeartsGame.OFFSET_PLAY + i);
+ }
+ if (_showSplitView) {
+ visibleCardCollectionIndexes
+ .add(HeartsGame.OFFSET_HAND + playerNum);
+ visibleCardCollectionIndexes.add(HeartsGame.OFFSET_TRICK + i);
+ }
+ }
+
break;
default:
break;
@@ -173,6 +231,7 @@
try {
HeartsGame game = config.game as HeartsGame;
game.passCards(_combinePassing());
+ game.debugString = null;
} catch (e) {
print("You can't do that! ${e.toString()}");
config.game.debugString = e.toString();
@@ -190,6 +249,7 @@
_clearPassing();
HeartsGame game = config.game as HeartsGame;
game.takeCards();
+ game.debugString = null;
} catch (e) {
print("You can't do that! ${e.toString()}");
config.game.debugString = e.toString();
@@ -203,6 +263,7 @@
String reason = game.canPlay(game.playerNumber, card);
if (reason == null) {
game.move(card, dest);
+ game.debugString = null;
} else {
print("You can't do that! ${reason}");
game.debugString = reason;
@@ -214,6 +275,7 @@
setState(() {
HeartsGame game = config.game as HeartsGame;
game.jumpToScorePhaseDebug();
+ game.debugString = null;
});
}
@@ -300,63 +362,132 @@
}
Widget showBoard() {
- return new HeartsBoard(config.croupier, this.update,
- width: config.width, height: 0.80 * config.height);
+ return new HeartsBoard(config.croupier,
+ width: config.width,
+ height: 0.80 * config.height,
+ trickTaking: trickTaking,
+ playedCards: playedCards);
+ }
+
+ String _getName(int playerNumber) {
+ return config.croupier.settingsFromPlayerNumber(playerNumber)?.name;
+ }
+
+ String _getStatus() {
+ HeartsGame game = config.game;
+
+ // Who's turn is it?
+ String name = _getName(game.whoseTurn) ?? "Player ${game.whoseTurn}";
+ String status =
+ game.whoseTurn == game.playerNumber ? "Your turn" : "${name}'s turn";
+
+ // Override if someone is taking a trick.
+ if (this.trickTaking) {
+ String trickTaker =
+ _getName(game.lastTrickTaker) ?? "Player ${game.lastTrickTaker}";
+ status = "${trickTaker}'s trick";
+ }
+
+ // Override if there is a debug string.
+ if (config.game.debugString != null) {
+ status = config.game.debugString;
+ }
+
+ return status;
+ }
+
+ Widget _buildStatusBar() {
+ return new Container(
+ padding: new EdgeDims.all(10.0),
+ decoration:
+ new BoxDecoration(backgroundColor: style.theme.primaryColor),
+ child: new Row([
+ new Text(_getStatus(), style: style.Text.largeStyle),
+ new IconButton(icon: "action/swap_vert", onPressed: () {
+ setState(() {
+ _showSplitView = !_showSplitView;
+ });
+ })
+ ], justifyContent: FlexJustifyContent.spaceBetween));
+ }
+
+ Widget _buildFullMiniBoard() {
+ return new Container(
+ width: config.width * 0.5,
+ height: config.height * 0.25,
+ child: new HeartsBoard(config.croupier,
+ width: config.width * 0.5,
+ height: config.height * 0.25,
+ cardWidth: config.height * 0.1,
+ cardHeight: config.height * 0.1,
+ isMini: true,
+ gameAcceptCallback: _makeGameMoveCallback,
+ trickTaking: trickTaking,
+ playedCards: playedCards));
}
Widget showPlay() {
HeartsGame game = config.game as HeartsGame;
+ int p = game.playerNumber;
List<Widget> cardCollections = new List<Widget>();
- // Note that this shouldn't normally be shown.
- // Since this is a duplicate card collection, it will not have keyed cards.
- List<Widget> plays = new List<Widget>();
- for (int i = 0; i < 4; i++) {
- plays.add(new CardCollectionComponent(
- game.cardCollections[i + HeartsGame.OFFSET_PLAY],
- true,
- CardCollectionOrientation.show1,
- width: config.width));
+ if (_showSplitView) {
+ if (!trickTaking) {
+ if (_detectTrick()) {
+ trickTaking = true;
+ _fillMissingPlayedCard();
+ // Unfortunately, ZCards are drawn on the game layer,
+ // so instead of setState, we must use trueSetState.
+ new Future.delayed(const Duration(milliseconds: SHOW_TRICK_DURATION),
+ () {
+ setState(() {
+ trickTaking = false;
+ });
+ });
+ } else {
+ _fillPlayedCards();
+ }
+ }
+ cardCollections.add(new Container(
+ decoration:
+ new BoxDecoration(backgroundColor: style.theme.primaryColor),
+ child: new Column([_buildFullMiniBoard(), _buildStatusBar()])));
+ } else {
+ Widget playArea = new Container(
+ decoration: new BoxDecoration(backgroundColor: Colors.teal[500]),
+ width: config.width,
+ child: new Center(
+ child: new CardCollectionComponent(
+ game.cardCollections[p + HeartsGame.OFFSET_PLAY],
+ true,
+ CardCollectionOrientation.show1,
+ useKeys: true,
+ animationType: component_card.CardAnimationType.NONE,
+ acceptCallback: _makeGameMoveCallback,
+ acceptType:
+ p == game.whoseTurn ? DropType.card : DropType.none,
+ backgroundColor:
+ p == game.whoseTurn ? Colors.white : Colors.grey[500],
+ altColor: p == game.whoseTurn
+ ? Colors.grey[200]
+ : Colors.grey[600])));
+
+ cardCollections.add(new Container(
+ decoration:
+ new BoxDecoration(backgroundColor: style.theme.primaryColor),
+ child: new Column([_buildStatusBar(), playArea])));
}
- cardCollections.add(new Container(
- decoration: new BoxDecoration(backgroundColor: Colors.teal[600]),
- width: config.width,
- child:
- new Flex(plays, justifyContent: FlexJustifyContent.spaceAround)));
-
- int p = game.playerNumber;
-
- Widget playArea = new Container(
- decoration: new BoxDecoration(backgroundColor: Colors.teal[500]),
- width: config.width,
- child: new Center(
- child: new CardCollectionComponent(
- game.cardCollections[p + HeartsGame.OFFSET_PLAY],
- true,
- CardCollectionOrientation.show1,
- useKeys: true,
- acceptCallback: _makeGameMoveCallback,
- acceptType: p == game.whoseTurn ? DropType.card : DropType.none,
- width: config.width,
- backgroundColor:
- p == game.whoseTurn ? Colors.white : Colors.grey[500],
- altColor: p == game.whoseTurn
- ? Colors.grey[200]
- : Colors.grey[600])));
- cardCollections.add(playArea);
List<logic_card.Card> cards = game.cardCollections[p];
CardCollectionComponent c = new CardCollectionComponent(
cards, game.playerNumber == p, CardCollectionOrientation.suit,
- dragChildren: game.whoseTurn == p,
+ dragChildren: true, // Can drag, but may not have anywhere to drop
comparator: _compareCards,
width: config.width,
- useKeys: true);
+ useKeys: _showSplitView);
cardCollections.add(c); // flex
- cardCollections.add(new Text("Player ${game.whoseTurn}'s turn"));
- cardCollections.add(new Text(game.debugString));
cardCollections.add(_makeDebugButtons());
return new Column(cardCollections,
@@ -372,7 +503,7 @@
} else if (!game.isPlayer || game.ready[game.playerNumber]) {
w = new Text("Waiting for other players...");
} else {
- w = _makeButton('Ready?', game.setReadyUI);
+ w = _makeButton('New Round', game.setReadyUI);
}
bool isTall = MediaQuery.of(context).orientation == Orientation.portrait;
@@ -456,8 +587,6 @@
List<logic_card.Card> hand,
AcceptCb cb,
NoArgCb buttoncb) {
- HeartsGame game = config.game as HeartsGame;
-
bool draggable = (cb != null);
bool completed = (buttoncb == null);
@@ -487,12 +616,8 @@
altColor: Colors.grey[700],
useKeys: true);
- return new Column(<Widget>[
- topArea,
- handArea,
- new Text(game.debugString),
- _makeDebugButtons()
- ], justifyContent: FlexJustifyContent.spaceBetween);
+ return new Column(<Widget>[topArea, handArea, _makeDebugButtons()],
+ justifyContent: FlexJustifyContent.spaceBetween);
}
Widget _topCardWidget(List<logic_card.Card> cards, AcceptCb cb) {
diff --git a/lib/components/proto/proto.part.dart b/lib/components/proto/proto.part.dart
index a09f6f8..79e1e7f 100644
--- a/lib/components/proto/proto.part.dart
+++ b/lib/components/proto/proto.part.dart
@@ -6,8 +6,8 @@
class ProtoGameComponent extends GameComponent {
ProtoGameComponent(Croupier croupier, NoArgCb cb,
- {double width, double height})
- : super(croupier, cb, width: width, height: height);
+ {Key key, double width, double height})
+ : super(croupier, cb, key: key, width: width, height: height);
ProtoGameComponentState createState() => new ProtoGameComponentState();
}
diff --git a/lib/components/solitaire/solitaire.part.dart b/lib/components/solitaire/solitaire.part.dart
index 95c8486..a9833db 100644
--- a/lib/components/solitaire/solitaire.part.dart
+++ b/lib/components/solitaire/solitaire.part.dart
@@ -6,8 +6,8 @@
class SolitaireGameComponent extends GameComponent {
SolitaireGameComponent(Croupier croupier, NoArgCb cb,
- {double width, double height})
- : super(croupier, cb, width: width, height: height);
+ {Key key, double width, double height})
+ : super(croupier, cb, key: key, width: width, height: height);
SolitaireGameComponentState createState() =>
new SolitaireGameComponentState();
diff --git a/lib/logic/game/game_def.part.dart b/lib/logic/game/game_def.part.dart
index 5d53ab3..c7544e5 100644
--- a/lib/logic/game/game_def.part.dart
+++ b/lib/logic/game/game_def.part.dart
@@ -107,7 +107,7 @@
}
bool debugMode = false;
- String debugString = 'hello?';
+ String debugString;
NoArgCb updateCallback; // Used to inform components of when a change has occurred. This is especially important when something non-UI related changes what should be drawn.
diff --git a/lib/logic/hearts/hearts_game.part.dart b/lib/logic/hearts/hearts_game.part.dart
index 477de00..a635060 100644
--- a/lib/logic/hearts/hearts_game.part.dart
+++ b/lib/logic/hearts/hearts_game.part.dart
@@ -415,13 +415,10 @@
return "It is not Player ${player}'s turn.";
}
if (trickNumber == 0 && this.numPlayed == 0 && c != TWO_OF_CLUBS) {
- return "Player ${player} must play the two of clubs.";
- }
- if (trickNumber == 0 && isPenaltyCard(c)) {
- return "Cannot play a penalty card on the first round of Hearts.";
+ return "You must play the 2 of Clubs";
}
if (this.numPlayed == 0 && isHeartsCard(c) && !heartsBroken) {
- return "Cannot lead with a heart when the suit has not been broken yet.";
+ return "Hearts have not been broken";
}
if (this.leadingCard != null) {
String leadingSuit = getCardSuit(this.leadingCard);
@@ -429,9 +426,12 @@
if (this.numPlayed >= 1 &&
leadingSuit != otherSuit &&
hasSuit(player, leadingSuit)) {
- return "Must follow with a ${leadingSuit}.";
+ return "You must follow suit";
}
}
+ if (trickNumber == 0 && isPenaltyCard(c)) {
+ return "No penalty cards on 1st trick";
+ }
return null;
}
diff --git a/lib/styles/common.dart b/lib/styles/common.dart
index b5473c1..73e731b 100644
--- a/lib/styles/common.dart
+++ b/lib/styles/common.dart
@@ -40,8 +40,10 @@
class Box {
static final BoxDecoration liveNow = new BoxDecoration(
border: new Border.all(color: theme.accentColor), borderRadius: 2.0);
- static final BoxDecoration border =
- new BoxDecoration(border: new Border.all(), borderRadius: 2.0);
+ static final BoxDecoration border = new BoxDecoration(
+ border: new Border.all(color: theme.primaryColor), borderRadius: 2.0);
+ static final BoxDecoration borderInactive = new BoxDecoration(
+ border: new Border.all(color: Colors.grey[300]), borderRadius: 2.0);
}
ThemeData theme = new ThemeData(
diff --git a/manifest.yaml b/manifest.yaml
index bfa1022..07fe25a 100644
--- a/manifest.yaml
+++ b/manifest.yaml
@@ -4,6 +4,7 @@
- name: action/help
- name: action/settings
- name: av/play_arrow
+ - name: action/swap_vert
- name: navigation/arrow_back
- name: navigation/menu
assets: