blob: 56143549f3c2caa2d4162d8e5289bde84bc8714c [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import 'card_collection.dart' show CardCollectionComponent, CardCollectionOrientation;
import '../logic/card.dart' as logic_card;
import '../logic/game/game.dart' show Game, GameType;
import '../logic/hearts/hearts.dart' show HeartsGame;
import 'dart:math' as math;
import 'package:flutter/material.dart';
const double defaultBoardHeight = 400.0;
const double defaultBoardWidth = 400.0;
const double defaultCardHeight = 40.0;
const double defaultCardWidth = 40.0;
/// A Board represents a fixed-size canvas for drawing a Game's UI.
/// While other Widgets may be drawn to accomodate space, a Board is meant to
/// consume a specific amount of space on the screen, which allows for more
/// control when positioning elements within the Board's area.
abstract class Board extends StatelessComponent {
final Game game;
final double _height;
final double _width;
final double _cardHeight;
final double _cardWidth;
double get height => _height ?? defaultBoardHeight;
double get width => _width ?? defaultBoardWidth;
double get cardHeight => _cardHeight ?? defaultCardHeight;
double get cardWidth => _cardWidth ?? defaultCardWidth;
Board(this.game,
{double height, double width, double cardHeight, double cardWidth})
: _height = height,
_width = width,
_cardHeight = cardHeight,
_cardWidth = cardWidth;
}
/// The HeartsBoard represents the Hearts table view, which shows the number of
/// cards each player has, and the cards they are currently playing.
class HeartsBoard extends Board {
HeartsBoard(HeartsGame game,
{double height, double width, double cardHeight, double cardWidth})
: super(game,
height: height,
width: width,
cardHeight: cardHeight,
cardWidth: cardWidth);
Widget build(BuildContext context) {
List<Widget> pile = new List<Widget>();
_addHandsToPile(pile);
_addProfilesToPile(pile);
_addPlaysToPile(pile);
return new Container(
height: this.height, width: this.width, child: new Stack(pile));
}
// Show the hands of each player (facedown) around the perimeter of the board.
void _addHandsToPile(List<Widget> pile) {
HeartsGame game = this.game;
for (int i = 0; i < 4; i++) {
List<logic_card.Card> cards =
game.cardCollections[i + HeartsGame.OFFSET_HAND];
CardCollectionOrientation ori = i % 2 == 0 ? CardCollectionOrientation.horz : CardCollectionOrientation.vert;
bool wide = (this.width >= this.height);
double smallerSide = wide ? this.height : this.width;
double sizeRatio = 0.60;
double cccSize = sizeRatio * smallerSide;
CardCollectionComponent ccc = new CardCollectionComponent(
cards, false, ori,
width: i % 2 == 0 ? cccSize : null,
height: i % 2 != 0 ? cccSize : null,
rotation: -math.PI / 2 * i,
useKeys: true);
Widget w;
switch (i) {
case 2:
w = new Positioned(
top: 0.0, left: (this.width - cccSize) / 2.0, child: ccc);
break;
case 3:
w = new Positioned(
top: (this.height - cccSize) / 2.0, left: 0.0, child: ccc);
break;
case 0:
w = new Positioned(
// TODO(alexfandrianto): 1.7 is a magic number, but it just looks right somehow.
// This could be due to the margins from each card collection.
top: this.height - 1.7 * this.cardHeight,
left: (this.width - cccSize) / 2.0,
child: ccc);
break;
case 1:
w = new Positioned(
top: (this.height - cccSize) / 2.0,
left: this.width - 1.7 * this.cardWidth,
child: ccc);
break;
default:
assert(false);
}
pile.add(w);
}
}
// Create and add Player Profile widgets to the board.
void _addProfilesToPile(List<Widget> pile) {
// TODO(alexfandrianto): Show player profiles.
// I need to access each player's CroupierSettings here.
}
// Add 4 play slots. If the board is wider than it is tall, we need to have
// A flat diamond (where the center 2 cards are stacked on top of each other).
// If the board is taller than it is wide, then we want a tall diamond. The
// center 2 cards should be horizontally adjacent.
// TODO(alexfandrianto): Once I get the player profile settings, I can set
// the background color of each play slot.
void _addPlaysToPile(List<Widget> pile) {
HeartsGame game = this.game;
for (int i = 0; i < 4; i++) {
List<logic_card.Card> cards =
game.cardCollections[i + HeartsGame.OFFSET_PLAY];
double MARGIN = 10.0;
CardCollectionComponent ccc = new CardCollectionComponent(
cards, true, CardCollectionOrientation.show1,
width: this.cardWidth,
widthCard: this.cardWidth,
height: this.cardHeight,
heightCard: this.cardHeight,
rotation: -math.PI / 2 * i,
useKeys: true);
Widget w;
double left02 = (this.width - this.cardWidth) / 2;
double top13 = (this.height - this.cardHeight) / 2.0;
double baseTop = (this.height - (this.cardHeight * 2 + MARGIN)) / 2;
double baseLeft = (this.width - (this.cardWidth * 2 + MARGIN)) / 2;
double dHeight = (this.cardHeight + MARGIN) / 2;
double dWidth = (this.cardWidth + MARGIN) / 2;
if (this.width >= this.height) {
switch (i) {
case 2:
w = new Positioned(top: baseTop, left: left02, child: ccc);
break;
case 3:
w = new Positioned(top: top13, left: baseLeft - dWidth, child: ccc);
break;
case 0:
w = new Positioned(
top: baseTop + dHeight * 2, left: left02, child: ccc);
break;
case 1:
w = new Positioned(
top: top13, left: baseLeft + dWidth * 3, child: ccc);
break;
default:
assert(false);
}
} else {
switch (i) {
case 2:
w = new Positioned(
top: baseTop - dHeight, left: left02, child: ccc);
break;
case 3:
w = new Positioned(top: top13, left: baseLeft, child: ccc);
break;
case 0:
w = new Positioned(
top: baseTop + dHeight * 3, left: left02, child: ccc);
break;
case 1:
w = new Positioned(
top: top13, left: baseLeft + dHeight * 2, child: ccc);
break;
default:
assert(false);
}
}
pile.add(w);
}
}
}