From aabc4f21af917b6a26f131a89a0edb8c17bcc6d7 Mon Sep 17 00:00:00 2001 From: alestiago Date: Wed, 27 Apr 2022 14:31:15 +0100 Subject: [PATCH] feat: implemented FlutterForestBonusBehavior --- lib/game/components/components.dart | 2 +- lib/game/components/flutter_forest.dart | 78 ------------------- .../flutter_forest/behaviors/behaviors.dart | 1 + .../flutter_forest_bonus_behavior.dart | 44 +++++++++++ .../cubit/flutter_forest_cubit.dart | 23 ++++++ .../cubit/flutter_forest_state.dart | 26 +++++++ .../flutter_forest/flutter_forest.dart | 62 +++++++++++++++ lib/game/pinball_game.dart | 2 +- .../cubit/alien_bumper_cubit.dart | 4 +- .../lib/src/components/components.dart | 2 +- .../dash_nest_bumper/behaviors/behaviors.dart | 1 + .../dash_nest_bumper_contact_behavior.dart | 15 ++++ .../cubit/dash_nest_bumper_cubit.dart | 21 +++++ .../cubit/dash_nest_bumper_state.dart | 10 +++ .../dash_nest_bumper.dart | 62 ++++++++------- .../cubit/google_letter_cubit.dart | 4 +- .../cubit/sparky_bumper_cubit.dart | 4 +- .../sparky_bumper/sparky_bumper.dart | 3 + .../src/components/dash_nest_bumper_test.dart | 34 -------- ...sparky_bumper_blinking_behavior_test.dart} | 0 .../test/src/contact_behavior_test.dart | 4 +- test/game/components/alien_zone_test.dart | 32 ++------ test/game/components/flutter_forest_test.dart | 67 ---------------- ...r_test.dart => scoring_behavior_test.dart} | 17 +++- .../components/sparky_fire_zone_test.dart | 47 ++++------- test/game/pinball_game_test.dart | 10 +-- 26 files changed, 289 insertions(+), 286 deletions(-) delete mode 100644 lib/game/components/flutter_forest.dart create mode 100644 lib/game/components/flutter_forest/behaviors/behaviors.dart create mode 100644 lib/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior.dart create mode 100644 lib/game/components/flutter_forest/cubit/flutter_forest_cubit.dart create mode 100644 lib/game/components/flutter_forest/cubit/flutter_forest_state.dart create mode 100644 lib/game/components/flutter_forest/flutter_forest.dart create mode 100644 packages/pinball_components/lib/src/components/dash_nest_bumper/behaviors/behaviors.dart create mode 100644 packages/pinball_components/lib/src/components/dash_nest_bumper/behaviors/dash_nest_bumper_contact_behavior.dart create mode 100644 packages/pinball_components/lib/src/components/dash_nest_bumper/cubit/dash_nest_bumper_cubit.dart create mode 100644 packages/pinball_components/lib/src/components/dash_nest_bumper/cubit/dash_nest_bumper_state.dart rename packages/pinball_components/lib/src/components/{ => dash_nest_bumper}/dash_nest_bumper.dart (75%) rename packages/pinball_components/test/src/components/sparky_bumper/behaviors/{sparky_bumper_blinking_behaviour_test.dart => sparky_bumper_blinking_behavior_test.dart} (100%) rename test/game/components/{scoring_behaviour_test.dart => scoring_behavior_test.dart} (86%) diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 17dc9ad7..321be988 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -4,7 +4,7 @@ export 'camera_controller.dart'; export 'controlled_ball.dart'; export 'controlled_flipper.dart'; export 'controlled_plunger.dart'; -export 'flutter_forest.dart'; +export 'flutter_forest/flutter_forest.dart'; export 'game_flow_controller.dart'; export 'google_word/google_word.dart'; export 'launcher.dart'; diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart deleted file mode 100644 index 243fcceb..00000000 --- a/lib/game/components/flutter_forest.dart +++ /dev/null @@ -1,78 +0,0 @@ -// ignore_for_file: avoid_renaming_method_parameters - -import 'package:flame/components.dart'; -import 'package:pinball/game/game.dart'; -import 'package:pinball_components/pinball_components.dart'; -import 'package:pinball_flame/pinball_flame.dart'; - -/// {@template flutter_forest} -/// Area positioned at the top right of the [Board] where the [Ball] -/// can bounce off [DashNestBumper]s. -/// -/// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest] -/// is awarded, and the [DashNestBumper.main] releases a new [Ball]. -/// {@endtemplate} -class FlutterForest extends Component - with Controls<_FlutterForestController>, HasGameRef { - /// {@macro flutter_forest} - FlutterForest() - : super( - children: [ - Signpost( - children: [ - ScoringBehavior(points: 20), - ], - )..initialPosition = Vector2(8.35, -58.3), - DashNestBumper.main( - children: [ - ScoringBehavior(points: 20), - ], - )..initialPosition = Vector2(18.55, -59.35), - DashNestBumper.a( - children: [ - ScoringBehavior(points: 20), - ], - )..initialPosition = Vector2(8.95, -51.95), - DashNestBumper.b( - children: [ - ScoringBehavior(points: 20), - ], - )..initialPosition = Vector2(23.3, -46.75), - DashAnimatronic()..position = Vector2(20, -66), - ], - ) { - controller = _FlutterForestController(this); - } -} - -class _FlutterForestController extends ComponentController - with HasGameRef { - _FlutterForestController(FlutterForest flutterForest) : super(flutterForest); - - final _activatedBumpers = {}; - - void activateBumper(DashNestBumper dashNestBumper) { - if (!_activatedBumpers.add(dashNestBumper)) return; - - dashNestBumper.activate(); - - final activatedBonus = _activatedBumpers.length == 3; - if (activatedBonus) { - _addBonusBall(); - - gameRef.read().add(const BonusActivated(GameBonus.dashNest)); - _activatedBumpers - ..forEach((bumper) => bumper.deactivate()) - ..clear(); - - component.firstChild()?.playing = true; - } - } - - Future _addBonusBall() async { - await gameRef.add( - ControlledBall.bonus(characterTheme: gameRef.characterTheme) - ..initialPosition = Vector2(17.2, -52.7), - ); - } -} diff --git a/lib/game/components/flutter_forest/behaviors/behaviors.dart b/lib/game/components/flutter_forest/behaviors/behaviors.dart new file mode 100644 index 00000000..c0f39810 --- /dev/null +++ b/lib/game/components/flutter_forest/behaviors/behaviors.dart @@ -0,0 +1 @@ +export 'flutter_forest_bonus_behavior.dart'; 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 new file mode 100644 index 00000000..eb651715 --- /dev/null +++ b/lib/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior.dart @@ -0,0 +1,44 @@ +import 'package:flame/components.dart'; +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] +/// is awarded, and the [DashNestBumper.main] releases a new [Ball]. +class FlutterForestBonusBehavior extends Component + with ParentIsA, HasGameRef { + @override + void onMount() { + super.onMount(); + // TODO(alestiago): Refactor subscription management once the following is + // merged: + // https://github.com/flame-engine/flame/pull/1538 + parent.bloc.stream.listen(_onNewState); + + final bumpers = parent.children.whereType(); + for (final bumper in bumpers) { + bumper.bloc.stream.listen((state) { + if (state == DashNestBumperState.active) { + parent.bloc.onBumperActivated(bumper); + } + }); + } + } + + void _onNewState(FlutterForestState state) { + if (state.hasBonus) { + gameRef.read().add(const BonusActivated(GameBonus.dashNest)); + + gameRef.add( + ControlledBall.bonus(theme: gameRef.theme) + ..initialPosition = Vector2(17.2, -52.7), + ); + parent.firstChild()?.playing = true; + for (final bumper in state.activatedBumpers) { + bumper.bloc.onReset(); + } + + parent.bloc.onBonusApplied(); + } + } +} diff --git a/lib/game/components/flutter_forest/cubit/flutter_forest_cubit.dart b/lib/game/components/flutter_forest/cubit/flutter_forest_cubit.dart new file mode 100644 index 00000000..67ac4a4e --- /dev/null +++ b/lib/game/components/flutter_forest/cubit/flutter_forest_cubit.dart @@ -0,0 +1,23 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:pinball_components/pinball_components.dart'; + +part 'flutter_forest_state.dart'; + +class FlutterForestCubit extends Cubit { + FlutterForestCubit() : super(FlutterForestState.initial()); + + void onBumperActivated(DashNestBumper dashNestBumper) { + emit( + state.copyWith( + activatedBumpers: state.activatedBumpers.union({dashNestBumper}), + ), + ); + } + + void onBonusApplied() { + emit( + state.copyWith(activatedBumpers: const {}), + ); + } +} diff --git a/lib/game/components/flutter_forest/cubit/flutter_forest_state.dart b/lib/game/components/flutter_forest/cubit/flutter_forest_state.dart new file mode 100644 index 00000000..d99a2986 --- /dev/null +++ b/lib/game/components/flutter_forest/cubit/flutter_forest_state.dart @@ -0,0 +1,26 @@ +// ignore_for_file: public_member_api_docs + +part of 'flutter_forest_cubit.dart'; + +class FlutterForestState extends Equatable { + const FlutterForestState({ + required this.activatedBumpers, + }); + + const FlutterForestState.initial() : this(activatedBumpers: const {}); + + final Set activatedBumpers; + + bool get hasBonus => activatedBumpers.length == 3; + + FlutterForestState copyWith({ + Set? activatedBumpers, + }) { + return FlutterForestState( + activatedBumpers: activatedBumpers ?? this.activatedBumpers, + ); + } + + @override + List get props => [activatedBumpers]; +} diff --git a/lib/game/components/flutter_forest/flutter_forest.dart b/lib/game/components/flutter_forest/flutter_forest.dart new file mode 100644 index 00000000..c6824e89 --- /dev/null +++ b/lib/game/components/flutter_forest/flutter_forest.dart @@ -0,0 +1,62 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:flame/components.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball/game/components/flutter_forest/behaviors/behaviors.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; + +export 'cubit/flutter_forest_cubit.dart'; + +/// {@template flutter_forest} +/// Area positioned at the top right of the [Board] where the [Ball] +/// can bounce off [DashNestBumper]s. +/// {@endtemplate} +class FlutterForest extends Component { + /// {@macro flutter_forest} + FlutterForest() + : bloc = FlutterForestCubit(), + super( + children: [ + Signpost( + children: [ + ScoringBehavior(points: 20), + ], + )..initialPosition = Vector2(8.35, -58.3), + DashNestBumper.main( + children: [ + ScoringBehavior(points: 20), + ], + )..initialPosition = Vector2(18.55, -59.35), + DashNestBumper.a( + children: [ + ScoringBehavior(points: 20), + ], + )..initialPosition = Vector2(8.95, -51.95), + DashNestBumper.b( + children: [ + ScoringBehavior(points: 20), + ], + )..initialPosition = Vector2(23.3, -46.75), + DashAnimatronic()..position = Vector2(20, -66), + FlutterForestBonusBehavior(), + ], + ); + + /// {@macro flutter_forest} + @visibleForTesting + FlutterForest.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 FlutterForestCubit bloc; + + @override + void onRemove() { + bloc.close(); + super.onRemove(); + } +} diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 1d483fe2..5839a2ac 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -139,7 +139,7 @@ class DebugPinballGame extends PinballGame with FPSCounter, TapDetector { @override Future onLoad() async { await super.onLoad(); - await _loadBackground(); + // await _loadBackground(); await add(_DebugInformation()); } diff --git a/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_cubit.dart b/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_cubit.dart index d1213f85..82478e10 100644 --- a/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_cubit.dart +++ b/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_cubit.dart @@ -9,12 +9,12 @@ class AlienBumperCubit extends Cubit { // ignore: public_member_api_docs AlienBumperCubit() : super(AlienBumperState.active); - /// Event added when a bumper contacts with a ball. + /// Event added when the bumper contacts with a ball. void onBallContacted() { emit(AlienBumperState.inactive); } - /// Event added when a bumper finishes blinking. + /// Event added when the bumper finishes blinking. void onBlinked() { emit(AlienBumperState.active); } diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index febbbe06..5d6f5744 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -8,7 +8,7 @@ export 'boundaries.dart'; export 'camera_zoom.dart'; export 'chrome_dino.dart'; export 'dash_animatronic.dart'; -export 'dash_nest_bumper.dart'; +export 'dash_nest_bumper/dash_nest_bumper.dart'; export 'dino_walls.dart'; export 'fire_effect.dart'; export 'flipper.dart'; diff --git a/packages/pinball_components/lib/src/components/dash_nest_bumper/behaviors/behaviors.dart b/packages/pinball_components/lib/src/components/dash_nest_bumper/behaviors/behaviors.dart new file mode 100644 index 00000000..839cbd67 --- /dev/null +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper/behaviors/behaviors.dart @@ -0,0 +1 @@ +export 'dash_nest_bumper_contact_behavior.dart'; diff --git a/packages/pinball_components/lib/src/components/dash_nest_bumper/behaviors/dash_nest_bumper_contact_behavior.dart b/packages/pinball_components/lib/src/components/dash_nest_bumper/behaviors/dash_nest_bumper_contact_behavior.dart new file mode 100644 index 00000000..09666492 --- /dev/null +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper/behaviors/dash_nest_bumper_contact_behavior.dart @@ -0,0 +1,15 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +// TODO(alestiago): Evaluate if there is any useful documentation that could +// be added to this class. +// ignore: public_member_api_docs +class DashBumperBallContactBehavior extends ContactBehavior { + @override + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + if (other is! Ball) return; + parent.bloc.onBallContacted(); + } +} diff --git a/packages/pinball_components/lib/src/components/dash_nest_bumper/cubit/dash_nest_bumper_cubit.dart b/packages/pinball_components/lib/src/components/dash_nest_bumper/cubit/dash_nest_bumper_cubit.dart new file mode 100644 index 00000000..729877c5 --- /dev/null +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper/cubit/dash_nest_bumper_cubit.dart @@ -0,0 +1,21 @@ +import 'package:bloc/bloc.dart'; + +part 'dash_nest_bumper_state.dart'; + +// TODO(alestiago): Evaluate if there is any useful documentation that could +// be added to this class. +// ignore: public_member_api_docs +class DashNestBumperCubit extends Cubit { + // ignore: public_member_api_docs + DashNestBumperCubit() : super(DashNestBumperState.inactive); + + /// Event added when the bumper contacts with a ball. + void onBallContacted() { + emit(DashNestBumperState.active); + } + + /// Event added when the bumper should return to its initial configuration. + void onReset() { + emit(DashNestBumperState.inactive); + } +} diff --git a/packages/pinball_components/lib/src/components/dash_nest_bumper/cubit/dash_nest_bumper_state.dart b/packages/pinball_components/lib/src/components/dash_nest_bumper/cubit/dash_nest_bumper_state.dart new file mode 100644 index 00000000..c169069f --- /dev/null +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper/cubit/dash_nest_bumper_state.dart @@ -0,0 +1,10 @@ +part of 'dash_nest_bumper_cubit.dart'; + +/// Indicates the [DashNestBumperCubit]'s current state. +enum DashNestBumperState { + /// A lit up bumper. + active, + + /// A dimmed bumper. + inactive, +} diff --git a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart b/packages/pinball_components/lib/src/components/dash_nest_bumper/dash_nest_bumper.dart similarity index 75% rename from packages/pinball_components/lib/src/components/dash_nest_bumper.dart rename to packages/pinball_components/lib/src/components/dash_nest_bumper/dash_nest_bumper.dart index 44402e3d..a1caf04e 100644 --- a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper/dash_nest_bumper.dart @@ -4,6 +4,10 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_components/src/components/dash_nest_bumper/behaviors/behaviors.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +export 'cubit/dash_nest_bumper_cubit.dart'; /// {@template dash_nest_bumper} /// Bumper with a nest appearance. @@ -17,6 +21,7 @@ class DashNestBumper extends BodyComponent with InitialPosition { required String inactiveAssetPath, required Vector2 spritePosition, Iterable? children, + required this.bloc, }) : _majorRadius = majorRadius, _minorRadius = minorRadius, super( @@ -26,7 +31,9 @@ class DashNestBumper extends BodyComponent with InitialPosition { activeAssetPath: activeAssetPath, inactiveAssetPath: inactiveAssetPath, position: spritePosition, + current: bloc.state, ), + DashBumperBallContactBehavior(), ...?children, ], renderBody: false, @@ -42,6 +49,7 @@ class DashNestBumper extends BodyComponent with InitialPosition { inactiveAssetPath: Assets.images.dash.bumper.main.inactive.keyName, spritePosition: Vector2(0, -0.3), children: children, + bloc: DashNestBumperCubit(), ); /// {@macro dash_nest_bumper} @@ -54,6 +62,7 @@ class DashNestBumper extends BodyComponent with InitialPosition { inactiveAssetPath: Assets.images.dash.bumper.a.inactive.keyName, spritePosition: Vector2(0.35, -1.2), children: children, + bloc: DashNestBumperCubit(), ); /// {@macro dash_nest_bumper} @@ -66,11 +75,29 @@ class DashNestBumper extends BodyComponent with InitialPosition { inactiveAssetPath: Assets.images.dash.bumper.b.inactive.keyName, spritePosition: Vector2(0.35, -1.2), children: children, + bloc: DashNestBumperCubit(), ); + /// {@macro dash_nest_bumper} + @visibleForTesting + DashNestBumper.test({required this.bloc}) + : _majorRadius = 3, + _minorRadius = 2.5; + final double _majorRadius; final double _minorRadius; + // TODO(alestiago): Consider refactoring once the following is merged: + // https://github.com/flame-engine/flame/pull/1538 + // ignore: public_member_api_docs + final DashNestBumperCubit bloc; + + @override + void onRemove() { + bloc.close(); + super.onRemove(); + } + @override Body createBody() { final shape = EllipseShape( @@ -86,41 +113,22 @@ class DashNestBumper extends BodyComponent with InitialPosition { return world.createBody(bodyDef)..createFixture(fixtureDef); } - - /// Activates the [DashNestBumper]. - void activate() { - firstChild<_DashNestBumperSpriteGroupComponent>()?.current = - DashNestBumperSpriteState.active; - } - - /// Deactivates the [DashNestBumper]. - void deactivate() { - firstChild<_DashNestBumperSpriteGroupComponent>()?.current = - DashNestBumperSpriteState.inactive; - } -} - -/// Indicates the [DashNestBumper]'s current sprite state. -@visibleForTesting -enum DashNestBumperSpriteState { - /// A lit up bumper. - active, - - /// A dimmed bumper. - inactive, } class _DashNestBumperSpriteGroupComponent - extends SpriteGroupComponent with HasGameRef { + extends SpriteGroupComponent + with HasGameRef, ParentIsA { _DashNestBumperSpriteGroupComponent({ required String activeAssetPath, required String inactiveAssetPath, required Vector2 position, + required DashNestBumperState current, }) : _activeAssetPath = activeAssetPath, _inactiveAssetPath = inactiveAssetPath, super( anchor: Anchor.center, position: position, + current: current, ); final String _activeAssetPath; @@ -129,15 +137,15 @@ class _DashNestBumperSpriteGroupComponent @override Future onLoad() async { await super.onLoad(); + parent.bloc.stream.listen((state) => current = state); + final sprites = { - DashNestBumperSpriteState.active: + DashNestBumperState.active: Sprite(gameRef.images.fromCache(_activeAssetPath)), - DashNestBumperSpriteState.inactive: + DashNestBumperState.inactive: Sprite(gameRef.images.fromCache(_inactiveAssetPath)), }; this.sprites = sprites; - - current = DashNestBumperSpriteState.inactive; size = sprites[current]!.originalSize / 10; } } diff --git a/packages/pinball_components/lib/src/components/google_letter/cubit/google_letter_cubit.dart b/packages/pinball_components/lib/src/components/google_letter/cubit/google_letter_cubit.dart index 9ad2f1e4..79b3d6cc 100644 --- a/packages/pinball_components/lib/src/components/google_letter/cubit/google_letter_cubit.dart +++ b/packages/pinball_components/lib/src/components/google_letter/cubit/google_letter_cubit.dart @@ -9,12 +9,12 @@ class GoogleLetterCubit extends Cubit { // ignore: public_member_api_docs GoogleLetterCubit() : super(GoogleLetterState.inactive); - /// Event added when a letter contacts with a ball. + /// Event added when the letter contacts with a ball. void onBallContacted() { emit(GoogleLetterState.active); } - /// Event added when a letter should return to its initial configuration. + /// Event added when the letter should return to its initial configuration. void onReset() { emit(GoogleLetterState.inactive); } diff --git a/packages/pinball_components/lib/src/components/sparky_bumper/cubit/sparky_bumper_cubit.dart b/packages/pinball_components/lib/src/components/sparky_bumper/cubit/sparky_bumper_cubit.dart index fe440d82..183dc0ce 100644 --- a/packages/pinball_components/lib/src/components/sparky_bumper/cubit/sparky_bumper_cubit.dart +++ b/packages/pinball_components/lib/src/components/sparky_bumper/cubit/sparky_bumper_cubit.dart @@ -9,12 +9,12 @@ class SparkyBumperCubit extends Cubit { // ignore: public_member_api_docs SparkyBumperCubit() : super(SparkyBumperState.active); - /// Event added when a bumper contacts with a ball. + /// Event added when the bumper contacts with a ball. void onBallContacted() { emit(SparkyBumperState.inactive); } - /// Event added when a bumper finishes blinking. + /// Event added when the bumper finishes blinking. void onBlinked() { emit(SparkyBumperState.active); } diff --git a/packages/pinball_components/lib/src/components/sparky_bumper/sparky_bumper.dart b/packages/pinball_components/lib/src/components/sparky_bumper/sparky_bumper.dart index 968cac2c..98e884be 100644 --- a/packages/pinball_components/lib/src/components/sparky_bumper/sparky_bumper.dart +++ b/packages/pinball_components/lib/src/components/sparky_bumper/sparky_bumper.dart @@ -141,6 +141,9 @@ class _SparkyBumperSpriteGroupComponent @override Future onLoad() async { await super.onLoad(); + // TODO(alestiago): Consider refactoring once the following is merged: + // https://github.com/flame-engine/flame/pull/1538 + // ignore: public_member_api_docs parent.bloc.stream.listen((state) => current = state); final sprites = { diff --git a/packages/pinball_components/test/src/components/dash_nest_bumper_test.dart b/packages/pinball_components/test/src/components/dash_nest_bumper_test.dart index 5219a8a6..c2d72a70 100644 --- a/packages/pinball_components/test/src/components/dash_nest_bumper_test.dart +++ b/packages/pinball_components/test/src/components/dash_nest_bumper_test.dart @@ -40,40 +40,6 @@ void main() { expect(game.contains(bumper), isTrue); }); - flameTester.test('activate switches to active sprite', (game) async { - final bumper = DashNestBumper.main(); - await game.ensureAdd(bumper); - - final spriteGroupComponent = bumper.firstChild()!; - - expect( - spriteGroupComponent.current, - equals(DashNestBumperSpriteState.inactive), - ); - - bumper.activate(); - - expect( - spriteGroupComponent.current, - equals(DashNestBumperSpriteState.active), - ); - }); - - flameTester.test('deactivate switches to inactive sprite', (game) async { - final bumper = DashNestBumper.main(); - await game.ensureAdd(bumper); - - final spriteGroupComponent = bumper.firstChild()! - ..current = DashNestBumperSpriteState.active; - - bumper.deactivate(); - - expect( - spriteGroupComponent.current, - equals(DashNestBumperSpriteState.inactive), - ); - }); - flameTester.test('adds new children', (game) async { final component = Component(); final dashNestBumper = DashNestBumper.a( diff --git a/packages/pinball_components/test/src/components/sparky_bumper/behaviors/sparky_bumper_blinking_behaviour_test.dart b/packages/pinball_components/test/src/components/sparky_bumper/behaviors/sparky_bumper_blinking_behavior_test.dart similarity index 100% rename from packages/pinball_components/test/src/components/sparky_bumper/behaviors/sparky_bumper_blinking_behaviour_test.dart rename to packages/pinball_components/test/src/components/sparky_bumper/behaviors/sparky_bumper_blinking_behavior_test.dart diff --git a/packages/pinball_flame/test/src/contact_behavior_test.dart b/packages/pinball_flame/test/src/contact_behavior_test.dart index 79d851a7..630156ed 100644 --- a/packages/pinball_flame/test/src/contact_behavior_test.dart +++ b/packages/pinball_flame/test/src/contact_behavior_test.dart @@ -8,9 +8,7 @@ import 'package:pinball_flame/pinball_flame.dart'; class _TestBodyComponent extends BodyComponent { @override - Body createBody() { - return world.createBody(BodyDef()); - } + Body createBody() => world.createBody(BodyDef()); } class _TestContactBehavior extends ContactBehavior { diff --git a/test/game/components/alien_zone_test.dart b/test/game/components/alien_zone_test.dart index bdea2378..3d648169 100644 --- a/test/game/components/alien_zone_test.dart +++ b/test/game/components/alien_zone_test.dart @@ -1,10 +1,10 @@ // ignore_for_file: cascade_invocations -import 'package:bloc_test/bloc_test.dart'; 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'; @@ -24,10 +24,8 @@ void main() { flameTester.test( 'loads correctly', (game) async { - final alienZone = AlienZone(); - await game.ensureAdd(alienZone); - - expect(game.contains(alienZone), isTrue); + await game.addFromBlueprint(SparkyFireZone()); + await game.ready(); }, ); @@ -36,33 +34,15 @@ void main() { 'two AlienBumper', (game) async { final alienZone = AlienZone(); - await game.ensureAdd(alienZone); + await game.addFromBlueprint(alienZone); + await game.ready(); expect( - alienZone.descendants().whereType().length, + game.descendants().whereType().length, equals(2), ); }, ); }); - - group('bumpers', () { - late GameBloc gameBloc; - - setUp(() { - gameBloc = MockGameBloc(); - whenListen( - gameBloc, - const Stream.empty(), - initialState: const GameState.initial(), - ); - }); - - final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballTestGame.new, - blocBuilder: () => gameBloc, - assets: assets, - ); - }); }); } diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index b5f0fae4..f91e0236 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -80,72 +80,5 @@ void main() { }, ); }); - - group('bumpers', () { - late GameBloc gameBloc; - - final flameBlocTester = FlameBlocTester( - gameBuilder: () => EmptyPinballTestGame(assets: assets), - blocBuilder: () { - gameBloc = MockGameBloc(); - const state = GameState.initial(); - whenListen(gameBloc, Stream.value(state), initialState: state); - return gameBloc; - }, - assets: assets, - ); - - flameBlocTester.testGameWidget( - 'adds GameBonus.dashNest to the game when 3 bumpers are activated', - setUp: (game, _) async { - final ball = Ball(baseColor: const Color(0xFFFF0000)); - final flutterForest = FlutterForest(); - await game.ensureAddAll([flutterForest, ball]); - - final bumpers = flutterForest.children.whereType(); - expect(bumpers.length, equals(3)); - for (final bumper in bumpers) { - beginContact(game, bumper, ball); - await game.ready(); - - if (bumper == bumpers.last) { - verify( - () => gameBloc.add(const BonusActivated(GameBonus.dashNest)), - ).called(1); - } else { - verifyNever( - () => gameBloc.add(const BonusActivated(GameBonus.dashNest)), - ); - } - } - }, - ); - - flameBlocTester.testGameWidget( - 'deactivates bumpers when 3 are active', - setUp: (game, _) async { - final ball = Ball(baseColor: const Color(0xFFFF0000)); - final flutterForest = FlutterForest(); - await game.ensureAddAll([flutterForest, ball]); - - final bumpers = [ - MockDashNestBumper(), - MockDashNestBumper(), - MockDashNestBumper(), - ]; - - for (final bumper in bumpers) { - flutterForest.controller.activateBumper(bumper); - await game.ready(); - - if (bumper == bumpers.last) { - for (final bumper in bumpers) { - verify(bumper.deactivate).called(1); - } - } - } - }, - ); - }); }); } diff --git a/test/game/components/scoring_behaviour_test.dart b/test/game/components/scoring_behavior_test.dart similarity index 86% rename from test/game/components/scoring_behaviour_test.dart rename to test/game/components/scoring_behavior_test.dart index 065b50ee..d5e706b0 100644 --- a/test/game/components/scoring_behaviour_test.dart +++ b/test/game/components/scoring_behavior_test.dart @@ -11,12 +11,18 @@ import 'package:pinball_components/pinball_components.dart'; import '../../helpers/helpers.dart'; +class _TestBodyComponent extends BodyComponent { + @override + Body createBody() => world.createBody(BodyDef()); +} + void main() { group('ScoringBehavior', () { group('beginContact', () { late GameBloc bloc; late PinballAudio audio; late Ball ball; + late BodyComponent parent; setUp(() { audio = MockPinballAudio(); @@ -25,6 +31,8 @@ void main() { final ballBody = MockBody(); when(() => ball.body).thenReturn(ballBody); when(() => ballBody.position).thenReturn(Vector2.all(4)); + + parent = _TestBodyComponent(); }); final flameBlocTester = FlameBlocTester( @@ -48,7 +56,8 @@ void main() { setUp: (game, tester) async { const points = 20; final scoringBehavior = ScoringBehavior(points: points); - await game.ensureAdd(scoringBehavior); + await parent.add(scoringBehavior); + await game.ensureAdd(parent); scoringBehavior.beginContact(ball, MockContact()); @@ -65,7 +74,8 @@ void main() { setUp: (game, tester) async { const points = 20; final scoringBehavior = ScoringBehavior(points: points); - await game.ensureAdd(scoringBehavior); + await parent.add(scoringBehavior); + await game.ensureAdd(parent); scoringBehavior.beginContact(ball, MockContact()); @@ -78,7 +88,8 @@ void main() { setUp: (game, tester) async { const points = 20; final scoringBehavior = ScoringBehavior(points: points); - await game.ensureAdd(scoringBehavior); + await parent.add(scoringBehavior); + await game.ensureAdd(parent); scoringBehavior.beginContact(ball, MockContact()); await game.ready(); diff --git a/test/game/components/sparky_fire_zone_test.dart b/test/game/components/sparky_fire_zone_test.dart index b7e8cd04..9b254617 100644 --- a/test/game/components/sparky_fire_zone_test.dart +++ b/test/game/components/sparky_fire_zone_test.dart @@ -1,6 +1,5 @@ // ignore_for_file: cascade_invocations -import 'package:bloc_test/bloc_test.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -71,54 +70,40 @@ void main() { }, ); }); - - group('bumpers', () { - late GameBloc gameBloc; - - setUp(() { - gameBloc = MockGameBloc(); - whenListen( - gameBloc, - const Stream.empty(), - initialState: const GameState.initial(), - ); - }); - - final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballTestGame.new, - blocBuilder: () => gameBloc, - assets: assets, - ); - }); }); - group('SparkyTurboChargeSensorBallContactCallback', () { + group('SparkyComputerSensor', () { flameTester.test('calls turboCharge', (game) async { + final sensor = SparkyComputerSensor(); final ball = MockControlledBall(); final controller = MockBallController(); when(() => ball.controller).thenReturn(controller); - when(() => ball.gameRef).thenReturn(game); when(controller.turboCharge).thenAnswer((_) async {}); + await game.ensureAddAll([ + sensor, + SparkyAnimatronic(), + ]); + + sensor.beginContact(ball, MockContact()); + verify(() => ball.controller.turboCharge()).called(1); }); flameTester.test('plays SparkyAnimatronic', (game) async { + final sensor = SparkyComputerSensor(); + final sparkyAnimatronic = SparkyAnimatronic(); final ball = MockControlledBall(); final controller = MockBallController(); when(() => ball.controller).thenReturn(controller); - when(() => ball.gameRef).thenReturn(game); when(controller.turboCharge).thenAnswer((_) async {}); - - final sparkyFireZone = SparkyFireZone(); - await game.addFromBlueprint(sparkyFireZone); - await game.ready(); - - final sparkyAnimatronic = - sparkyFireZone.components.whereType().single; + await game.ensureAddAll([ + sensor, + sparkyAnimatronic, + ]); expect(sparkyAnimatronic.playing, isFalse); - + sensor.beginContact(ball, MockContact()); expect(sparkyAnimatronic.playing, isTrue); }); }); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index 0f3d2b30..b265c0aa 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -66,6 +66,8 @@ void main() { group('PinballGame', () { group('components', () { + // TODO(alestiago): tests that Blueprints get added once the Blueprint + // class is removed. flameTester.test( 'has only one BottomWall', (game) async { @@ -97,14 +99,6 @@ void main() { ); }); - flameTester.test( - 'one AlienZone', - (game) async { - await game.ready(); - expect(game.children.whereType().length, equals(1)); - }, - ); - group('controller', () { // TODO(alestiago): Write test to be controller agnostic. group('listenWhen', () {