From ca6b8cc1b37c83c6e9667eb6d1c380ead594a451 Mon Sep 17 00:00:00 2001 From: Felix Angelov Date: Sat, 7 May 2022 14:27:31 -0500 Subject: [PATCH] refactor: AndroidSpaceshipBonusBehavior use FlameBlocListener (#371) --- .../android_acres/android_acres.dart | 66 ++++++++++--------- .../android_spaceship_bonus_behavior.dart | 27 ++++---- .../android_spaceship/android_spaceship.dart | 15 +---- ...p_entrance_ball_contact_behavior.dart.dart | 8 ++- .../android_spaceship_test.dart | 45 +++++++------ ...p_entrance_ball_contact_behavior_test.dart | 12 ++-- .../android_acres/android_acres_test.dart | 19 +++++- ...android_spaceship_bonus_behavior_test.dart | 32 +++++++-- 8 files changed, 135 insertions(+), 89 deletions(-) diff --git a/lib/game/components/android_acres/android_acres.dart b/lib/game/components/android_acres/android_acres.dart index 7f9fff13..902eb11c 100644 --- a/lib/game/components/android_acres/android_acres.dart +++ b/lib/game/components/android_acres/android_acres.dart @@ -1,6 +1,7 @@ // ignore_for_file: avoid_renaming_method_parameters import 'package:flame/components.dart'; +import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/material.dart'; import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/components/android_acres/behaviors/behaviors.dart'; @@ -15,42 +16,43 @@ class AndroidAcres extends Component { AndroidAcres() : super( children: [ - SpaceshipRamp( + FlameBlocProvider( + create: AndroidSpaceshipCubit.new, children: [ - RampShotBehavior( - points: Points.fiveThousand, - ), - RampBonusBehavior( - points: Points.oneMillion, + SpaceshipRamp( + children: [ + RampShotBehavior(points: Points.fiveThousand), + RampBonusBehavior(points: Points.oneMillion), + ], ), + SpaceshipRail(), + AndroidSpaceship(position: Vector2(-26.5, -28.5)), + AndroidAnimatronic( + children: [ + ScoringContactBehavior(points: Points.twoHundredThousand), + ], + )..initialPosition = Vector2(-26, -28.25), + AndroidBumper.a( + children: [ + ScoringContactBehavior(points: Points.twentyThousand), + BumperNoiseBehavior(), + ], + )..initialPosition = Vector2(-25.2, 1.5), + AndroidBumper.b( + children: [ + ScoringContactBehavior(points: Points.twentyThousand), + BumperNoiseBehavior(), + ], + )..initialPosition = Vector2(-32.9, -9.3), + AndroidBumper.cow( + children: [ + ScoringContactBehavior(points: Points.twentyThousand), + BumperNoiseBehavior(), + ], + )..initialPosition = Vector2(-20.7, -13), + AndroidSpaceshipBonusBehavior(), ], ), - SpaceshipRail(), - AndroidSpaceship(position: Vector2(-26.5, -28.5)), - AndroidAnimatronic( - children: [ - ScoringContactBehavior(points: Points.twoHundredThousand), - ], - )..initialPosition = Vector2(-26, -28.25), - AndroidBumper.a( - children: [ - ScoringContactBehavior(points: Points.twentyThousand), - BumperNoiseBehavior(), - ], - )..initialPosition = Vector2(-25.2, 1.5), - AndroidBumper.b( - children: [ - ScoringContactBehavior(points: Points.twentyThousand), - BumperNoiseBehavior(), - ], - )..initialPosition = Vector2(-32.9, -9.3), - AndroidBumper.cow( - children: [ - ScoringContactBehavior(points: Points.twentyThousand), - BumperNoiseBehavior(), - ], - )..initialPosition = Vector2(-20.7, -13), - AndroidSpaceshipBonusBehavior(), ], ); diff --git a/lib/game/components/android_acres/behaviors/android_spaceship_bonus_behavior.dart b/lib/game/components/android_acres/behaviors/android_spaceship_bonus_behavior.dart index cbb6e516..3b6f59ec 100644 --- a/lib/game/components/android_acres/behaviors/android_spaceship_bonus_behavior.dart +++ b/lib/game/components/android_acres/behaviors/android_spaceship_bonus_behavior.dart @@ -5,18 +5,21 @@ import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; /// Adds a [GameBonus.androidSpaceship] when [AndroidSpaceship] has a bonus. -class AndroidSpaceshipBonusBehavior extends Component - with ParentIsA, FlameBlocReader { +class AndroidSpaceshipBonusBehavior extends Component { @override - void onMount() { - super.onMount(); - final androidSpaceship = parent.firstChild()!; - androidSpaceship.bloc.stream.listen((state) { - final listenWhen = state == AndroidSpaceshipState.withBonus; - if (!listenWhen) return; - - bloc.add(const BonusActivated(GameBonus.androidSpaceship)); - androidSpaceship.bloc.onBonusAwarded(); - }); + Future onLoad() async { + await super.onLoad(); + await add( + FlameBlocListener( + listenWhen: (_, state) => state == AndroidSpaceshipState.withBonus, + onNewState: (state) { + readBloc().add( + const BonusActivated(GameBonus.androidSpaceship), + ); + readBloc() + .onBonusAwarded(); + }, + ), + ); } } diff --git a/packages/pinball_components/lib/src/components/android_spaceship/android_spaceship.dart b/packages/pinball_components/lib/src/components/android_spaceship/android_spaceship.dart index b3c721a1..0fd4628d 100644 --- a/packages/pinball_components/lib/src/components/android_spaceship/android_spaceship.dart +++ b/packages/pinball_components/lib/src/components/android_spaceship/android_spaceship.dart @@ -11,10 +11,8 @@ import 'package:pinball_flame/pinball_flame.dart'; export 'cubit/android_spaceship_cubit.dart'; class AndroidSpaceship extends Component { - AndroidSpaceship({ - required Vector2 position, - }) : bloc = AndroidSpaceshipCubit(), - super( + AndroidSpaceship({required Vector2 position}) + : super( children: [ _SpaceshipSaucer()..initialPosition = position, _SpaceshipSaucerSpriteAnimationComponent()..position = position, @@ -38,17 +36,8 @@ class AndroidSpaceship extends Component { /// This can be used for testing [AndroidSpaceship]'s behaviors in isolation. @visibleForTesting AndroidSpaceship.test({ - required this.bloc, Iterable? children, }) : super(children: children); - - final AndroidSpaceshipCubit bloc; - - @override - void onRemove() { - bloc.close(); - super.onRemove(); - } } class _SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { diff --git a/packages/pinball_components/lib/src/components/android_spaceship/behaviors/android_spaceship_entrance_ball_contact_behavior.dart.dart b/packages/pinball_components/lib/src/components/android_spaceship/behaviors/android_spaceship_entrance_ball_contact_behavior.dart.dart index de5ed1ff..b577b7b3 100644 --- a/packages/pinball_components/lib/src/components/android_spaceship/behaviors/android_spaceship_entrance_ball_contact_behavior.dart.dart +++ b/packages/pinball_components/lib/src/components/android_spaceship/behaviors/android_spaceship_entrance_ball_contact_behavior.dart.dart @@ -1,14 +1,18 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; class AndroidSpaceshipEntranceBallContactBehavior - extends ContactBehavior { + extends ContactBehavior + with FlameBlocReader { @override void beginContact(Object other, Contact contact) { super.beginContact(other, contact); if (other is! Ball) return; - parent.parent.bloc.onBallEntered(); + bloc.onBallEntered(); } } diff --git a/packages/pinball_components/test/src/components/android_spaceship/android_spaceship_test.dart b/packages/pinball_components/test/src/components/android_spaceship/android_spaceship_test.dart index fff69b0a..70edd32e 100644 --- a/packages/pinball_components/test/src/components/android_spaceship/android_spaceship_test.dart +++ b/packages/pinball_components/test/src/components/android_spaceship/android_spaceship_test.dart @@ -1,7 +1,7 @@ // ignore_for_file: cascade_invocations -import 'package:bloc_test/bloc_test.dart'; import 'package:flame/components.dart'; +import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -21,9 +21,18 @@ void main() { Assets.images.android.spaceship.lightBeam.keyName, ]; final flameTester = FlameTester(() => TestGame(assets)); + late AndroidSpaceshipCubit bloc; + + setUp(() { + bloc = _MockAndroidSpaceshipCubit(); + }); flameTester.test('loads correctly', (game) async { - final component = AndroidSpaceship(position: Vector2.zero()); + final component = + FlameBlocProvider.value( + value: bloc, + children: [AndroidSpaceship(position: Vector2.zero())], + ); await game.ensureAdd(component); expect(game.contains(component), isTrue); }); @@ -33,7 +42,13 @@ void main() { setUp: (game, tester) async { await game.images.loadAll(assets); final canvas = ZCanvasComponent( - children: [AndroidSpaceship(position: Vector2.zero())], + children: [ + FlameBlocProvider.value( + value: bloc, + children: [AndroidSpaceship(position: Vector2.zero())], + ), + ], ); await game.ensureAdd(canvas); game.camera.followVector2(Vector2.zero()); @@ -70,28 +85,16 @@ void main() { }, ); - flameTester.test('closes bloc when removed', (game) async { - final bloc = _MockAndroidSpaceshipCubit(); - whenListen( - bloc, - const Stream.empty(), - initialState: AndroidSpaceshipState.withoutBonus, - ); - when(bloc.close).thenAnswer((_) async {}); - final androidSpaceship = AndroidSpaceship.test(bloc: bloc); - - await game.ensureAdd(androidSpaceship); - game.remove(androidSpaceship); - await game.ready(); - - verify(bloc.close).called(1); - }); - flameTester.test( 'AndroidSpaceshipEntrance has an ' 'AndroidSpaceshipEntranceBallContactBehavior', (game) async { final androidSpaceship = AndroidSpaceship(position: Vector2.zero()); - await game.ensureAdd(androidSpaceship); + final provider = + FlameBlocProvider.value( + value: bloc, + children: [androidSpaceship], + ); + await game.ensureAdd(provider); final androidSpaceshipEntrance = androidSpaceship.firstChild(); diff --git a/packages/pinball_components/test/src/components/android_spaceship/behaviors/android_spaceship_entrance_ball_contact_behavior_test.dart b/packages/pinball_components/test/src/components/android_spaceship/behaviors/android_spaceship_entrance_ball_contact_behavior_test.dart index d6056beb..4b0f16ea 100644 --- a/packages/pinball_components/test/src/components/android_spaceship/behaviors/android_spaceship_entrance_ball_contact_behavior_test.dart +++ b/packages/pinball_components/test/src/components/android_spaceship/behaviors/android_spaceship_entrance_ball_contact_behavior_test.dart @@ -1,6 +1,7 @@ // ignore_for_file: cascade_invocations import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -43,16 +44,19 @@ void main() { ); final entrance = AndroidSpaceshipEntrance(); - final androidSpaceship = AndroidSpaceship.test( - bloc: bloc, - children: [entrance], + final androidSpaceship = FlameBlocProvider.value( + value: bloc, + children: [ + AndroidSpaceship.test(children: [entrance]) + ], ); await entrance.add(behavior); await game.ensureAdd(androidSpaceship); behavior.beginContact(_MockBall(), _MockContact()); - verify(androidSpaceship.bloc.onBallEntered).called(1); + verify(bloc.onBallEntered).called(1); }, ); }, diff --git a/test/game/components/android_acres/android_acres_test.dart b/test/game/components/android_acres/android_acres_test.dart index e88d1608..5bf22da9 100644 --- a/test/game/components/android_acres/android_acres_test.dart +++ b/test/game/components/android_acres/android_acres_test.dart @@ -131,11 +131,28 @@ void main() { ); }); + flameTester.test('adds a FlameBlocProvider', (game) async { + final androidAcres = AndroidAcres(); + await game.pump(androidAcres); + expect( + androidAcres.children + .whereType< + FlameBlocProvider>() + .single, + isNotNull, + ); + }); + flameTester.test('adds an AndroidSpaceshipBonusBehavior', (game) async { final androidAcres = AndroidAcres(); await game.pump(androidAcres); + final provider = androidAcres.children + .whereType< + FlameBlocProvider>() + .single; expect( - androidAcres.children.whereType().single, + provider.children.whereType().single, isNotNull, ); }); diff --git a/test/game/components/android_acres/behaviors/android_spaceship_bonus_behavior_test.dart b/test/game/components/android_acres/behaviors/android_spaceship_bonus_behavior_test.dart index b9345c27..7f907985 100644 --- a/test/game/components/android_acres/behaviors/android_spaceship_bonus_behavior_test.dart +++ b/test/game/components/android_acres/behaviors/android_spaceship_bonus_behavior_test.dart @@ -1,5 +1,8 @@ // ignore_for_file: cascade_invocations +import 'dart:async'; + +import 'package:bloc_test/bloc_test.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; @@ -41,13 +44,21 @@ class _TestGame extends Forge2DGame { Future pump( AndroidAcres child, { required GameBloc gameBloc, + required AndroidSpaceshipCubit androidSpaceshipCubit, }) async { // Not needed once https://github.com/flame-engine/flame/issues/1607 // is fixed await onLoad(); await ensureAdd( - FlameBlocProvider.value( - value: gameBloc, + FlameMultiBlocProvider( + providers: [ + FlameBlocProvider.value( + value: gameBloc, + ), + FlameBlocProvider.value( + value: androidSpaceshipCubit, + ), + ], children: [child], ), ); @@ -56,6 +67,9 @@ class _TestGame extends Forge2DGame { class _MockGameBloc extends Mock implements GameBloc {} +class _MockAndroidSpaceshipCubit extends Mock implements AndroidSpaceshipCubit { +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -70,21 +84,31 @@ void main() { flameTester.testGameWidget( 'adds GameBonus.androidSpaceship to the game ' - 'when android spacehship has a bonus', + 'when android spaceship has a bonus', setUp: (game, tester) async { await game.onLoad(); final behavior = AndroidSpaceshipBonusBehavior(); final parent = AndroidAcres.test(); final androidSpaceship = AndroidSpaceship(position: Vector2.zero()); + final androidSpaceshipCubit = _MockAndroidSpaceshipCubit(); + final streamController = StreamController(); + + whenListen( + androidSpaceshipCubit, + streamController.stream, + initialState: AndroidSpaceshipState.withoutBonus, + ); await parent.add(androidSpaceship); await game.pump( parent, + androidSpaceshipCubit: androidSpaceshipCubit, gameBloc: gameBloc, ); await parent.ensureAdd(behavior); - androidSpaceship.bloc.onBallEntered(); + streamController.add(AndroidSpaceshipState.withBonus); + await tester.pump(); verify(