From d80914515222e3da696438345416ad16ec24296f Mon Sep 17 00:00:00 2001 From: alestiago Date: Wed, 27 Apr 2022 14:30:42 +0100 Subject: [PATCH] feat: migrated to new ContactCallbacks --- lib/game/components/alien_zone.dart | 73 +++++------- lib/game/components/components.dart | 2 +- lib/game/components/controlled_ball.dart | 4 - lib/game/components/flutter_forest.dart | 76 +++++-------- lib/game/components/google_word.dart | 23 ++-- lib/game/components/score_points.dart | 47 -------- lib/game/components/scoring_behaviour.dart | 53 +++++++++ lib/game/components/sparky_fire_zone.dart | 92 ++++++--------- lib/game/components/wall.dart | 16 +-- lib/game/pinball_game.dart | 7 -- .../lib/src/components/alien_bumper.dart | 14 ++- .../lib/src/components/dash_nest_bumper.dart | 20 +++- .../lib/src/components/launch_ramp.dart | 7 -- .../lib/src/components/layer_sensor.dart | 39 +++---- .../lib/src/components/signpost.dart | 10 +- .../lib/src/components/spaceship.dart | 13 --- .../lib/src/components/spaceship_rail.dart | 8 -- .../lib/src/components/spaceship_ramp.dart | 8 -- .../lib/src/components/sparky_bumper.dart | 20 +++- packages/pinball_components/pubspec.yaml | 6 +- .../google_word/google_letter_game.dart | 10 -- .../pinball_components/sandbox/pubspec.lock | 20 ++-- .../pinball_components/sandbox/pubspec.yaml | 6 +- .../test/helpers/mocks.dart | 3 - .../src/components/layer_sensor_test.dart | 11 -- packages/pinball_flame/lib/pinball_flame.dart | 1 + .../lib/src/contacts_callbacks_adder.dart | 38 +++++++ packages/pinball_flame/pubspec.yaml | 6 +- .../pinball_flame/test/helpers/mocks.dart | 3 - pubspec.lock | 35 +++--- pubspec.yaml | 6 +- test/game/components/alien_zone_test.dart | 42 +------ test/game/components/board_test.dart | 4 +- .../components/controlled_flipper_test.dart | 4 +- test/game/components/flutter_forest_test.dart | 29 +---- test/game/components/score_points_test.dart | 105 ------------------ .../components/scoring_behaviour_test.dart | 100 +++++++++++++++++ .../components/sparky_fire_zone_test.dart | 46 +------- test/game/components/wall_test.dart | 3 - test/game/pinball_game_test.dart | 11 +- test/helpers/mocks.dart | 3 - test/helpers/test_games.dart | 33 ++++-- 42 files changed, 455 insertions(+), 602 deletions(-) delete mode 100644 lib/game/components/score_points.dart create mode 100644 lib/game/components/scoring_behaviour.dart create mode 100644 packages/pinball_flame/lib/src/contacts_callbacks_adder.dart delete mode 100644 test/game/components/score_points_test.dart create mode 100644 test/game/components/scoring_behaviour_test.dart diff --git a/lib/game/components/alien_zone.dart b/lib/game/components/alien_zone.dart index 720c1180..5603e09a 100644 --- a/lib/game/components/alien_zone.dart +++ b/lib/game/components/alien_zone.dart @@ -2,9 +2,9 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template alien_zone} /// Area positioned below [Spaceship] where the [Ball] @@ -12,49 +12,36 @@ import 'package:pinball_components/pinball_components.dart'; /// /// When a [Ball] hits an [AlienBumper], the bumper animates. /// {@endtemplate} -class AlienZone extends Component with HasGameRef { +class AlienZone extends Blueprint { /// {@macro alien_zone} - AlienZone(); - - @override - Future onLoad() async { - await super.onLoad(); - - gameRef.addContactCallback(AlienBumperBallContactCallback()); - - final lowerBumper = _AlienBumper.a() - ..initialPosition = Vector2(-32.52, -9.1); - final upperBumper = _AlienBumper.b() - ..initialPosition = Vector2(-22.89, -17.35); - - await addAll([ - lowerBumper, - upperBumper, - ]); - } -} - -// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D -// ContactCallback process is enhanced. -class _AlienBumper extends AlienBumper with ScorePoints { - _AlienBumper.a() : super.a(); - - _AlienBumper.b() : super.b(); - - @override - int get points => 20; + AlienZone() + : super( + components: [ + AlienBumper.a( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(-32.52, -9.1), + AlienBumper.b( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(-22.89, -17.35), + ], + ); } /// Listens when a [Ball] bounces against an [AlienBumper]. -@visibleForTesting -class AlienBumperBallContactCallback - extends ContactCallback { - @override - void begin( - AlienBumper alienBumper, - Ball _, - Contact __, - ) { - alienBumper.animate(); - } -} +// TODO(alestiago): Add Bumper animation behaviour. +// @visibleForTesting +// class AlienBumperBallContactCallback +// extends ContactCallback { +// @override +// void begin( +// AlienBumper alienBumper, +// Ball _, +// Contact __, +// ) { +// alienBumper.animate(); +// } +// } diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 7d4b23f7..95e3b697 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -8,6 +8,6 @@ export 'flutter_forest.dart'; export 'game_flow_controller.dart'; export 'google_word.dart'; export 'launcher.dart'; -export 'score_points.dart'; +export 'scoring_behaviour.dart'; export 'sparky_fire_zone.dart'; export 'wall.dart'; diff --git a/lib/game/components/controlled_ball.dart b/lib/game/components/controlled_ball.dart index 6468c821..0336cade 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -49,10 +49,6 @@ class BallController extends ComponentController /// {@macro ball_controller} BallController(Ball ball) : super(ball); - /// Removes the [Ball] from a [PinballGame]. - /// - /// Triggered by [BottomWallBallContactCallback] when the [Ball] falls into - /// a [BottomWall]. void lost() { component.shouldRemove = true; } diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart index 971cd700..f3a0a01a 100644 --- a/lib/game/components/flutter_forest.dart +++ b/lib/game/components/flutter_forest.dart @@ -1,7 +1,6 @@ // ignore_for_file: avoid_renaming_method_parameters import 'package:flame/components.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; @@ -16,33 +15,34 @@ import 'package:pinball_flame/pinball_flame.dart'; class FlutterForest extends Component with Controls<_FlutterForestController>, HasGameRef { /// {@macro flutter_forest} - FlutterForest() { + FlutterForest() + : super( + children: [ + Signpost( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(8.35, -58.3), + DashNestBumper.main( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(18.55, -59.35), + DashNestBumper.a( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(8.95, -51.95), + DashNestBumper.b( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(23.3, -46.75), + DashAnimatronic()..position = Vector2(20, -66), + ], + ) { controller = _FlutterForestController(this); } - - @override - Future onLoad() async { - await super.onLoad(); - gameRef.addContactCallback(_DashNestBumperBallContactCallback()); - - final signpost = Signpost()..initialPosition = Vector2(8.35, -58.3); - - final bigNest = _DashNestBumper.main() - ..initialPosition = Vector2(18.55, -59.35); - final smallLeftNest = _DashNestBumper.a() - ..initialPosition = Vector2(8.95, -51.95); - final smallRightNest = _DashNestBumper.b() - ..initialPosition = Vector2(23.3, -46.75); - final dashAnimatronic = DashAnimatronic()..position = Vector2(20, -66); - - await addAll([ - signpost, - smallLeftNest, - smallRightNest, - bigNest, - dashAnimatronic, - ]); - } } class _FlutterForestController extends ComponentController @@ -76,27 +76,3 @@ class _FlutterForestController extends ComponentController ); } } - -// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D -// ContactCallback process is enhanced. -class _DashNestBumper extends DashNestBumper with ScorePoints { - _DashNestBumper.main() : super.main(); - - _DashNestBumper.a() : super.a(); - - _DashNestBumper.b() : super.b(); - - @override - int get points => 20; -} - -class _DashNestBumperBallContactCallback - extends ContactCallback { - @override - void begin(DashNestBumper dashNestBumper, _, __) { - final parent = dashNestBumper.parent; - if (parent is FlutterForest) { - parent.controller.activateBumper(dashNestBumper); - } - } -} diff --git a/lib/game/components/google_word.dart b/lib/game/components/google_word.dart index 34609c64..fc8b9cac 100644 --- a/lib/game/components/google_word.dart +++ b/lib/game/components/google_word.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:flame/components.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; @@ -25,7 +24,6 @@ class GoogleWord extends Component @override Future onLoad() async { await super.onLoad(); - gameRef.addContactCallback(_GoogleLetterBallContactCallback()); final offsets = [ Vector2(-12.92, 1.82), @@ -71,13 +69,14 @@ class _GoogleWordController extends ComponentController } /// Activates a [GoogleLetter] when it contacts with a [Ball]. -class _GoogleLetterBallContactCallback - extends ContactCallback { - @override - void begin(GoogleLetter googleLetter, _, __) { - final parent = googleLetter.parent; - if (parent is GoogleWord) { - parent.controller.activate(googleLetter); - } - } -} +// TODO(alestiago): Add animation behaviour. +// class _GoogleLetterBallContactCallback +// extends ContactCallback { +// @override +// void begin(GoogleLetter googleLetter, _, __) { +// final parent = googleLetter.parent; +// if (parent is GoogleWord) { +// parent.controller.activate(googleLetter); +// } +// } +// } diff --git a/lib/game/components/score_points.dart b/lib/game/components/score_points.dart deleted file mode 100644 index 8a76680d..00000000 --- a/lib/game/components/score_points.dart +++ /dev/null @@ -1,47 +0,0 @@ -// ignore_for_file: avoid_renaming_method_parameters - -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball/game/game.dart'; -import 'package:pinball_components/pinball_components.dart'; - -/// {@template score_points} -/// Specifies the amount of points received on [Ball] collision. -/// {@endtemplate} -mixin ScorePoints on BodyComponent { - /// {@macro score_points} - int get points; - - @override - Future onLoad() async { - await super.onLoad(); - body.userData = this; - } -} - -/// {@template ball_score_points_callbacks} -/// Adds points to the score when a [Ball] collides with a [BodyComponent] that -/// implements [ScorePoints]. -/// {@endtemplate} -class BallScorePointsCallback extends ContactCallback { - /// {@macro ball_score_points_callbacks} - BallScorePointsCallback(PinballGame game) : _gameRef = game; - - final PinballGame _gameRef; - - @override - void begin( - Ball ball, - ScorePoints scorePoints, - Contact _, - ) { - _gameRef.read().add(Scored(points: scorePoints.points)); - _gameRef.audio.score(); - - _gameRef.add( - ScoreText( - text: scorePoints.points.toString(), - position: ball.body.position, - ), - ); - } -} diff --git a/lib/game/components/scoring_behaviour.dart b/lib/game/components/scoring_behaviour.dart new file mode 100644 index 00000000..e51f6926 --- /dev/null +++ b/lib/game/components/scoring_behaviour.dart @@ -0,0 +1,53 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +/// {@template scoring_behaviour} +/// +/// {@endtemplate} +class ScoringBehaviour extends Component + with ContactCallbacks, HasGameRef { + /// {@macro scoring_behaviour} + ScoringBehaviour({ + required int points, + }) : _points = points; + + final int _points; + + @override + Future onLoad() async { + await super.onLoad(); + + // TODO(alestiago): Refactor once the following is merged: + // https://github.com/flame-engine/flame/pull/1566 + final parent = this.parent; + if (parent is BodyComponent) { + final userData = parent.body.userData; + if (userData is ContactCallbacks) { + userData.add(this); + } else { + parent.body.userData = this; + } + } + } + + @override + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + if (other is! Ball) return; + + gameRef.read().add(Scored(points: _points)); + gameRef.audio.score(); + + gameRef.add( + ScoreText( + text: _points.toString(), + position: other.body.position, + ), + ); + } +} diff --git a/lib/game/components/sparky_fire_zone.dart b/lib/game/components/sparky_fire_zone.dart index a5450761..4f4b1b4b 100644 --- a/lib/game/components/sparky_fire_zone.dart +++ b/lib/game/components/sparky_fire_zone.dart @@ -1,7 +1,6 @@ // ignore_for_file: avoid_renaming_method_parameters import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; @@ -17,9 +16,21 @@ class SparkyFireZone extends Blueprint { SparkyFireZone() : super( components: [ - _SparkyBumper.a()..initialPosition = Vector2(-22.9, -41.65), - _SparkyBumper.b()..initialPosition = Vector2(-21.25, -57.9), - _SparkyBumper.c()..initialPosition = Vector2(-3.3, -52.55), + SparkyBumper.a( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(-22.9, -41.65), + SparkyBumper.b( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(-21.25, -57.9), + SparkyBumper.c( + children: [ + ScoringBehaviour(points: 20), + ], + )..initialPosition = Vector2(-3.3, -52.55), SparkyComputerSensor()..initialPosition = Vector2(-13, -49.8), SparkyAnimatronic()..position = Vector2(-13.8, -58.2), ], @@ -29,48 +40,27 @@ class SparkyFireZone extends Blueprint { ); } -// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D -// ContactCallback process is enhanced. -class _SparkyBumper extends SparkyBumper with ScorePoints { - _SparkyBumper.a() : super.a(); - - _SparkyBumper.b() : super.b(); - - _SparkyBumper.c() : super.c(); - - @override - int get points => 20; - - @override - Future onLoad() async { - await super.onLoad(); - // TODO(alestiago): Revisit once this has been merged: - // https://github.com/flame-engine/flame/pull/1547 - gameRef.addContactCallback(SparkyBumperBallContactCallback()); - } -} - /// Listens when a [Ball] bounces bounces against a [SparkyBumper]. -@visibleForTesting -class SparkyBumperBallContactCallback - extends ContactCallback { - @override - void begin( - SparkyBumper sparkyBumper, - Ball _, - Contact __, - ) { - sparkyBumper.animate(); - } -} +// TODO(alestiago): Add animation behaviour. +// @visibleForTesting +// class SparkyBumperBallContactCallback +// extends ContactCallback { +// @override +// void begin( +// SparkyBumper sparkyBumper, +// Ball _, +// Contact __, +// ) { +// sparkyBumper.animate(); +// } +// } /// {@template sparky_computer_sensor} /// Small sensor body used to detect when a ball has entered the /// [SparkyComputer]. /// {@endtemplate} -// TODO(alestiago): Revisit once this has been merged: -// https://github.com/flame-engine/flame/pull/1547 -class SparkyComputerSensor extends BodyComponent with InitialPosition { +class SparkyComputerSensor extends BodyComponent + with InitialPosition, ContactCallbacks { /// {@macro sparky_computer_sensor} SparkyComputerSensor() { renderBody = false; @@ -88,23 +78,11 @@ class SparkyComputerSensor extends BodyComponent with InitialPosition { } @override - Future onLoad() async { - await super.onLoad(); - // TODO(alestiago): Revisit once this has been merged: - // https://github.com/flame-engine/flame/pull/1547 - gameRef.addContactCallback(SparkyComputerSensorBallContactCallback()); - } -} + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + if (other is! ControlledBall) return; -@visibleForTesting -// TODO(alestiago): Revisit once this has been merged: -// https://github.com/flame-engine/flame/pull/1547 -// ignore: public_member_api_docs -class SparkyComputerSensorBallContactCallback - extends ContactCallback { - @override - void begin(_, ControlledBall controlledBall, __) { - controlledBall.controller.turboCharge(); - controlledBall.gameRef.firstChild()?.playing = true; + other.controller.turboCharge(); + gameRef.firstChild()?.playing = true; } } diff --git a/lib/game/components/wall.dart b/lib/game/components/wall.dart index aaae1d23..2f180d61 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -42,25 +42,19 @@ class Wall extends BodyComponent { /// {@template bottom_wall} /// [Wall] located at the bottom of the board. /// -/// Collisions with [BottomWall] are listened by -/// [BottomWallBallContactCallback]. /// {@endtemplate} -class BottomWall extends Wall { +class BottomWall extends Wall with ContactCallbacks { /// {@macro bottom_wall} BottomWall() : super( start: BoardDimensions.bounds.bottomLeft.toVector2(), end: BoardDimensions.bounds.bottomRight.toVector2(), ); -} -/// {@template bottom_wall_ball_contact_callback} -/// Listens when a [ControlledBall] falls into a [BottomWall]. -/// {@endtemplate} -class BottomWallBallContactCallback - extends ContactCallback { @override - void begin(ControlledBall ball, BottomWall wall, Contact contact) { - ball.controller.lost(); + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + if (other is! ControlledBall) return; + other.controller.lost(); } } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 374a55a1..9a79dafd 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -41,8 +41,6 @@ class PinballGame extends Forge2DGame @override Future onLoad() async { - _addContactCallbacks(); - unawaited(add(gameFlowController = GameFlowController(this))); unawaited(add(CameraController(this))); unawaited(add(Backboard.waiting(position: Vector2(0, -88)))); @@ -74,11 +72,6 @@ class PinballGame extends Forge2DGame await super.onLoad(); } - void _addContactCallbacks() { - addContactCallback(BallScorePointsCallback(this)); - addContactCallback(BottomWallBallContactCallback()); - } - Future _addBonusWord() async { await add( GoogleWord( diff --git a/packages/pinball_components/lib/src/components/alien_bumper.dart b/packages/pinball_components/lib/src/components/alien_bumper.dart index 1f96d214..a8081d7a 100644 --- a/packages/pinball_components/lib/src/components/alien_bumper.dart +++ b/packages/pinball_components/lib/src/components/alien_bumper.dart @@ -15,6 +15,7 @@ class AlienBumper extends BodyComponent with InitialPosition { required double minorRadius, required String onAssetPath, required String offAssetPath, + Iterable? children, }) : _majorRadius = majorRadius, _minorRadius = minorRadius, super( @@ -24,27 +25,32 @@ class AlienBumper extends BodyComponent with InitialPosition { onAssetPath: onAssetPath, offAssetPath: offAssetPath, ), + if (children != null) ...children, ], ) { renderBody = false; } /// {@macro alien_bumper} - AlienBumper.a() - : this._( + AlienBumper.a({ + Iterable? children, + }) : this._( majorRadius: 3.52, minorRadius: 2.97, onAssetPath: Assets.images.alienBumper.a.active.keyName, offAssetPath: Assets.images.alienBumper.a.inactive.keyName, + children: children, ); /// {@macro alien_bumper} - AlienBumper.b() - : this._( + AlienBumper.b({ + Iterable? children, + }) : this._( majorRadius: 3.19, minorRadius: 2.79, onAssetPath: Assets.images.alienBumper.b.active.keyName, offAssetPath: Assets.images.alienBumper.b.inactive.keyName, + children: children, ); final double _majorRadius; diff --git a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart index 46f96b37..75de002d 100644 --- a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart @@ -16,6 +16,7 @@ class DashNestBumper extends BodyComponent with InitialPosition { required String activeAssetPath, required String inactiveAssetPath, required Vector2 spritePosition, + Iterable? children, }) : _majorRadius = majorRadius, _minorRadius = minorRadius, super( @@ -26,39 +27,46 @@ class DashNestBumper extends BodyComponent with InitialPosition { inactiveAssetPath: inactiveAssetPath, position: spritePosition, ), + if (children != null) ...children, ], ) { renderBody = false; } /// {@macro dash_nest_bumper} - DashNestBumper.main() - : this._( + DashNestBumper.main({ + Iterable? children, + }) : this._( majorRadius: 5.1, minorRadius: 3.75, activeAssetPath: Assets.images.dash.bumper.main.active.keyName, inactiveAssetPath: Assets.images.dash.bumper.main.inactive.keyName, spritePosition: Vector2(0, -0.3), + children: children, ); /// {@macro dash_nest_bumper} - DashNestBumper.a() - : this._( + DashNestBumper.a({ + Iterable? children, + }) : this._( majorRadius: 3, minorRadius: 2.5, activeAssetPath: Assets.images.dash.bumper.a.active.keyName, inactiveAssetPath: Assets.images.dash.bumper.a.inactive.keyName, spritePosition: Vector2(0.35, -1.2), + children: children, ); /// {@macro dash_nest_bumper} - DashNestBumper.b() - : this._( + DashNestBumper.b({ + Iterable? children, + }) : this._( majorRadius: 3, minorRadius: 2.5, activeAssetPath: Assets.images.dash.bumper.b.active.keyName, inactiveAssetPath: Assets.images.dash.bumper.b.inactive.keyName, spritePosition: Vector2(0.35, -1.2), + children: children, ); final double _majorRadius; diff --git a/packages/pinball_components/lib/src/components/launch_ramp.dart b/packages/pinball_components/lib/src/components/launch_ramp.dart index baa54744..6d6144d7 100644 --- a/packages/pinball_components/lib/src/components/launch_ramp.dart +++ b/packages/pinball_components/lib/src/components/launch_ramp.dart @@ -107,13 +107,6 @@ class _LaunchRampBase extends BodyComponent with Layered { return body; } - - @override - Future onLoad() async { - await super.onLoad(); - gameRef - .addContactCallback(LayerSensorBallContactCallback<_LaunchRampExit>()); - } } class _LaunchRampBaseSpriteComponent extends SpriteComponent with HasGameRef { diff --git a/packages/pinball_components/lib/src/components/layer_sensor.dart b/packages/pinball_components/lib/src/components/layer_sensor.dart index 85cc8506..a3af2c26 100644 --- a/packages/pinball_components/lib/src/components/layer_sensor.dart +++ b/packages/pinball_components/lib/src/components/layer_sensor.dart @@ -23,7 +23,8 @@ enum LayerEntranceOrientation { /// By default the base [layer] is set to [Layer.board] and the /// [outsidePriority] is set to the lowest possible [Layer]. /// {@endtemplate} -abstract class LayerSensor extends BodyComponent with InitialPosition, Layered { +abstract class LayerSensor extends BodyComponent + with InitialPosition, Layered, ContactCallbacks { /// {@macro layer_sensor} LayerSensor({ required Layer insideLayer, @@ -75,35 +76,29 @@ abstract class LayerSensor extends BodyComponent with InitialPosition, Layered { return world.createBody(bodyDef)..createFixture(fixtureDef); } -} -/// {@template layer_sensor_ball_contact_callback} -/// Detects when a [Ball] enters or exits a [Layer] through a [LayerSensor]. -/// -/// Modifies [Ball]'s [Layer] and render priority depending on whether the -/// [Ball] is on or outside of a [Layer]. -/// {@endtemplate} -class LayerSensorBallContactCallback - extends ContactCallback { @override - void begin(Ball ball, LayerEntrance layerEntrance, Contact _) { - if (ball.layer != layerEntrance.insideLayer) { + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + if (other is! Ball) return; + + if (other.layer != insideLayer) { final isBallEnteringOpening = - (layerEntrance.orientation == LayerEntranceOrientation.down && - ball.body.linearVelocity.y < 0) || - (layerEntrance.orientation == LayerEntranceOrientation.up && - ball.body.linearVelocity.y > 0); + (orientation == LayerEntranceOrientation.down && + other.body.linearVelocity.y < 0) || + (orientation == LayerEntranceOrientation.up && + other.body.linearVelocity.y > 0); if (isBallEnteringOpening) { - ball - ..layer = layerEntrance.insideLayer - ..priority = layerEntrance.insidePriority + other + ..layer = insideLayer + ..priority = insidePriority ..reorderChildren(); } } else { - ball - ..layer = layerEntrance.outsideLayer - ..priority = layerEntrance.outsidePriority + other + ..layer = outsideLayer + ..priority = outsidePriority ..reorderChildren(); } } diff --git a/packages/pinball_components/lib/src/components/signpost.dart b/packages/pinball_components/lib/src/components/signpost.dart index 175c3382..cc6de9ea 100644 --- a/packages/pinball_components/lib/src/components/signpost.dart +++ b/packages/pinball_components/lib/src/components/signpost.dart @@ -46,10 +46,14 @@ extension on SignpostSpriteState { /// {@endtemplate} class Signpost extends BodyComponent with InitialPosition { /// {@macro signpost} - Signpost() - : super( + Signpost({ + Iterable? children, + }) : super( priority: RenderPriority.signpost, - children: [_SignpostSpriteComponent()], + children: [ + _SignpostSpriteComponent(), + if (children != null) ...children, + ], ) { renderBody = false; } diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index 4ea4e05a..24b07c66 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -50,19 +50,6 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { renderBody = false; } - @override - Future onLoad() async { - await super.onLoad(); - - gameRef - ..addContactCallback( - LayerSensorBallContactCallback<_SpaceshipEntrance>(), - ) - ..addContactCallback( - LayerSensorBallContactCallback<_SpaceshipHole>(), - ); - } - @override Body createBody() { final shape = CircleShape()..radius = 3; diff --git a/packages/pinball_components/lib/src/components/spaceship_rail.dart b/packages/pinball_components/lib/src/components/spaceship_rail.dart index 1175384b..d76e8e25 100644 --- a/packages/pinball_components/lib/src/components/spaceship_rail.dart +++ b/packages/pinball_components/lib/src/components/spaceship_rail.dart @@ -114,14 +114,6 @@ class _SpaceshipRailRamp extends BodyComponent with Layered { _createFixtureDefs().forEach(body.createFixture); return body; } - - @override - Future onLoad() async { - await super.onLoad(); - gameRef.addContactCallback( - LayerSensorBallContactCallback<_SpaceshipRailExit>(), - ); - } } class _SpaceshipRailRampSpriteComponent extends SpriteComponent diff --git a/packages/pinball_components/lib/src/components/spaceship_ramp.dart b/packages/pinball_components/lib/src/components/spaceship_ramp.dart index 30211251..a1c3d289 100644 --- a/packages/pinball_components/lib/src/components/spaceship_ramp.dart +++ b/packages/pinball_components/lib/src/components/spaceship_ramp.dart @@ -145,14 +145,6 @@ class _SpaceshipRampBackground extends BodyComponent return body; } - - @override - Future onLoad() async { - await super.onLoad(); - gameRef.addContactCallback( - LayerSensorBallContactCallback<_SpaceshipRampOpening>(), - ); - } } class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent diff --git a/packages/pinball_components/lib/src/components/sparky_bumper.dart b/packages/pinball_components/lib/src/components/sparky_bumper.dart index becac26b..51a1e81e 100644 --- a/packages/pinball_components/lib/src/components/sparky_bumper.dart +++ b/packages/pinball_components/lib/src/components/sparky_bumper.dart @@ -16,6 +16,7 @@ class SparkyBumper extends BodyComponent with InitialPosition { required String onAssetPath, required String offAssetPath, required Vector2 spritePosition, + Iterable? children, }) : _majorRadius = majorRadius, _minorRadius = minorRadius, super( @@ -26,39 +27,46 @@ class SparkyBumper extends BodyComponent with InitialPosition { offAssetPath: offAssetPath, position: spritePosition, ), + if (children != null) ...children, ], ) { renderBody = false; } /// {@macro sparky_bumper} - SparkyBumper.a() - : this._( + SparkyBumper.a({ + Iterable? children, + }) : this._( majorRadius: 2.9, minorRadius: 2.1, onAssetPath: Assets.images.sparky.bumper.a.active.keyName, offAssetPath: Assets.images.sparky.bumper.a.inactive.keyName, spritePosition: Vector2(0, -0.25), + children: children, ); /// {@macro sparky_bumper} - SparkyBumper.b() - : this._( + SparkyBumper.b({ + Iterable? children, + }) : this._( majorRadius: 2.85, minorRadius: 2, onAssetPath: Assets.images.sparky.bumper.b.active.keyName, offAssetPath: Assets.images.sparky.bumper.b.inactive.keyName, spritePosition: Vector2(0, -0.35), + children: children, ); /// {@macro sparky_bumper} - SparkyBumper.c() - : this._( + SparkyBumper.c({ + Iterable? children, + }) : this._( majorRadius: 3, minorRadius: 2.2, onAssetPath: Assets.images.sparky.bumper.c.active.keyName, offAssetPath: Assets.images.sparky.bumper.c.inactive.keyName, spritePosition: Vector2(0, -0.4), + children: children, ); final double _majorRadius; diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index a7cb8367..6a9910b1 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -8,7 +8,11 @@ environment: dependencies: flame: ^1.1.1 - flame_forge2d: ^0.11.0 + flame_forge2d: + git: + url: https://github.com/flame-engine/flame/ + path: packages/flame_forge2d/ + ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: sdk: flutter geometry: diff --git a/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart b/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart index d8022e57..be90fdb9 100644 --- a/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart @@ -17,7 +17,6 @@ class GoogleLetterGame extends BallGame { @override Future onLoad() async { await super.onLoad(); - addContactCallback(_BallGoogleLetterContactCallback()); camera.followVector2(Vector2.zero()); await add(GoogleLetter(0)); @@ -25,12 +24,3 @@ class GoogleLetterGame extends BallGame { await traceAllBodies(); } } - -class _BallGoogleLetterContactCallback - extends ContactCallback { - @override - void begin(Ball a, GoogleLetter b, Contact contact) { - super.begin(a, b, contact); - b.activate(); - } -} diff --git a/packages/pinball_components/sandbox/pubspec.lock b/packages/pinball_components/sandbox/pubspec.lock index d7ab8901..8d61da32 100644 --- a/packages/pinball_components/sandbox/pubspec.lock +++ b/packages/pinball_components/sandbox/pubspec.lock @@ -102,9 +102,11 @@ packages: flame_forge2d: dependency: "direct main" description: - name: flame_forge2d - url: "https://pub.dartlang.org" - source: hosted + path: "packages/flame_forge2d" + ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f + resolved-ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f + url: "https://github.com/flame-engine/flame/" + source: git version: "0.11.0" flutter: dependency: "direct main" @@ -169,7 +171,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" json_annotation: dependency: transitive description: @@ -197,7 +199,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" meta: dependency: transitive description: @@ -218,7 +220,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" path_provider_linux: dependency: transitive description: @@ -349,7 +351,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -384,7 +386,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.9" typed_data: dependency: transitive description: @@ -454,7 +456,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" very_good_analysis: dependency: "direct dev" description: diff --git a/packages/pinball_components/sandbox/pubspec.yaml b/packages/pinball_components/sandbox/pubspec.yaml index dd9f8259..d663cb04 100644 --- a/packages/pinball_components/sandbox/pubspec.yaml +++ b/packages/pinball_components/sandbox/pubspec.yaml @@ -9,7 +9,11 @@ environment: dependencies: dashbook: ^0.1.7 flame: ^1.1.1 - flame_forge2d: ^0.11.0 + flame_forge2d: + git: + url: https://github.com/flame-engine/flame/ + path: packages/flame_forge2d/ + ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: sdk: flutter pinball_components: diff --git a/packages/pinball_components/test/helpers/mocks.dart b/packages/pinball_components/test/helpers/mocks.dart index 520555df..f8ff494d 100644 --- a/packages/pinball_components/test/helpers/mocks.dart +++ b/packages/pinball_components/test/helpers/mocks.dart @@ -15,7 +15,4 @@ class MockGame extends Mock implements Forge2DGame {} class MockContact extends Mock implements Contact {} -class MockContactCallback extends Mock - implements ContactCallback {} - class MockComponent extends Mock implements Component {} diff --git a/packages/pinball_components/test/src/components/layer_sensor_test.dart b/packages/pinball_components/test/src/components/layer_sensor_test.dart index f91a6bcb..3eb2a488 100644 --- a/packages/pinball_components/test/src/components/layer_sensor_test.dart +++ b/packages/pinball_components/test/src/components/layer_sensor_test.dart @@ -22,11 +22,6 @@ class TestLayerSensor extends LayerSensor { Shape get shape => PolygonShape()..setAsBoxXY(1, 1); } -class TestLayerSensorBallContactCallback - extends LayerSensorBallContactCallback { - TestLayerSensorBallContactCallback() : super(); -} - void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(TestGame.new); @@ -135,18 +130,15 @@ void main() { insidePriority: insidePriority, insideLayer: Layer.spaceshipEntranceRamp, )..initialPosition = Vector2(0, 10); - final callback = TestLayerSensorBallContactCallback(); when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); - callback.begin(ball, sensor, MockContact()); verify(() => ball.layer = sensor.insideLayer).called(1); verify(() => ball.priority = sensor.insidePriority).called(1); verify(ball.reorderChildren).called(1); when(() => ball.layer).thenReturn(sensor.insideLayer); - callback.begin(ball, sensor, MockContact()); verify(() => ball.layer = Layer.board); verify(() => ball.priority = RenderPriority.ballOnBoard).called(1); verify(ball.reorderChildren).called(1); @@ -161,18 +153,15 @@ void main() { insidePriority: insidePriority, insideLayer: Layer.spaceshipEntranceRamp, )..initialPosition = Vector2(0, 10); - final callback = TestLayerSensorBallContactCallback(); when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); - callback.begin(ball, sensor, MockContact()); verify(() => ball.layer = sensor.insideLayer).called(1); verify(() => ball.priority = sensor.insidePriority).called(1); verify(ball.reorderChildren).called(1); when(() => ball.layer).thenReturn(sensor.insideLayer); - callback.begin(ball, sensor, MockContact()); verify(() => ball.layer = Layer.board); verify(() => ball.priority = RenderPriority.ballOnBoard).called(1); verify(ball.reorderChildren).called(1); diff --git a/packages/pinball_flame/lib/pinball_flame.dart b/packages/pinball_flame/lib/pinball_flame.dart index 709e7627..1d8bc7ac 100644 --- a/packages/pinball_flame/lib/pinball_flame.dart +++ b/packages/pinball_flame/lib/pinball_flame.dart @@ -2,5 +2,6 @@ library pinball_flame; export 'src/blueprint.dart'; export 'src/component_controller.dart'; +export 'src/contacts_callbacks_adder.dart'; export 'src/keyboard_input_controller.dart'; export 'src/sprite_animation.dart'; diff --git a/packages/pinball_flame/lib/src/contacts_callbacks_adder.dart b/packages/pinball_flame/lib/src/contacts_callbacks_adder.dart new file mode 100644 index 00000000..2d4b5dc8 --- /dev/null +++ b/packages/pinball_flame/lib/src/contacts_callbacks_adder.dart @@ -0,0 +1,38 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; + +/// {@template contact_callbacks_adder} +/// +/// {@endtemplate} +// TODO(alestiago): Consider adding streams to [ContactCallbacks]. +extension ContactCallbacksAdder on ContactCallbacks { + /// {@macro contact_callbacks_adder} + void add(ContactCallbacks contactCallbacks) { + if (contactCallbacks.onBeginContact != null) { + onBeginContact = (other, contact) { + onBeginContact?.call(other, contact); + contactCallbacks.beginContact(other, contact); + }; + } + + if (contactCallbacks.onEndContact != null) { + onEndContact = (other, contact) { + onEndContact?.call(other, contact); + contactCallbacks.endContact(other, contact); + }; + } + + if (contactCallbacks.onPreSolve != null) { + onPreSolve = (other, contact, oldManifold) { + onPreSolve?.call(other, contact, oldManifold); + contactCallbacks.preSolve(other, contact, oldManifold); + }; + } + + if (contactCallbacks.onPostSolve != null) { + onPostSolve = (other, contact, impulse) { + onPostSolve?.call(other, contact, impulse); + contactCallbacks.postSolve(other, contact, impulse); + }; + } + } +} diff --git a/packages/pinball_flame/pubspec.yaml b/packages/pinball_flame/pubspec.yaml index ad8ec131..89caf5bb 100644 --- a/packages/pinball_flame/pubspec.yaml +++ b/packages/pinball_flame/pubspec.yaml @@ -8,7 +8,11 @@ environment: dependencies: flame: ^1.1.1 - flame_forge2d: ^0.11.0 + flame_forge2d: + git: + url: https://github.com/flame-engine/flame/ + path: packages/flame_forge2d/ + ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: sdk: flutter diff --git a/packages/pinball_flame/test/helpers/mocks.dart b/packages/pinball_flame/test/helpers/mocks.dart index bf96390d..1c5042ff 100644 --- a/packages/pinball_flame/test/helpers/mocks.dart +++ b/packages/pinball_flame/test/helpers/mocks.dart @@ -4,7 +4,4 @@ import 'package:mocktail/mocktail.dart'; class MockForge2DGame extends Mock implements Forge2DGame {} -class MockContactCallback extends Mock - implements ContactCallback {} - class MockComponent extends Mock implements Component {} diff --git a/pubspec.lock b/pubspec.lock index 1a502f37..4a851209 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "31.0.0" + version: "39.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "2.8.0" + version: "4.0.0" args: dependency: transitive description: @@ -71,13 +71,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" - cli_util: - dependency: transitive - description: - name: cli_util - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.5" clock: dependency: transitive description: @@ -214,9 +207,11 @@ packages: flame_forge2d: dependency: "direct main" description: - name: flame_forge2d - url: "https://pub.dartlang.org" - source: hosted + path: "packages/flame_forge2d" + ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f + resolved-ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f + url: "https://github.com/flame-engine/flame/" + source: git version: "0.11.0" flame_test: dependency: "direct dev" @@ -321,7 +316,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" json_annotation: dependency: transitive description: @@ -356,7 +351,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" meta: dependency: transitive description: @@ -419,7 +414,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" path_provider: dependency: transitive description: @@ -592,7 +587,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -634,21 +629,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.19.5" + version: "1.21.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.9" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.13" typed_data: dependency: transitive description: @@ -669,7 +664,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" very_good_analysis: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 3b950c27..48c570c3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,11 @@ dependencies: equatable: ^2.0.3 flame: ^1.1.1 flame_bloc: ^1.2.0 - flame_forge2d: ^0.11.0 + flame_forge2d: + git: + url: https://github.com/flame-engine/flame/ + path: packages/flame_forge2d/ + ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: sdk: flutter flutter_bloc: ^8.0.1 diff --git a/test/game/components/alien_zone_test.dart b/test/game/components/alien_zone_test.dart index de4e58fc..bdea2378 100644 --- a/test/game/components/alien_zone_test.dart +++ b/test/game/components/alien_zone_test.dart @@ -1,11 +1,8 @@ // ignore_for_file: cascade_invocations -import 'dart:ui'; - 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'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -19,7 +16,9 @@ void main() { Assets.images.alienBumper.b.active.keyName, Assets.images.alienBumper.b.inactive.keyName, ]; - final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); + final flameTester = FlameTester( + () => EmptyPinballTestGame(assets: assets), + ); group('AlienZone', () { flameTester.test( @@ -64,41 +63,6 @@ void main() { blocBuilder: () => gameBloc, assets: assets, ); - - flameTester.test('call animate on contact', (game) async { - final contactCallback = AlienBumperBallContactCallback(); - final bumper = MockAlienBumper(); - final ball = MockBall(); - - when(bumper.animate).thenAnswer((_) async {}); - - contactCallback.begin(bumper, ball, MockContact()); - - verify(bumper.animate).called(1); - }); - - flameBlocTester.testGameWidget( - 'add Scored event', - setUp: (game, tester) async { - final ball = Ball(baseColor: const Color(0xFF00FFFF)); - final alienZone = AlienZone(); - - await game.ensureAdd(alienZone); - await game.ensureAdd(ball); - game.addContactCallback(BallScorePointsCallback(game)); - - final bumpers = alienZone.descendants().whereType(); - - for (final bumper in bumpers) { - beginContact(game, bumper, ball); - verify( - () => gameBloc.add( - Scored(points: bumper.points), - ), - ).called(1); - } - }, - ); }); }); } diff --git a/test/game/components/board_test.dart b/test/game/components/board_test.dart index bc1c5c39..a73d7a50 100644 --- a/test/game/components/board_test.dart +++ b/test/game/components/board_test.dart @@ -26,7 +26,9 @@ void main() { Assets.images.flipper.left.keyName, Assets.images.flipper.right.keyName, ]; - final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); + final flameTester = FlameTester( + () => EmptyPinballTestGame(assets: assets), + ); group('Board', () { flameTester.test( diff --git a/test/game/components/controlled_flipper_test.dart b/test/game/components/controlled_flipper_test.dart index 1b2a7e43..2f970254 100644 --- a/test/game/components/controlled_flipper_test.dart +++ b/test/game/components/controlled_flipper_test.dart @@ -15,7 +15,9 @@ void main() { Assets.images.flipper.left.keyName, Assets.images.flipper.right.keyName, ]; - final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); + final flameTester = FlameTester( + () => EmptyPinballTestGame(assets: assets), + ); final flameBlocTester = FlameBlocTester( gameBuilder: EmptyPinballTestGame.new, diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index 388410d5..34f49a65 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -25,7 +25,9 @@ void main() { Assets.images.signpost.active2.keyName, Assets.images.signpost.active3.keyName, ]; - final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); + final flameTester = FlameTester( + () => EmptyPinballTestGame(assets: assets), + ); group('FlutterForest', () { flameTester.test( @@ -88,7 +90,7 @@ void main() { }); final flameBlocTester = FlameBlocTester( - gameBuilder: () => EmptyPinballTestGame(assets), + gameBuilder: () => EmptyPinballTestGame(assets: assets), blocBuilder: () { gameBloc = MockGameBloc(); const state = GameState.initial(); @@ -98,29 +100,6 @@ void main() { assets: assets, ); - flameBlocTester.testGameWidget( - 'add Scored event', - setUp: (game, tester) async { - final flutterForest = FlutterForest(); - await game.ensureAddAll([ - flutterForest, - ball, - ]); - game.addContactCallback(BallScorePointsCallback(game)); - - final bumpers = flutterForest.descendants().whereType(); - - for (final bumper in bumpers) { - beginContact(game, bumper, ball); - verify( - () => gameBloc.add( - Scored(points: bumper.points), - ), - ).called(1); - } - }, - ); - flameBlocTester.testGameWidget( 'adds GameBonus.dashNest to the game when 3 bumpers are activated', setUp: (game, _) async { diff --git a/test/game/components/score_points_test.dart b/test/game/components/score_points_test.dart deleted file mode 100644 index dcd0ad82..00000000 --- a/test/game/components/score_points_test.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:pinball/game/game.dart'; -import 'package:pinball_audio/pinball_audio.dart'; -import 'package:pinball_components/pinball_components.dart'; - -import '../../helpers/helpers.dart'; - -class FakeScorePoints extends BodyComponent with ScorePoints { - @override - Body createBody() { - throw UnimplementedError(); - } - - @override - int get points => 2; -} - -void main() { - group('BallScorePointsCallback', () { - late PinballGame game; - late GameBloc bloc; - late PinballAudio audio; - late Ball ball; - late FakeScorePoints fakeScorePoints; - - setUp(() { - game = MockPinballGame(); - bloc = MockGameBloc(); - audio = MockPinballAudio(); - fakeScorePoints = FakeScorePoints(); - - ball = MockBall(); - final ballBody = MockBody(); - when(() => ball.body).thenReturn(ballBody); - when(() => ballBody.position).thenReturn(Vector2.all(4)); - }); - - setUpAll(() { - registerFallbackValue(FakeGameEvent()); - }); - - group('begin', () { - test( - 'emits Scored event with points', - () { - when(game.read).thenReturn(bloc); - when(() => game.audio).thenReturn(audio); - - BallScorePointsCallback(game).begin( - ball, - fakeScorePoints, - FakeContact(), - ); - - verify( - () => bloc.add( - Scored(points: fakeScorePoints.points), - ), - ).called(1); - }, - ); - - test( - 'plays a Score sound', - () { - when(game.read).thenReturn(bloc); - when(() => game.audio).thenReturn(audio); - - BallScorePointsCallback(game).begin( - ball, - fakeScorePoints, - FakeContact(), - ); - - verify(audio.score).called(1); - }, - ); - - test( - "adds a ScoreText component at Ball's position", - () { - when(game.read).thenReturn(bloc); - when(() => game.audio).thenReturn(audio); - - BallScorePointsCallback(game).begin( - ball, - fakeScorePoints, - FakeContact(), - ); - - verify( - () => game.add( - ScoreText( - text: fakeScorePoints.points.toString(), - position: ball.body.position, - ), - ), - ).called(1); - }, - ); - }); - }); -} diff --git a/test/game/components/scoring_behaviour_test.dart b/test/game/components/scoring_behaviour_test.dart new file mode 100644 index 00000000..bea9a3fd --- /dev/null +++ b/test/game/components/scoring_behaviour_test.dart @@ -0,0 +1,100 @@ +// ignore_for_file: cascade_invocations + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_audio/pinball_audio.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('ScoringBehaviour', () { + group('beginContact', () { + late GameBloc bloc; + late PinballAudio audio; + late Ball ball; + + setUp(() { + audio = MockPinballAudio(); + + ball = MockBall(); + final ballBody = MockBody(); + when(() => ball.body).thenReturn(ballBody); + when(() => ballBody.position).thenReturn(Vector2.all(4)); + }); + + final flameBlocTester = FlameBlocTester( + gameBuilder: () => EmptyPinballTestGame( + audio: audio, + ), + blocBuilder: () { + bloc = MockGameBloc(); + const state = GameState( + score: 0, + balls: 0, + bonusHistory: [], + ); + whenListen(bloc, Stream.value(state), initialState: state); + return bloc; + }, + ); + + flameBlocTester.testGameWidget( + 'emits Scored event with points', + setUp: (game, tester) async { + const points = 20; + final scoringBehaviour = ScoringBehaviour(points: points); + await game.ensureAdd(scoringBehaviour); + + scoringBehaviour.beginContact(ball, MockContact()); + + verify( + () => bloc.add( + const Scored(points: points), + ), + ).called(1); + }, + ); + + flameBlocTester.testGameWidget( + 'plays score sound', + setUp: (game, tester) async { + const points = 20; + final scoringBehaviour = ScoringBehaviour(points: points); + await game.ensureAdd(scoringBehaviour); + + scoringBehaviour.beginContact(ball, MockContact()); + + verify(audio.score).called(1); + }, + ); + + flameBlocTester.testGameWidget( + "adds a ScoreText component at Ball's position with points", + setUp: (game, tester) async { + const points = 20; + final scoringBehaviour = ScoringBehaviour(points: points); + await game.ensureAdd(scoringBehaviour); + + scoringBehaviour.beginContact(ball, MockContact()); + await game.ready(); + + final scoreText = game.descendants().whereType(); + expect(scoreText.length, equals(1)); + expect( + scoreText.first.text, + equals(points.toString()), + ); + expect( + scoreText.first.position, + equals(ball.body.position), + ); + }, + ); + }); + }); +} diff --git a/test/game/components/sparky_fire_zone_test.dart b/test/game/components/sparky_fire_zone_test.dart index 0ad69dab..b7e8cd04 100644 --- a/test/game/components/sparky_fire_zone_test.dart +++ b/test/game/components/sparky_fire_zone_test.dart @@ -1,7 +1,5 @@ // ignore_for_file: cascade_invocations -import 'dart:ui'; - import 'package:bloc_test/bloc_test.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -23,7 +21,10 @@ void main() { Assets.images.sparky.bumper.c.inactive.keyName, Assets.images.sparky.animatronic.keyName, ]; - final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); + + final flameTester = FlameTester( + () => EmptyPinballTestGame(assets: assets), + ); group('SparkyFireZone', () { flameTester.test('loads correctly', (game) async { @@ -88,59 +89,21 @@ void main() { blocBuilder: () => gameBloc, assets: assets, ); - - flameTester.test('call animate on contact', (game) async { - final contactCallback = SparkyBumperBallContactCallback(); - final bumper = MockSparkyBumper(); - final ball = MockBall(); - - when(bumper.animate).thenAnswer((_) async {}); - - contactCallback.begin(bumper, ball, MockContact()); - - verify(bumper.animate).called(1); - }); - - flameBlocTester.testGameWidget( - 'add Scored event', - setUp: (game, tester) async { - final ball = Ball(baseColor: const Color(0xFF00FFFF)); - final sparkyFireZone = SparkyFireZone(); - await game.addFromBlueprint(sparkyFireZone); - await game.ensureAdd(ball); - game.addContactCallback(BallScorePointsCallback(game)); - - final bumpers = sparkyFireZone.components.whereType(); - - for (final bumper in bumpers) { - beginContact(game, bumper, ball); - verify( - () => gameBloc.add( - Scored(points: bumper.points), - ), - ).called(1); - } - }, - ); }); }); group('SparkyTurboChargeSensorBallContactCallback', () { flameTester.test('calls turboCharge', (game) async { - final callback = SparkyComputerSensorBallContactCallback(); final ball = MockControlledBall(); final controller = MockBallController(); when(() => ball.controller).thenReturn(controller); when(() => ball.gameRef).thenReturn(game); when(controller.turboCharge).thenAnswer((_) async {}); - callback.begin(MockSparkyComputerSensor(), ball, MockContact()); - verify(() => ball.controller.turboCharge()).called(1); }); flameTester.test('plays SparkyAnimatronic', (game) async { - final callback = SparkyComputerSensorBallContactCallback(); final ball = MockControlledBall(); final controller = MockBallController(); when(() => ball.controller).thenReturn(controller); @@ -155,7 +118,6 @@ void main() { sparkyFireZone.components.whereType().single; expect(sparkyAnimatronic.playing, isFalse); - callback.begin(MockSparkyComputerSensor(), ball, MockContact()); expect(sparkyAnimatronic.playing, isTrue); }); diff --git a/test/game/components/wall_test.dart b/test/game/components/wall_test.dart index 92fe33ea..2905ab9a 100644 --- a/test/game/components/wall_test.dart +++ b/test/game/components/wall_test.dart @@ -122,7 +122,6 @@ void main() { ); final wall = BottomWall(); await game.ensureAddAll([ball, wall]); - game.addContactCallback(BottomWallBallContactCallback()); beginContact(game, ball, wall); await game.ready(); @@ -139,7 +138,6 @@ void main() { ); final wall = BottomWall(); await game.ensureAddAll([ball, wall]); - game.addContactCallback(BottomWallBallContactCallback()); beginContact(game, ball, wall); await game.ready(); @@ -154,7 +152,6 @@ void main() { final ball = ControlledBall.debug(); final wall = BottomWall(); await game.ensureAddAll([ball, wall]); - game.addContactCallback(BottomWallBallContactCallback()); beginContact(game, ball, wall); await game.ready(); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index d6bbb099..0f3d2b30 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -57,13 +57,14 @@ void main() { Assets.images.dino.dinoLandTop.keyName, Assets.images.dino.dinoLandBottom.keyName, ]; - final flameTester = FlameTester(() => PinballTestGame(assets)); - final debugModeFlameTester = FlameTester(() => DebugPinballTestGame(assets)); + final flameTester = FlameTester( + () => PinballTestGame(assets: assets), + ); + final debugModeFlameTester = FlameTester( + () => DebugPinballTestGame(assets: assets), + ); group('PinballGame', () { - // TODO(alestiago): test if [PinballGame] registers - // [BallScorePointsCallback] once the following issue is resolved: - // https://github.com/flame-engine/flame/issues/1416 group('components', () { flameTester.test( 'has only one BottomWall', diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index 586ef3b0..da6f5124 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -29,9 +29,6 @@ class MockBallController extends Mock implements BallController {} class MockContact extends Mock implements Contact {} -class MockContactCallback extends Mock - implements ContactCallback {} - class MockGameBloc extends Mock implements GameBloc {} class MockStartGameBloc extends Mock implements StartGameBloc {} diff --git a/test/helpers/test_games.dart b/test/helpers/test_games.dart index deeba2c3..baa466b8 100644 --- a/test/helpers/test_games.dart +++ b/test/helpers/test_games.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; +import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_theme/pinball_theme.dart'; import 'helpers.dart'; @@ -16,11 +17,14 @@ class TestGame extends Forge2DGame with FlameBloc { } class PinballTestGame extends PinballGame { - PinballTestGame([List? assets]) - : _assets = assets, + PinballTestGame({ + List? assets, + PinballAudio? audio, + CharacterTheme? theme, + }) : _assets = assets, super( - audio: MockPinballAudio(), - characterTheme: const DashTheme(), + audio: audio ?? MockPinballAudio(), + characterTheme: theme ?? const DashTheme(), ); final List? _assets; @@ -34,11 +38,14 @@ class PinballTestGame extends PinballGame { } class DebugPinballTestGame extends DebugPinballGame { - DebugPinballTestGame([List? assets]) - : _assets = assets, + DebugPinballTestGame({ + List? assets, + PinballAudio? audio, + CharacterTheme? theme, + }) : _assets = assets, super( - audio: MockPinballAudio(), - characterTheme: const DashTheme(), + audio: audio ?? MockPinballAudio(), + characterTheme: theme ?? const DashTheme(), ); final List? _assets; @@ -53,7 +60,15 @@ class DebugPinballTestGame extends DebugPinballGame { } class EmptyPinballTestGame extends PinballTestGame { - EmptyPinballTestGame([List? assets]) : super(assets); + EmptyPinballTestGame({ + List? assets, + PinballAudio? audio, + CharacterTheme? theme, + }) : super( + assets: assets, + audio: audio, + theme: theme, + ); @override Future onLoad() async {