From 823cdb6af58ee52d01361f09bbba5d21078e6ff5 Mon Sep 17 00:00:00 2001 From: Alejandro Santiago Date: Wed, 23 Mar 2022 16:15:06 +0000 Subject: [PATCH] feat: include DashNest bonus (#80) * feat: included dash nest logic Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> --- lib/game/bloc/game_bloc.dart | 25 +++++++++++ lib/game/bloc/game_event.dart | 9 ++++ lib/game/bloc/game_state.dart | 12 +++++ test/game/bloc/game_bloc_test.dart | 52 ++++++++++++++++++++++ test/game/bloc/game_event_test.dart | 17 +++++++ test/game/bloc/game_state_test.dart | 16 +++++++ test/game/components/ball_test.dart | 1 + test/game/components/board_test.dart | 19 ++++---- test/game/components/bonus_word_test.dart | 3 ++ test/game/view/game_hud_test.dart | 1 + test/game/view/pinball_game_page_test.dart | 1 + test/helpers/extensions.dart | 39 ---------------- 12 files changed, 146 insertions(+), 49 deletions(-) diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 663fee35..c02417a7 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -12,6 +12,7 @@ class GameBloc extends Bloc { on(_onBallLost); on(_onScored); on(_onBonusLetterActivated); + on(_onDashNestActivated); } static const bonusWord = 'GOOGLE'; @@ -52,4 +53,28 @@ class GameBloc extends Bloc { ); } } + + void _onDashNestActivated(DashNestActivated event, Emitter emit) { + const nestsRequiredForBonus = 3; + + final newNests = { + ...state.activatedDashNests, + event.nestId, + }; + if (newNests.length == nestsRequiredForBonus) { + emit( + state.copyWith( + activatedDashNests: {}, + bonusHistory: [ + ...state.bonusHistory, + GameBonus.dashNest, + ], + ), + ); + } else { + emit( + state.copyWith(activatedDashNests: newNests), + ); + } + } } diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index 0edc91ab..b05c5336 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -45,3 +45,12 @@ class BonusLetterActivated extends GameEvent { @override List get props => [letterIndex]; } + +class DashNestActivated extends GameEvent { + const DashNestActivated(this.nestId); + + final String nestId; + + @override + List get props => [nestId]; +} diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index e2c39d1f..5c722946 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -7,6 +7,10 @@ enum GameBonus { /// Bonus achieved when the user activate all of the bonus /// letters on the board, forming the bonus word word, + + /// Bonus achieved when the user activates all of the Dash + /// nests on the board, adding a new ball to the board. + dashNest, } /// {@template game_state} @@ -19,6 +23,7 @@ class GameState extends Equatable { required this.balls, required this.activatedBonusLetters, required this.bonusHistory, + required this.activatedDashNests, }) : assert(score >= 0, "Score can't be negative"), assert(balls >= 0, "Number of balls can't be negative"); @@ -26,6 +31,7 @@ class GameState extends Equatable { : score = 0, balls = 3, activatedBonusLetters = const [], + activatedDashNests = const {}, bonusHistory = const []; /// The current score of the game. @@ -39,6 +45,9 @@ class GameState extends Equatable { /// Active bonus letters. final List activatedBonusLetters; + /// Active dash nests. + final Set activatedDashNests; + /// Holds the history of all the [GameBonus]es earned by the player during a /// PinballGame. final List bonusHistory; @@ -57,6 +66,7 @@ class GameState extends Equatable { int? score, int? balls, List? activatedBonusLetters, + Set? activatedDashNests, List? bonusHistory, }) { assert( @@ -69,6 +79,7 @@ class GameState extends Equatable { balls: balls ?? this.balls, activatedBonusLetters: activatedBonusLetters ?? this.activatedBonusLetters, + activatedDashNests: activatedDashNests ?? this.activatedDashNests, bonusHistory: bonusHistory ?? this.bonusHistory, ); } @@ -78,6 +89,7 @@ class GameState extends Equatable { score, balls, activatedBonusLetters, + activatedDashNests, bonusHistory, ]; } diff --git a/test/game/bloc/game_bloc_test.dart b/test/game/bloc/game_bloc_test.dart index 18e50858..f4b79001 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -25,18 +25,21 @@ void main() { score: 0, balls: 2, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 0, balls: 1, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 0, balls: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), ], @@ -56,12 +59,14 @@ void main() { score: 2, balls: 3, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 5, balls: 3, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), ], @@ -82,18 +87,21 @@ void main() { score: 0, balls: 2, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 0, balls: 1, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 0, balls: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), ], @@ -113,18 +121,21 @@ void main() { score: 0, balls: 3, activatedBonusLetters: [0], + activatedDashNests: {}, bonusHistory: [], ), GameState( score: 0, balls: 3, activatedBonusLetters: [0, 1], + activatedDashNests: {}, bonusHistory: [], ), GameState( score: 0, balls: 3, activatedBonusLetters: [0, 1, 2], + activatedDashNests: {}, bonusHistory: [], ), ], @@ -145,46 +156,87 @@ void main() { score: 0, balls: 3, activatedBonusLetters: [0], + activatedDashNests: {}, bonusHistory: [], ), GameState( score: 0, balls: 3, activatedBonusLetters: [0, 1], + activatedDashNests: {}, bonusHistory: [], ), GameState( score: 0, balls: 3, activatedBonusLetters: [0, 1, 2], + activatedDashNests: {}, bonusHistory: [], ), GameState( score: 0, balls: 3, activatedBonusLetters: [0, 1, 2, 3], + activatedDashNests: {}, bonusHistory: [], ), GameState( score: 0, balls: 3, activatedBonusLetters: [0, 1, 2, 3, 4], + activatedDashNests: {}, bonusHistory: [], ), GameState( score: 0, balls: 3, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [GameBonus.word], ), GameState( score: GameBloc.bonusWordScore, balls: 3, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [GameBonus.word], ), ], ); }); + + group('DashNestActivated', () { + blocTest( + 'adds the bonus when all nests are activated', + build: GameBloc.new, + act: (bloc) => bloc + ..add(const DashNestActivated('0')) + ..add(const DashNestActivated('1')) + ..add(const DashNestActivated('2')), + expect: () => const [ + GameState( + score: 0, + balls: 3, + activatedBonusLetters: [], + activatedDashNests: {'0'}, + bonusHistory: [], + ), + GameState( + score: 0, + balls: 3, + activatedBonusLetters: [], + activatedDashNests: {'0', '1'}, + bonusHistory: [], + ), + GameState( + score: 0, + balls: 3, + activatedBonusLetters: [], + activatedDashNests: {}, + bonusHistory: [GameBonus.dashNest], + ), + ], + ); + }); }); } diff --git a/test/game/bloc/game_event_test.dart b/test/game/bloc/game_event_test.dart index d6d2278b..af9f6148 100644 --- a/test/game/bloc/game_event_test.dart +++ b/test/game/bloc/game_event_test.dart @@ -67,5 +67,22 @@ void main() { }, ); }); + + group('DashNestActivated', () { + test('can be instantiated', () { + expect(const DashNestActivated('0'), isNotNull); + }); + + test('supports value equality', () { + expect( + DashNestActivated('0'), + equals(DashNestActivated('0')), + ); + expect( + DashNestActivated('0'), + isNot(equals(DashNestActivated('1'))), + ); + }); + }); }); } diff --git a/test/game/bloc/game_state_test.dart b/test/game/bloc/game_state_test.dart index 8ab72e6c..aa8bdf66 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -11,6 +11,7 @@ void main() { score: 0, balls: 0, activatedBonusLetters: const [], + activatedDashNests: const {}, bonusHistory: const [], ), equals( @@ -18,6 +19,7 @@ void main() { score: 0, balls: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), ), @@ -31,6 +33,7 @@ void main() { score: 0, balls: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), isNotNull, @@ -47,6 +50,7 @@ void main() { balls: -1, score: 0, activatedBonusLetters: const [], + activatedDashNests: const {}, bonusHistory: const [], ), throwsAssertionError, @@ -63,6 +67,7 @@ void main() { balls: 0, score: -1, activatedBonusLetters: const [], + activatedDashNests: const {}, bonusHistory: const [], ), throwsAssertionError, @@ -78,6 +83,7 @@ void main() { balls: 0, score: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); expect(gameState.isGameOver, isTrue); @@ -90,6 +96,7 @@ void main() { balls: 1, score: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); expect(gameState.isGameOver, isFalse); @@ -105,6 +112,7 @@ void main() { balls: 1, score: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); expect(gameState.isLastBall, isTrue); @@ -119,6 +127,7 @@ void main() { balls: 2, score: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); expect(gameState.isLastBall, isFalse); @@ -134,6 +143,7 @@ void main() { balls: 3, score: 0, activatedBonusLetters: [1], + activatedDashNests: {}, bonusHistory: [], ); expect(gameState.isLetterActivated(1), isTrue); @@ -147,6 +157,7 @@ void main() { balls: 3, score: 0, activatedBonusLetters: [1], + activatedDashNests: {}, bonusHistory: [], ); expect(gameState.isLetterActivated(0), isFalse); @@ -163,6 +174,7 @@ void main() { balls: 0, score: 2, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); expect( @@ -180,6 +192,7 @@ void main() { balls: 0, score: 2, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); expect( @@ -197,12 +210,14 @@ void main() { score: 2, balls: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); final otherGameState = GameState( score: gameState.score + 1, balls: gameState.balls + 1, activatedBonusLetters: const [0], + activatedDashNests: const {'1'}, bonusHistory: const [GameBonus.word], ); expect(gameState, isNot(equals(otherGameState))); @@ -212,6 +227,7 @@ void main() { score: otherGameState.score, balls: otherGameState.balls, activatedBonusLetters: otherGameState.activatedBonusLetters, + activatedDashNests: otherGameState.activatedDashNests, bonusHistory: otherGameState.bonusHistory, ), equals(otherGameState), diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart index 7a48b21f..f94c1526 100644 --- a/test/game/components/ball_test.dart +++ b/test/game/components/ball_test.dart @@ -156,6 +156,7 @@ void main() { score: 10, balls: 1, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ), ); diff --git a/test/game/components/board_test.dart b/test/game/components/board_test.dart index 7791d891..f0cd0e16 100644 --- a/test/game/components/board_test.dart +++ b/test/game/components/board_test.dart @@ -30,9 +30,9 @@ void main() { await game.ready(); await game.ensureAdd(board); - final leftFlippers = board.findNestedChildren( - condition: (flipper) => flipper.side.isLeft, - ); + final leftFlippers = board.descendants().whereType().where( + (flipper) => flipper.side.isLeft, + ); expect(leftFlippers.length, equals(1)); }, ); @@ -43,10 +43,9 @@ void main() { final board = Board(); await game.ready(); await game.ensureAdd(board); - - final rightFlippers = board.findNestedChildren( - condition: (flipper) => flipper.side.isRight, - ); + final rightFlippers = board.descendants().whereType().where( + (flipper) => flipper.side.isRight, + ); expect(rightFlippers.length, equals(1)); }, ); @@ -58,7 +57,7 @@ void main() { await game.ready(); await game.ensureAdd(board); - final baseboards = board.findNestedChildren(); + final baseboards = board.descendants().whereType(); expect(baseboards.length, equals(2)); }, ); @@ -70,7 +69,7 @@ void main() { await game.ready(); await game.ensureAdd(board); - final kickers = board.findNestedChildren(); + final kickers = board.descendants().whereType(); expect(kickers.length, equals(2)); }, ); @@ -83,7 +82,7 @@ void main() { await game.ready(); await game.ensureAdd(board); - final roundBumpers = board.findNestedChildren(); + final roundBumpers = board.descendants().whereType(); expect(roundBumpers.length, equals(3)); }, ); diff --git a/test/game/components/bonus_word_test.dart b/test/game/components/bonus_word_test.dart index 47a7e257..a12a5a74 100644 --- a/test/game/components/bonus_word_test.dart +++ b/test/game/components/bonus_word_test.dart @@ -234,6 +234,7 @@ void main() { score: 0, balls: 2, activatedBonusLetters: [0], + activatedDashNests: {}, bonusHistory: [], ); whenListen( @@ -259,6 +260,7 @@ void main() { score: 0, balls: 2, activatedBonusLetters: [0], + activatedDashNests: {}, bonusHistory: [], ); @@ -283,6 +285,7 @@ void main() { score: 0, balls: 2, activatedBonusLetters: [0], + activatedDashNests: {}, bonusHistory: [], ); diff --git a/test/game/view/game_hud_test.dart b/test/game/view/game_hud_test.dart index 536edbad..953b89eb 100644 --- a/test/game/view/game_hud_test.dart +++ b/test/game/view/game_hud_test.dart @@ -13,6 +13,7 @@ void main() { score: 10, balls: 2, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index dcf0c001..5298d6ac 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -88,6 +88,7 @@ void main() { score: 0, balls: 0, activatedBonusLetters: [], + activatedDashNests: {}, bonusHistory: [], ); diff --git a/test/helpers/extensions.dart b/test/helpers/extensions.dart index abea191d..b3c4c6f8 100644 --- a/test/helpers/extensions.dart +++ b/test/helpers/extensions.dart @@ -1,4 +1,3 @@ -import 'package:flame/components.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_theme/pinball_theme.dart'; @@ -21,41 +20,3 @@ extension DebugPinballGameTest on DebugPinballGame { ), ); } - -extension ComponentX on Component { - T findNestedChild({ - bool Function(T)? condition, - }) { - T? nestedChild; - propagateToChildren((child) { - final foundChild = (condition ?? (_) => true)(child); - if (foundChild) { - nestedChild = child; - } - - return !foundChild; - }); - - if (nestedChild == null) { - throw Exception('No child of type $T found.'); - } else { - return nestedChild!; - } - } - - List findNestedChildren({ - bool Function(T)? condition, - }) { - final nestedChildren = []; - propagateToChildren((child) { - final foundChild = (condition ?? (_) => true)(child); - if (foundChild) { - nestedChildren.add(child); - } - - return true; - }); - - return nestedChildren; - } -}