| Index: samples/pop_pop_win/lib/src/stage/game_element.dart
|
| diff --git a/samples/pop_pop_win/lib/src/stage/game_element.dart b/samples/pop_pop_win/lib/src/stage/game_element.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3bceeb4f5677533ac42af3e8e49c902bcb96e782
|
| --- /dev/null
|
| +++ b/samples/pop_pop_win/lib/src/stage/game_element.dart
|
| @@ -0,0 +1,311 @@
|
| +library pop_pop_win.stage.game_element;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:math';
|
| +
|
| +import 'package:bot/bot.dart' show Size, Vector, Tuple;
|
| +import 'package:stagexl/stagexl.dart' hide Vector, Point;
|
| +
|
| +import 'package:pop_pop_win/src/audio.dart';
|
| +import 'package:pop_pop_win/src/game.dart';
|
| +
|
| +import 'board_element.dart';
|
| +import 'game_background_element.dart';
|
| +import 'game_root.dart';
|
| +import 'score_element.dart';
|
| +import 'square_element.dart';
|
| +
|
| +class GameElement extends Sprite {
|
| + static const _edgeOffset = 32;
|
| + static const _backgroundSize = const Size(2048, 1536);
|
| + static const _backgroundHoleSize = 16 * SquareElement.SIZE + 2 * _edgeOffset;
|
| + static const BOARD_OFFSET = const Vector(352, 96);
|
| + static const _popAnimationHitFrame = 12;
|
| + static const _popExplodeAnimationOffset = const Vector(-88, -88);
|
| + static const _dartAnimationOffset =
|
| + const Vector(-512 + 0.5 * SquareElement.SIZE,
|
| + -388 + 0.5 * SquareElement.SIZE);
|
| +
|
| + final GameRoot manager;
|
| +
|
| + final Random _rnd = new Random();
|
| +
|
| + GameBackgroundElement _gameBackground;
|
| + BoardElement _boardElement;
|
| + ScoreElement _scoreElement;
|
| + SimpleButton _newGameButton, _logoButton;
|
| + Sprite _popLayer = new Sprite(), _dartLayer = new Sprite();
|
| +
|
| + num _boardSize, _boardScale;
|
| +
|
| +
|
| + int _targetX, _targetY;
|
| + TextureAtlas _animations;
|
| +
|
| + Game get game => manager.game;
|
| + ResourceManager get resourceManager => manager.resourceManager;
|
| +
|
| + num get boardSize => _boardSize;
|
| + num get boardScale => _boardScale;
|
| +
|
| + ScoreElement get scoreElement => _scoreElement;
|
| + BoardElement get boardElement => _boardElement;
|
| +
|
| + GameElement(this.manager) {
|
| + TextureAtlas opa = resourceManager.getTextureAtlas('opaque');
|
| + TextureAtlas sta = resourceManager.getTextureAtlas('static');
|
| + _animations = resourceManager.getTextureAtlas('animated');
|
| +
|
| + _boardSize = game.field.width * SquareElement.SIZE + 2 * _edgeOffset;
|
| + _boardScale = _backgroundHoleSize / _boardSize;
|
| +
|
| + _gameBackground = new GameBackgroundElement(this, opa);
|
| +
|
| + var newButtonNormal = new Bitmap(sta.getBitmapData("button_new_game"));
|
| + var newButtonPressed =
|
| + new Bitmap(sta.getBitmapData("button_new_game_clicked"));
|
| +
|
| + _newGameButton = new SimpleButton(newButtonNormal, newButtonPressed,
|
| + newButtonPressed, newButtonPressed)
|
| + ..x = 450
|
| + ..y = 20
|
| + ..onMouseClick.listen((e) {
|
| + GameAudio.click();
|
| + manager.newGame();
|
| + })
|
| + ..addTo(this);
|
| +
|
| + _boardElement = new BoardElement(this)
|
| + ..x = BOARD_OFFSET.x + _edgeOffset * _boardScale
|
| + ..y = BOARD_OFFSET.y + _edgeOffset * _boardScale;
|
| +
|
| +
|
| + manager.bestTimeMilliseconds.then((v) {
|
| + if (v == null) v = 0;
|
| + _scoreElement = new ScoreElement(v)..addTo(this);
|
| +
|
| + stage.juggler.add(_scoreElement);
|
| +
|
| + });
|
| +
|
| + num logoScale = min(max(_boardScale, 1.1), 1.5);
|
| + Bitmap logo = new Bitmap(sta.getBitmapData('logo_win'));
|
| + _logoButton = new SimpleButton(logo, logo, logo, logo);
|
| + _logoButton
|
| + ..y = 20
|
| + ..scaleX = logoScale
|
| + ..scaleY = logoScale
|
| + ..x = _backgroundSize.width / 2 - _logoButton.width / 2
|
| + ..onMouseClick.listen((e) => _titleClickedEventHandle.add(null))
|
| + ..addTo(this);
|
| +
|
| + _popLayer
|
| + ..mouseEnabled = false
|
| + ..x = BOARD_OFFSET.x + _edgeOffset * _boardScale
|
| + ..y = BOARD_OFFSET.y + _edgeOffset * _boardScale
|
| + ..scaleX = _boardScale
|
| + ..scaleY = _boardScale
|
| + ..addTo(this);
|
| +
|
| + _dartLayer
|
| + ..mouseEnabled = false
|
| + ..x = BOARD_OFFSET.x + _edgeOffset * _boardScale
|
| + ..y = BOARD_OFFSET.y + _edgeOffset * _boardScale
|
| + ..scaleX = _boardScale
|
| + ..scaleY = _boardScale
|
| + ..addTo(this);
|
| +
|
| + }
|
| +
|
| + bool get canRevealTarget =>
|
| + _targetX != null && game.canReveal(_targetX, _targetY);
|
| +
|
| + bool get canFlagTarget =>
|
| + _targetX != null && game.canToggleFlag(_targetX, _targetY);
|
| +
|
| + void revealTarget() {
|
| + if (_targetX != null) {
|
| + game.reveal(_targetX, _targetY);
|
| + }
|
| + }
|
| +
|
| + void click(int x, int y, bool alt) {
|
| + assert(!game.gameEnded);
|
| + final ss = game.getSquareState(x, y);
|
| +
|
| + List<Point> reveals = null;
|
| +
|
| + if (alt) {
|
| + if (ss == SquareState.hidden || ss == SquareState.flagged) {
|
| + _toggleFlag(x, y);
|
| + } else if (ss == SquareState.revealed) {
|
| + if (game.canReveal(x, y)) {
|
| + // get adjacent ballons
|
| + final adjHidden = game.field.getAdjacentIndices(x, y)
|
| + .map((i) {
|
| + final t = game.field.getCoordinate(i);
|
| + return new Point(t.item1, t.item2);
|
| + })
|
| + .where((t) => game.getSquareState(t.x, t.y) == SquareState.hidden)
|
| + .toList();
|
| +
|
| + assert(adjHidden.length > 0);
|
| +
|
| + _startDartAnimation(adjHidden);
|
| + reveals = game.reveal(x, y);
|
| + }
|
| + }
|
| + } else {
|
| + if (ss == SquareState.hidden) {
|
| + _startDartAnimation([new Point(x, y)]);
|
| + reveals = game.reveal(x, y);
|
| + }
|
| + }
|
| +
|
| + if (reveals != null && reveals.length > 0) {
|
| + assert(game.state != GameState.lost);
|
| + if (!alt) {
|
| + // if it was a normal click, the first item should be the clicked item
|
| + var first = reveals[0];
|
| + assert(first.x == x);
|
| + assert(first.y == y);
|
| + }
|
| + _startPopAnimation(new Point(x, y), reveals);
|
| + } else if (game.state == GameState.lost) {
|
| + _startPopAnimation(new Point(x, y));
|
| + }
|
| + }
|
| +
|
| + bool _toggleFlag(int x, int y) {
|
| + assert(!game.gameEnded);
|
| + final se = _boardElement.squares.get(x, y);
|
| + final ss = se.squareState;
|
| + if (ss == SquareState.hidden) {
|
| + game.setFlag(x, y, true);
|
| + se.updateState();
|
| + GameAudio.flag();
|
| + return true;
|
| + } else if (ss == SquareState.flagged) {
|
| + game.setFlag(x, y, false);
|
| + se.updateState();
|
| + GameAudio.unflag();
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + void _startPopAnimation(Point start, [Iterable<Point> reveals = null]) {
|
| + if (reveals == null) {
|
| + assert(game.state == GameState.lost);
|
| + reveals = new Iterable.generate(game.field.length)
|
| + .map((i) {
|
| + var t = game.field.getCoordinate(i);
|
| + var c = new Point(t.item1, t.item2);
|
| + return new Tuple(c, game.getSquareState(c.x, c.y));
|
| + })
|
| + .where((t2) => t2.item2 == SquareState.bomb ||
|
| + t2.item2 == SquareState.hidden)
|
| + .map((t2) => t2.item1)
|
| + .toList();
|
| + }
|
| +
|
| + final values = reveals.map((c) {
|
| + var initialOffset =
|
| + new Vector(SquareElement.SIZE * c.x, SquareElement.SIZE * c.y);
|
| + var squareOffset = _popExplodeAnimationOffset + initialOffset;
|
| +
|
| + var delay = _popAnimationHitFrame + ((c - start).magnitude * 4).toInt();
|
| + delay += _rnd.nextInt(10);
|
| +
|
| + return [c, initialOffset, squareOffset, delay];
|
| + }).toList();
|
| +
|
| + values.sort((a, b) {
|
| + int da = a[3];
|
| + int db = b[3];
|
| + return da.compareTo(db);
|
| + });
|
| +
|
| + for (var v in values) {
|
| + Point c = v[0];
|
| + Vector initialOffset = v[1];
|
| + Vector squareOffset = v[2];
|
| + int delay = v[3];
|
| +
|
| + var se = _boardElement.squares.get(c.x, c.y);
|
| + var ss = se.squareState;
|
| +
|
| + var texturePrefix = ss == SquareState.bomb ?
|
| + 'balloon_explode' : 'balloon_pop';
|
| +
|
| + var anim = new FlipBook(_animations.getBitmapDatas(texturePrefix),
|
| + stage.frameRate, false)
|
| + ..x = squareOffset.x
|
| + ..y = squareOffset.y
|
| + ..alpha = 0
|
| + ..mouseEnabled = false
|
| + ..addTo(_popLayer);
|
| +
|
| + anim.onComplete.listen((e) => anim.removeFromParent());
|
| +
|
| + stage.juggler
|
| + ..add(anim)
|
| + ..delayCall(() => _animationDelay(anim, se, ss),
|
| + delay / stage.frameRate);
|
| + }
|
| + }
|
| +
|
| + void _startDartAnimation(List<Point> points) {
|
| + assert(points.length >= 1);
|
| + GameAudio.throwDart();
|
| + for (var point in points) {
|
| + var squareOffset = _dartAnimationOffset +
|
| + new Vector(SquareElement.SIZE * point.x,
|
| + SquareElement.SIZE * point.y);
|
| +
|
| + var dart = new FlipBook(_animations.getBitmapDatas('dart'),
|
| + stage.frameRate, false)
|
| + ..x = squareOffset.x
|
| + ..y = squareOffset.y
|
| + ..mouseEnabled = false
|
| + ..play()
|
| + ..addTo(_dartLayer);
|
| +
|
| + dart.onComplete.listen((e) => dart.removeFromParent());
|
| +
|
| + var shadow = new FlipBook(_animations.getBitmapDatas('shadow'),
|
| + stage.frameRate, false)
|
| + ..x = squareOffset.x
|
| + ..y = squareOffset.y
|
| + ..mouseEnabled = false
|
| + ..play()
|
| + ..addTo(_dartLayer);
|
| +
|
| + shadow.onComplete.listen((e) => shadow.removeFromParent());
|
| +
|
| + stage.juggler
|
| + ..add(dart)
|
| + ..add(shadow);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void _animationDelay(FlipBook anim, SquareElement se, SquareState ss) {
|
| + anim
|
| + ..alpha = 1
|
| + ..play();
|
| + se.updateState();
|
| + switch (ss) {
|
| + case SquareState.revealed:
|
| + case SquareState.hidden:
|
| + GameAudio.pop();
|
| + break;
|
| + case SquareState.bomb:
|
| + GameAudio.bomb();
|
| + break;
|
| + }
|
| +}
|
| +
|
| +final StreamController _titleClickedEventHandle = new StreamController();
|
| +
|
| +Stream get titleClickedEvent => _titleClickedEventHandle.stream;
|
|
|