diff --git a/lib/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior.dart b/lib/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior.dart index b884410e..86857ee4 100644 --- a/lib/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior.dart +++ b/lib/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior.dart @@ -3,7 +3,10 @@ import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; -/// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest] +/// Bonus obtained at the [FlutterForest]. +/// +/// When all [DashNestBumper]s are hit at least once three times, the [Signpost] +/// progresses. When the [Signpost] fully progresses, the [GameBonus.dashNest] /// is awarded, and the [DashNestBumper.main] releases a new [Ball]. class FlutterForestBonusBehavior extends Component with ParentIsA, HasGameRef { @@ -12,28 +15,36 @@ class FlutterForestBonusBehavior extends Component super.onMount(); final bumpers = parent.children.whereType(); + final signpost = parent.firstChild()!; + final animatronic = parent.firstChild()!; + final canvas = gameRef.firstChild()!; + for (final bumper in bumpers) { // TODO(alestiago): Refactor subscription management once the following is // merged: // https://github.com/flame-engine/flame/pull/1538 bumper.bloc.stream.listen((state) { - final achievedBonus = bumpers.every( + final activatedAllBumpers = bumpers.every( (bumper) => bumper.bloc.state == DashNestBumperState.active, ); - if (achievedBonus) { - gameRef - .read() - .add(const BonusActivated(GameBonus.dashNest)); - gameRef.firstChild()!.add( - ControlledBall.bonus(characterTheme: gameRef.characterTheme) - ..initialPosition = Vector2(17.2, -52.7), - ); - parent.firstChild()?.playing = true; - + if (activatedAllBumpers) { + signpost.bloc.onProgressed(); for (final bumper in bumpers) { bumper.bloc.onReset(); } + + if (signpost.bloc.isFullyProgressed()) { + gameRef + .read() + .add(const BonusActivated(GameBonus.dashNest)); + canvas.add( + ControlledBall.bonus(characterTheme: gameRef.characterTheme) + ..initialPosition = Vector2(17.2, -52.7), + ); + animatronic.playing = true; + signpost.bloc.onProgressed(); + } } }); } diff --git a/packages/pinball_components/assets/images/dash/bumper/a/inactive.png b/packages/pinball_components/assets/images/dash/bumper/a/inactive.png index aead95ec..bd37498d 100644 Binary files a/packages/pinball_components/assets/images/dash/bumper/a/inactive.png and b/packages/pinball_components/assets/images/dash/bumper/a/inactive.png differ diff --git a/packages/pinball_components/assets/images/dash/bumper/b/inactive.png b/packages/pinball_components/assets/images/dash/bumper/b/inactive.png index 3d53b743..81cd775a 100644 Binary files a/packages/pinball_components/assets/images/dash/bumper/b/inactive.png and b/packages/pinball_components/assets/images/dash/bumper/b/inactive.png differ diff --git a/packages/pinball_components/assets/images/dash/bumper/main/inactive.png b/packages/pinball_components/assets/images/dash/bumper/main/inactive.png index b1d0ae7d..51df02ee 100644 Binary files a/packages/pinball_components/assets/images/dash/bumper/main/inactive.png and b/packages/pinball_components/assets/images/dash/bumper/main/inactive.png differ diff --git a/packages/pinball_components/assets/images/signpost/active1.png b/packages/pinball_components/assets/images/signpost/active1.png index 1addb228..78997bf6 100644 Binary files a/packages/pinball_components/assets/images/signpost/active1.png and b/packages/pinball_components/assets/images/signpost/active1.png differ diff --git a/packages/pinball_components/assets/images/signpost/active2.png b/packages/pinball_components/assets/images/signpost/active2.png index 081a936c..39caa821 100644 Binary files a/packages/pinball_components/assets/images/signpost/active2.png and b/packages/pinball_components/assets/images/signpost/active2.png differ diff --git a/packages/pinball_components/assets/images/signpost/active3.png b/packages/pinball_components/assets/images/signpost/active3.png index 8d781dfb..f43c190c 100644 Binary files a/packages/pinball_components/assets/images/signpost/active3.png and b/packages/pinball_components/assets/images/signpost/active3.png differ diff --git a/packages/pinball_components/assets/images/signpost/inactive.png b/packages/pinball_components/assets/images/signpost/inactive.png index 6043454b..9fa23330 100644 Binary files a/packages/pinball_components/assets/images/signpost/inactive.png and b/packages/pinball_components/assets/images/signpost/inactive.png differ diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 81896c74..c5ea7f9f 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -28,7 +28,7 @@ export 'plunger.dart'; export 'rocket.dart'; export 'score_component.dart'; export 'shapes/shapes.dart'; -export 'signpost.dart'; +export 'signpost/signpost.dart'; export 'slingshot.dart'; export 'spaceship_rail.dart'; export 'spaceship_ramp.dart'; diff --git a/packages/pinball_components/lib/src/components/signpost.dart b/packages/pinball_components/lib/src/components/signpost.dart deleted file mode 100644 index 13425342..00000000 --- a/packages/pinball_components/lib/src/components/signpost.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:flame/components.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; -import 'package:pinball_components/pinball_components.dart'; - -/// Represents the [Signpost]'s current [Sprite] state. -@visibleForTesting -enum SignpostSpriteState { - /// Signpost with no active dashes. - inactive, - - /// Signpost with a single sign of active dashes. - active1, - - /// Signpost with two signs of active dashes. - active2, - - /// Signpost with all signs of active dashes. - active3, -} - -extension on SignpostSpriteState { - String get path { - switch (this) { - case SignpostSpriteState.inactive: - return Assets.images.signpost.inactive.keyName; - case SignpostSpriteState.active1: - return Assets.images.signpost.active1.keyName; - case SignpostSpriteState.active2: - return Assets.images.signpost.active2.keyName; - case SignpostSpriteState.active3: - return Assets.images.signpost.active3.keyName; - } - } - - SignpostSpriteState get next { - return SignpostSpriteState - .values[(index + 1) % SignpostSpriteState.values.length]; - } -} - -/// {@template signpost} -/// A sign, found in the Flutter Forest. -/// -/// Lights up a new sign whenever all three [DashNestBumper]s are hit. -/// {@endtemplate} -class Signpost extends BodyComponent with InitialPosition { - /// {@macro signpost} - Signpost({ - Iterable? children, - }) : super( - renderBody: false, - children: [ - _SignpostSpriteComponent(), - ...?children, - ], - ); - - /// Forwards the sprite to the next [SignpostSpriteState]. - /// - /// If the current state is the last one it cycles back to the initial state. - void progress() => firstChild<_SignpostSpriteComponent>()!.progress(); - - @override - Body createBody() { - final shape = CircleShape()..radius = 0.25; - final fixtureDef = FixtureDef(shape); - final bodyDef = BodyDef( - position: initialPosition, - ); - - return world.createBody(bodyDef)..createFixture(fixtureDef); - } -} - -class _SignpostSpriteComponent extends SpriteGroupComponent - with HasGameRef { - _SignpostSpriteComponent() - : super( - anchor: Anchor.bottomCenter, - position: Vector2(0.65, 0.45), - ); - - void progress() => current = current?.next; - - @override - Future onLoad() async { - await super.onLoad(); - - final sprites = {}; - this.sprites = sprites; - for (final spriteState in SignpostSpriteState.values) { - sprites[spriteState] = Sprite( - gameRef.images.fromCache(spriteState.path), - ); - } - - current = SignpostSpriteState.inactive; - size = sprites[current]!.originalSize / 10; - } -} diff --git a/packages/pinball_components/lib/src/components/signpost/cubit/signpost_cubit.dart b/packages/pinball_components/lib/src/components/signpost/cubit/signpost_cubit.dart new file mode 100644 index 00000000..f94feebe --- /dev/null +++ b/packages/pinball_components/lib/src/components/signpost/cubit/signpost_cubit.dart @@ -0,0 +1,18 @@ +// ignore_for_file: public_member_api_docs + +import 'package:bloc/bloc.dart'; + +part 'signpost_state.dart'; + +class SignpostCubit extends Cubit { + SignpostCubit() : super(SignpostState.inactive); + + void onProgressed() { + final index = SignpostState.values.indexOf(state); + emit( + SignpostState.values[(index + 1) % SignpostState.values.length], + ); + } + + bool isFullyProgressed() => state == SignpostState.active3; +} diff --git a/packages/pinball_components/lib/src/components/signpost/cubit/signpost_state.dart b/packages/pinball_components/lib/src/components/signpost/cubit/signpost_state.dart new file mode 100644 index 00000000..72173bf1 --- /dev/null +++ b/packages/pinball_components/lib/src/components/signpost/cubit/signpost_state.dart @@ -0,0 +1,17 @@ +// ignore_for_file: public_member_api_docs + +part of 'signpost_cubit.dart'; + +enum SignpostState { + /// Signpost with no active eggs. + inactive, + + /// Signpost with a single sign of lit up eggs. + active1, + + /// Signpost with two signs of lit up eggs. + active2, + + /// Signpost with all signs of lit up eggs. + active3, +} diff --git a/packages/pinball_components/lib/src/components/signpost/signpost.dart b/packages/pinball_components/lib/src/components/signpost/signpost.dart new file mode 100644 index 00000000..d22f46f3 --- /dev/null +++ b/packages/pinball_components/lib/src/components/signpost/signpost.dart @@ -0,0 +1,109 @@ +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/foundation.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +export 'cubit/signpost_cubit.dart'; + +/// {@template signpost} +/// A sign, found in the Flutter Forest. +/// +/// Lights up a new sign whenever all three [DashNestBumper]s are hit. +/// {@endtemplate} +class Signpost extends BodyComponent with InitialPosition { + /// {@macro signpost} + Signpost({ + Iterable? children, + }) : this._( + children: children, + bloc: SignpostCubit(), + ); + + Signpost._({ + Iterable? children, + required this.bloc, + }) : super( + renderBody: false, + children: [ + _SignpostSpriteComponent( + current: bloc.state, + ), + ...?children, + ], + ); + + /// Creates a [Signpost] without any children. + /// + /// This can be used for testing [Signpost]'s behaviors in isolation. + // TODO(alestiago): Refactor injecting bloc once the following is merged: + // https://github.com/flame-engine/flame/pull/1538 + @visibleForTesting + Signpost.test({ + required this.bloc, + }); + + // TODO(alestiago): Consider refactoring once the following is merged: + // https://github.com/flame-engine/flame/pull/1538 + // ignore: public_member_api_docs + final SignpostCubit bloc; + + @override + void onRemove() { + bloc.close(); + super.onRemove(); + } + + @override + Body createBody() { + final shape = CircleShape()..radius = 0.25; + final bodyDef = BodyDef( + position: initialPosition, + ); + + return world.createBody(bodyDef)..createFixtureFromShape(shape); + } +} + +class _SignpostSpriteComponent extends SpriteGroupComponent + with HasGameRef, ParentIsA { + _SignpostSpriteComponent({ + required SignpostState current, + }) : super( + anchor: Anchor.bottomCenter, + position: Vector2(0.65, 0.45), + current: current, + ); + + @override + Future onLoad() async { + await super.onLoad(); + parent.bloc.stream.listen((state) => current = state); + + final sprites = {}; + this.sprites = sprites; + for (final spriteState in SignpostState.values) { + sprites[spriteState] = Sprite( + gameRef.images.fromCache(spriteState.path), + ); + } + + current = SignpostState.inactive; + size = sprites[current]!.originalSize / 10; + } +} + +extension on SignpostState { + String get path { + switch (this) { + case SignpostState.inactive: + return Assets.images.signpost.inactive.keyName; + case SignpostState.active1: + return Assets.images.signpost.active1.keyName; + case SignpostState.active2: + return Assets.images.signpost.active2.keyName; + case SignpostState.active3: + return Assets.images.signpost.active3.keyName; + } + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/flutter_forest/signpost_game.dart b/packages/pinball_components/sandbox/lib/stories/flutter_forest/signpost_game.dart index 349dd811..020311d3 100644 --- a/packages/pinball_components/sandbox/lib/stories/flutter_forest/signpost_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/flutter_forest/signpost_game.dart @@ -34,6 +34,6 @@ class SignpostGame extends BallGame { @override void onTap() { super.onTap(); - firstChild()!.progress(); + firstChild()!.bloc.onProgressed(); } } diff --git a/packages/pinball_components/test/src/components/golden/camera_zoom/finished.png b/packages/pinball_components/test/src/components/golden/camera_zoom/finished.png index 1d3daa81..035a152f 100644 Binary files a/packages/pinball_components/test/src/components/golden/camera_zoom/finished.png and b/packages/pinball_components/test/src/components/golden/camera_zoom/finished.png differ diff --git a/packages/pinball_components/test/src/components/golden/camera_zoom/in_between.png b/packages/pinball_components/test/src/components/golden/camera_zoom/in_between.png index f0312ae5..23c1142d 100644 Binary files a/packages/pinball_components/test/src/components/golden/camera_zoom/in_between.png and b/packages/pinball_components/test/src/components/golden/camera_zoom/in_between.png differ diff --git a/packages/pinball_components/test/src/components/golden/camera_zoom/no_zoom.png b/packages/pinball_components/test/src/components/golden/camera_zoom/no_zoom.png index 5fd65077..200ab49f 100644 Binary files a/packages/pinball_components/test/src/components/golden/camera_zoom/no_zoom.png and b/packages/pinball_components/test/src/components/golden/camera_zoom/no_zoom.png differ diff --git a/packages/pinball_components/test/src/components/golden/signpost/active1.png b/packages/pinball_components/test/src/components/golden/signpost/active1.png index f11af5a8..0e0f9e79 100644 Binary files a/packages/pinball_components/test/src/components/golden/signpost/active1.png and b/packages/pinball_components/test/src/components/golden/signpost/active1.png differ diff --git a/packages/pinball_components/test/src/components/golden/signpost/active2.png b/packages/pinball_components/test/src/components/golden/signpost/active2.png index 6ddf8786..9dfae564 100644 Binary files a/packages/pinball_components/test/src/components/golden/signpost/active2.png and b/packages/pinball_components/test/src/components/golden/signpost/active2.png differ diff --git a/packages/pinball_components/test/src/components/golden/signpost/active3.png b/packages/pinball_components/test/src/components/golden/signpost/active3.png index 5e9b0005..a99c9e48 100644 Binary files a/packages/pinball_components/test/src/components/golden/signpost/active3.png and b/packages/pinball_components/test/src/components/golden/signpost/active3.png differ diff --git a/packages/pinball_components/test/src/components/golden/signpost/inactive.png b/packages/pinball_components/test/src/components/golden/signpost/inactive.png index 7ed00fba..7f089716 100644 Binary files a/packages/pinball_components/test/src/components/golden/signpost/inactive.png and b/packages/pinball_components/test/src/components/golden/signpost/inactive.png differ diff --git a/packages/pinball_components/test/src/components/signpost/cubit/signpost_cubit_test.dart b/packages/pinball_components/test/src/components/signpost/cubit/signpost_cubit_test.dart new file mode 100644 index 00000000..081beab2 --- /dev/null +++ b/packages/pinball_components/test/src/components/signpost/cubit/signpost_cubit_test.dart @@ -0,0 +1,39 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +void main() { + group('SignpostCubit', () { + blocTest( + 'onProgressed progresses', + build: SignpostCubit.new, + act: (bloc) { + bloc + ..onProgressed() + ..onProgressed() + ..onProgressed() + ..onProgressed(); + }, + expect: () => [ + SignpostState.active1, + SignpostState.active2, + SignpostState.active3, + SignpostState.inactive, + ], + ); + + test('isFullyProgressed when on active3', () { + final bloc = SignpostCubit(); + expect(bloc.isFullyProgressed(), isFalse); + + bloc.onProgressed(); + expect(bloc.isFullyProgressed(), isFalse); + + bloc.onProgressed(); + expect(bloc.isFullyProgressed(), isFalse); + + bloc.onProgressed(); + expect(bloc.isFullyProgressed(), isTrue); + }); + }); +} diff --git a/packages/pinball_components/test/src/components/signpost_test.dart b/packages/pinball_components/test/src/components/signpost/signpost_test.dart similarity index 64% rename from packages/pinball_components/test/src/components/signpost_test.dart rename to packages/pinball_components/test/src/components/signpost/signpost_test.dart index 23aa6bd0..6aecd0bd 100644 --- a/packages/pinball_components/test/src/components/signpost_test.dart +++ b/packages/pinball_components/test/src/components/signpost/signpost_test.dart @@ -1,11 +1,15 @@ // ignore_for_file: cascade_invocations +import 'package:bloc_test/bloc_test.dart'; import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; -import '../../helpers/helpers.dart'; +import '../../../helpers/helpers.dart'; + +class _MockSignpostCubit extends Mock implements SignpostCubit {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -18,13 +22,13 @@ void main() { final flameTester = FlameTester(() => TestGame(assets)); group('Signpost', () { + const goldenPath = '../golden/signpost/'; + flameTester.test( 'loads correctly', (game) async { final signpost = Signpost(); - await game.ready(); await game.ensureAdd(signpost); - expect(game.contains(signpost), isTrue); }, ); @@ -39,8 +43,8 @@ void main() { await tester.pump(); expect( - signpost.firstChild()!.current, - SignpostSpriteState.inactive, + signpost.bloc.state, + equals(SignpostState.inactive), ); game.camera.followVector2(Vector2.zero()); @@ -48,7 +52,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/signpost/inactive.png'), + matchesGoldenFile('${goldenPath}inactive.png'), ); }, ); @@ -59,12 +63,12 @@ void main() { await game.images.loadAll(assets); final signpost = Signpost(); await game.ensureAdd(signpost); - signpost.progress(); + signpost.bloc.onProgressed(); await tester.pump(); expect( - signpost.firstChild()!.current, - SignpostSpriteState.active1, + signpost.bloc.state, + equals(SignpostState.active1), ); game.camera.followVector2(Vector2.zero()); @@ -72,7 +76,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/signpost/active1.png'), + matchesGoldenFile('${goldenPath}active1.png'), ); }, ); @@ -83,14 +87,14 @@ void main() { await game.images.loadAll(assets); final signpost = Signpost(); await game.ensureAdd(signpost); - signpost - ..progress() - ..progress(); + signpost.bloc + ..onProgressed() + ..onProgressed(); await tester.pump(); expect( - signpost.firstChild()!.current, - SignpostSpriteState.active2, + signpost.bloc.state, + equals(SignpostState.active2), ); game.camera.followVector2(Vector2.zero()); @@ -98,7 +102,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/signpost/active2.png'), + matchesGoldenFile('${goldenPath}active2.png'), ); }, ); @@ -109,15 +113,16 @@ void main() { await game.images.loadAll(assets); final signpost = Signpost(); await game.ensureAdd(signpost); - signpost - ..progress() - ..progress() - ..progress(); + + signpost.bloc + ..onProgressed() + ..onProgressed() + ..onProgressed(); await tester.pump(); expect( - signpost.firstChild()!.current, - SignpostSpriteState.active3, + signpost.bloc.state, + equals(SignpostState.active3), ); game.camera.followVector2(Vector2.zero()); @@ -125,33 +130,12 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/signpost/active3.png'), + matchesGoldenFile('${goldenPath}active3.png'), ); }, ); }); - flameTester.test( - 'progress correctly cycles through all sprites', - (game) async { - final signpost = Signpost(); - await game.ready(); - await game.ensureAdd(signpost); - - final spriteComponent = signpost.firstChild()!; - - expect(spriteComponent.current, SignpostSpriteState.inactive); - signpost.progress(); - expect(spriteComponent.current, SignpostSpriteState.active1); - signpost.progress(); - expect(spriteComponent.current, SignpostSpriteState.active2); - signpost.progress(); - expect(spriteComponent.current, SignpostSpriteState.active3); - signpost.progress(); - expect(spriteComponent.current, SignpostSpriteState.inactive); - }, - ); - flameTester.test('adds new children', (game) async { final component = Component(); final signpost = Signpost( @@ -160,5 +144,22 @@ void main() { await game.ensureAdd(signpost); expect(signpost.children, contains(component)); }); + + flameTester.test('closes bloc when removed', (game) async { + final bloc = _MockSignpostCubit(); + whenListen( + bloc, + const Stream.empty(), + initialState: SignpostState.inactive, + ); + when(bloc.close).thenAnswer((_) async {}); + final component = Signpost.test(bloc: bloc); + + await game.ensureAdd(component); + game.remove(component); + await game.ready(); + + verify(bloc.close).called(1); + }); }); } diff --git a/test/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior_test.dart b/test/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior_test.dart index 3481cb38..2de84edd 100644 --- a/test/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior_test.dart +++ b/test/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior_test.dart @@ -16,6 +16,7 @@ import '../../../../helpers/helpers.dart'; void main() { group('FlutterForestBonusBehavior', () { late GameBloc gameBloc; + final assets = [Assets.images.dash.animatronic.keyName]; setUp(() { gameBloc = MockGameBloc(); @@ -31,9 +32,14 @@ void main() { blocBuilder: () => gameBloc, ); + void _contactedBumper(DashNestBumper bumper) => + bumper.bloc.onBallContacted(); + flameBlocTester.testGameWidget( - 'adds GameBonus.dashNest to the game when all bumpers are active', + 'adds GameBonus.dashNest to the game ' + 'when bumpers are activated three times', setUp: (game, tester) async { + await game.images.loadAll(assets); final behavior = FlutterForestBonusBehavior(); final parent = FlutterForest.test(); final bumpers = [ @@ -41,12 +47,18 @@ void main() { DashNestBumper.test(bloc: DashNestBumperCubit()), DashNestBumper.test(bloc: DashNestBumperCubit()), ]; + final animatronic = DashAnimatronic(); + final signpost = Signpost.test(bloc: SignpostCubit()); await game.ensureAdd(ZCanvasComponent(children: [parent])); - await parent.ensureAddAll([...bumpers, behavior]); + await parent.ensureAddAll([...bumpers, animatronic, signpost]); + await parent.ensureAdd(behavior); - for (final bumper in bumpers) { - bumper.bloc.onBallContacted(); - } + expect(game.descendants().whereType(), equals(bumpers)); + bumpers.forEach(_contactedBumper); + await tester.pump(); + bumpers.forEach(_contactedBumper); + await tester.pump(); + bumpers.forEach(_contactedBumper); await tester.pump(); verify( @@ -56,8 +68,10 @@ void main() { ); flameBlocTester.testGameWidget( - 'adds a new ball to the game when all bumpers are active', + 'adds a new Ball to the game ' + 'when bumpers are activated three times', setUp: (game, tester) async { + await game.images.loadAll(assets); final behavior = FlutterForestBonusBehavior(); final parent = FlutterForest.test(); final bumpers = [ @@ -65,18 +79,68 @@ void main() { DashNestBumper.test(bloc: DashNestBumperCubit()), DashNestBumper.test(bloc: DashNestBumperCubit()), ]; + final animatronic = DashAnimatronic(); + final signpost = Signpost.test(bloc: SignpostCubit()); await game.ensureAdd(ZCanvasComponent(children: [parent])); + await parent.ensureAddAll([...bumpers, animatronic, signpost]); await parent.ensureAdd(behavior); - for (final bumper in bumpers) { - bumper.bloc.onBallContacted(); - } + expect(game.descendants().whereType(), equals(bumpers)); + bumpers.forEach(_contactedBumper); + await tester.pump(); + bumpers.forEach(_contactedBumper); + await tester.pump(); + bumpers.forEach(_contactedBumper); + await tester.pump(); + await game.ready(); + expect( + game.descendants().whereType().length, + equals(1), + ); + }, + ); + + flameBlocTester.testGameWidget( + 'progress the signpost ' + 'when bumpers are activated', + setUp: (game, tester) async { + await game.images.loadAll(assets); + final behavior = FlutterForestBonusBehavior(); + final parent = FlutterForest.test(); + final bumpers = [ + DashNestBumper.test(bloc: DashNestBumperCubit()), + DashNestBumper.test(bloc: DashNestBumperCubit()), + DashNestBumper.test(bloc: DashNestBumperCubit()), + ]; + final animatronic = DashAnimatronic(); + final signpost = Signpost.test(bloc: SignpostCubit()); + await game.ensureAdd(ZCanvasComponent(children: [parent])); + await parent.ensureAddAll([...bumpers, animatronic, signpost]); + await parent.ensureAdd(behavior); + + expect(game.descendants().whereType(), equals(bumpers)); + + bumpers.forEach(_contactedBumper); + await tester.pump(); + expect( + signpost.bloc.state, + equals(SignpostState.active1), + ); - // expect( - // game.descendants().whereType().single, - // isNotNull, - // ); + bumpers.forEach(_contactedBumper); + await tester.pump(); + expect( + signpost.bloc.state, + equals(SignpostState.active2), + ); + + bumpers.forEach(_contactedBumper); + await tester.pump(); + expect( + signpost.bloc.state, + equals(SignpostState.inactive), + ); }, ); }); diff --git a/test/game/components/flutter_forest/flutter_forest_test.dart b/test/game/components/flutter_forest/flutter_forest_test.dart index 4f32e0f4..5761a9eb 100644 --- a/test/game/components/flutter_forest/flutter_forest_test.dart +++ b/test/game/components/flutter_forest/flutter_forest_test.dart @@ -4,6 +4,7 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import '../../../helpers/helpers.dart'; @@ -31,8 +32,8 @@ void main() { 'loads correctly', (game) async { final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); - expect(game.contains(flutterForest), isTrue); + await game.ensureAdd(ZCanvasComponent(children: [flutterForest])); + expect(game.descendants(), contains(flutterForest)); }, ); @@ -41,10 +42,9 @@ void main() { 'a Signpost', (game) async { final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); - + await game.ensureAdd(ZCanvasComponent(children: [flutterForest])); expect( - flutterForest.descendants().whereType().length, + game.descendants().whereType().length, equals(1), ); }, @@ -54,11 +54,10 @@ void main() { 'a DashAnimatronic', (game) async { final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); - + await game.ensureAdd(ZCanvasComponent(children: [flutterForest])); expect( - flutterForest.firstChild(), - isNotNull, + game.descendants().whereType().length, + equals(1), ); }, ); @@ -67,10 +66,9 @@ void main() { 'three DashNestBumper', (game) async { final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); - + await game.ensureAdd(ZCanvasComponent(children: [flutterForest])); expect( - flutterForest.descendants().whereType().length, + game.descendants().whereType().length, equals(3), ); },