diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index e645d6ec..e51e8201 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -9,4 +9,5 @@ export 'kicker.dart'; export 'launcher_ramp.dart'; export 'plunger.dart'; export 'score_points.dart'; +export 'spaceship_exit_rail.dart'; export 'wall.dart'; diff --git a/lib/game/components/jetpack_ramp.dart b/lib/game/components/jetpack_ramp.dart index e5eae883..b58ddfa6 100644 --- a/lib/game/components/jetpack_ramp.dart +++ b/lib/game/components/jetpack_ramp.dart @@ -114,7 +114,7 @@ class _JetpackRampOpening extends RampOpening { final double _rotation; - static final Vector2 _size = Vector2(JetpackRamp.width / 3, .1); + static final Vector2 _size = Vector2(JetpackRamp.width / 4, .1); @override Shape get shape => PolygonShape() diff --git a/lib/game/components/plunger.dart b/lib/game/components/plunger.dart index d9137457..5703e525 100644 --- a/lib/game/components/plunger.dart +++ b/lib/game/components/plunger.dart @@ -85,7 +85,7 @@ class Plunger extends BodyComponent with KeyboardHandler, InitialPosition { plunger: this, anchor: anchor, ); - world.createJoint(jointDef); + world.createJoint(PrismaticJoint(jointDef)); } @override diff --git a/lib/game/components/spaceship_exit_rail.dart b/lib/game/components/spaceship_exit_rail.dart new file mode 100644 index 00000000..0dc38322 --- /dev/null +++ b/lib/game/components/spaceship_exit_rail.dart @@ -0,0 +1,198 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'dart:math' as math; +import 'dart:ui'; + +import 'package:flame/extensions.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; + +/// {@template spaceship_exit_rail} +/// A [Blueprint] for the spaceship drop tube. +/// {@endtemplate} +class SpaceshipExitRail extends Forge2DBlueprint { + /// {@macro spaceship_exit_rail} + SpaceshipExitRail({required this.position}); + + /// The [position] where the elements will be created + final Vector2 position; + + @override + void build(_) { + addAllContactCallback([ + SpaceshipExitRailEndBallContactCallback(), + ]); + + final spaceshipExitRailRamp = _SpaceshipExitRailRamp() + ..initialPosition = position; + final exitRail = SpaceshipExitRailEnd() + ..initialPosition = position + _SpaceshipExitRailRamp.exitPoint; + + addAll([ + spaceshipExitRailRamp, + exitRail, + ]); + } +} + +class _SpaceshipExitRailRamp extends BodyComponent + with InitialPosition, Layered { + _SpaceshipExitRailRamp() : super(priority: 2) { + layer = Layer.spaceshipExitRail; + // TODO(ruimiguel): remove color once asset is placed. + paint = Paint() + ..color = const Color.fromARGB(255, 249, 65, 3) + ..style = PaintingStyle.stroke; + } + + static final exitPoint = Vector2(9.2, -48.5); + + List _createFixtureDefs() { + const entranceRotationAngle = 175 * math.pi / 180; + const curveRotationAngle = 275 * math.pi / 180; + const exitRotationAngle = 340 * math.pi / 180; + const width = 5.5; + + final fixturesDefs = []; + + final entranceWall = ArcShape( + center: Vector2(width / 2, 0), + arcRadius: width / 2, + angle: math.pi, + rotation: entranceRotationAngle, + ); + final entranceFixtureDef = FixtureDef(entranceWall); + fixturesDefs.add(entranceFixtureDef); + + final topLeftControlPoints = [ + Vector2(0, 0), + Vector2(10, .5), + Vector2(7, 4), + Vector2(15.5, 8.3), + ]; + final topLeftCurveShape = BezierCurveShape( + controlPoints: topLeftControlPoints, + )..rotate(curveRotationAngle); + final topLeftFixtureDef = FixtureDef(topLeftCurveShape); + fixturesDefs.add(topLeftFixtureDef); + + final topRightControlPoints = [ + Vector2(0, width), + Vector2(10, 6.5), + Vector2(7, 10), + Vector2(15.5, 13.2), + ]; + final topRightCurveShape = BezierCurveShape( + controlPoints: topRightControlPoints, + )..rotate(curveRotationAngle); + final topRightFixtureDef = FixtureDef(topRightCurveShape); + fixturesDefs.add(topRightFixtureDef); + + final mediumLeftControlPoints = [ + topLeftControlPoints.last, + Vector2(21, 12.9), + Vector2(30, 7.1), + Vector2(32, 4.8), + ]; + final mediumLeftCurveShape = BezierCurveShape( + controlPoints: mediumLeftControlPoints, + )..rotate(curveRotationAngle); + final mediumLeftFixtureDef = FixtureDef(mediumLeftCurveShape); + fixturesDefs.add(mediumLeftFixtureDef); + + final mediumRightControlPoints = [ + topRightControlPoints.last, + Vector2(21, 17.2), + Vector2(30, 12.1), + Vector2(32, 10.2), + ]; + final mediumRightCurveShape = BezierCurveShape( + controlPoints: mediumRightControlPoints, + )..rotate(curveRotationAngle); + final mediumRightFixtureDef = FixtureDef(mediumRightCurveShape); + fixturesDefs.add(mediumRightFixtureDef); + + final bottomLeftControlPoints = [ + mediumLeftControlPoints.last, + Vector2(40, -1), + Vector2(48, 1.9), + Vector2(50.5, 2.5), + ]; + final bottomLeftCurveShape = BezierCurveShape( + controlPoints: bottomLeftControlPoints, + )..rotate(curveRotationAngle); + final bottomLeftFixtureDef = FixtureDef(bottomLeftCurveShape); + fixturesDefs.add(bottomLeftFixtureDef); + + final bottomRightControlPoints = [ + mediumRightControlPoints.last, + Vector2(40, 4), + Vector2(46, 6.5), + Vector2(48.8, 7.6), + ]; + final bottomRightCurveShape = BezierCurveShape( + controlPoints: bottomRightControlPoints, + )..rotate(curveRotationAngle); + final bottomRightFixtureDef = FixtureDef(bottomRightCurveShape); + fixturesDefs.add(bottomRightFixtureDef); + + final exitWall = ArcShape( + center: exitPoint, + arcRadius: width / 2, + angle: math.pi, + rotation: exitRotationAngle, + ); + final exitFixtureDef = FixtureDef(exitWall); + fixturesDefs.add(exitFixtureDef); + + return fixturesDefs; + } + + @override + Body createBody() { + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } +} + +/// {@template spaceship_exit_rail_end} +/// A sensor [BodyComponent] responsible for sending the [Ball] +/// back to the board. +/// {@endtemplate} +class SpaceshipExitRailEnd extends RampOpening { + /// {@macro spaceship_exit_rail_end} + SpaceshipExitRailEnd() + : super( + pathwayLayer: Layer.spaceshipExitRail, + orientation: RampOrientation.down, + ) { + layer = Layer.spaceshipExitRail; + } + + @override + Shape get shape { + return CircleShape()..radius = 1; + } +} + +/// [ContactCallback] that handles the contact between the [Ball] +/// and a [SpaceshipExitRailEnd]. +/// +/// It resets the [Ball] priority and filter data so it will "be back" on the +/// board. +class SpaceshipExitRailEndBallContactCallback + extends ContactCallback { + @override + void begin(SpaceshipExitRailEnd exitRail, Ball ball, _) { + ball + ..priority = 1 + ..gameRef.reorderChildren() + ..layer = exitRail.outsideLayer; + } +} diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index e50fbd68..9673b2d2 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -53,6 +53,13 @@ class PinballGame extends Forge2DGame ), ), ); + unawaited( + addFromBlueprint( + SpaceshipExitRail( + position: Vector2(-34.3, 23.8), + ), + ), + ); // Fix camera on the center of the board. camera diff --git a/packages/pinball_components/lib/src/components/layer.dart b/packages/pinball_components/lib/src/components/layer.dart index a3f11f46..e0e64ddc 100644 --- a/packages/pinball_components/lib/src/components/layer.dart +++ b/packages/pinball_components/lib/src/components/layer.dart @@ -61,6 +61,9 @@ enum Layer { /// Collide only with Spaceship group elements. spaceship, + + /// Collide only with Spaceship exit rail group elements. + spaceshipExitRail, } /// {@template layer_mask_bits} @@ -89,6 +92,8 @@ extension LayerMaskBits on Layer { return 0x0005; case Layer.spaceship: return 0x000A; + case Layer.spaceshipExitRail: + return 0x0004; } } } diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index 257f7fcd..7e9d097e 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -32,7 +32,10 @@ class Spaceship extends Forge2DBlueprint { SpaceshipSaucer()..initialPosition = position, SpaceshipEntrance()..initialPosition = position, AndroidHead()..initialPosition = position, - SpaceshipHole()..initialPosition = position - Vector2(5.2, 4.8), + SpaceshipHole( + onExitLayer: Layer.spaceshipExitRail, + onExitElevation: 2, + )..initialPosition = position - Vector2(5.2, 4.8), SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8), SpaceshipWall()..initialPosition = position, ]); @@ -44,7 +47,8 @@ class Spaceship extends Forge2DBlueprint { /// {@endtemplate} class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_saucer} - SpaceshipSaucer() : super(priority: 2) { + // TODO(ruimiguel): apply Elevated when PR merged. + SpaceshipSaucer() : super(priority: 3) { layer = Layer.spaceship; } @@ -88,7 +92,7 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { /// {@endtemplate} class AndroidHead extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_bridge} - AndroidHead() : super(priority: 3) { + AndroidHead() : super(priority: 4) { layer = Layer.spaceship; } @@ -149,6 +153,10 @@ class SpaceshipEntrance extends RampOpening { layer = Layer.spaceship; } + /// Priority order for [SpaceshipHole] on enter. + // TODO(ruimiguel): apply Elevated when PR merged. + final int onEnterElevation = 4; + @override Shape get shape { renderBody = false; @@ -169,29 +177,31 @@ class SpaceshipEntrance extends RampOpening { /// {@template spaceship_hole} /// A sensor [BodyComponent] responsible for sending the [Ball] -/// back to the board. +/// out from the [Spaceship]. /// {@endtemplate} -class SpaceshipHole extends BodyComponent with InitialPosition, Layered { +class SpaceshipHole extends RampOpening { /// {@macro spaceship_hole} - SpaceshipHole() { + SpaceshipHole({Layer? onExitLayer, this.onExitElevation = 1}) + : super( + pathwayLayer: Layer.spaceship, + outsideLayer: onExitLayer, + orientation: RampOrientation.up, + ) { layer = Layer.spaceship; } - @override - Body createBody() { - renderBody = false; - final shape = ArcShape(center: Vector2(-3.5, 2), arcRadius: 6, angle: 1); + /// Priority order for [SpaceshipHole] on exit. + // TODO(ruimiguel): apply Elevated when PR merged. + final int onExitElevation; - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..angle = 5.2 - ..type = BodyType.static; - - return world.createBody(bodyDef) - ..createFixture( - FixtureDef(shape)..isSensor = true, - ); + @override + Shape get shape { + return ArcShape( + center: Vector2(0, 4.2), + arcRadius: 6, + angle: 1, + rotation: 60 * pi / 180, + ); } } @@ -225,6 +235,7 @@ class _SpaceshipWallShape extends ChainShape { /// {@endtemplate} class SpaceshipWall extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_wall} + // TODO(ruimiguel): apply Elevated when PR merged SpaceshipWall() : super(priority: 4) { layer = Layer.spaceship; } @@ -258,7 +269,8 @@ class SpaceshipEntranceBallContactCallback @override void begin(SpaceshipEntrance entrance, Ball ball, _) { ball - ..priority = 3 + // TODO(ruimiguel): apply Elevated when PR merged. + ..priority = entrance.onEnterElevation ..gameRef.reorderChildren() ..layer = Layer.spaceship; } @@ -267,15 +279,16 @@ class SpaceshipEntranceBallContactCallback /// [ContactCallback] that handles the contact between the [Ball] /// and a [SpaceshipHole]. /// -/// It resets the [Ball] priority and filter data so it will "be back" on the +/// It sets the [Ball] priority and filter data so it will "be back" on the /// board. class SpaceshipHoleBallContactCallback extends ContactCallback { @override void begin(SpaceshipHole hole, Ball ball, _) { ball - ..priority = 1 + // TODO(ruimiguel): apply Elevated when PR merged. + ..priority = hole.onExitElevation ..gameRef.reorderChildren() - ..layer = Layer.board; + ..layer = hole.outsideLayer; } } diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index f3fa6be1..c0eab284 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -7,8 +7,8 @@ environment: sdk: ">=2.16.0 <3.0.0" dependencies: - flame: ^1.1.0-releasecandidate.6 - flame_forge2d: ^0.9.0-releasecandidate.6 + flame: ^1.1.0 + flame_forge2d: ^0.10.0 flutter: sdk: flutter geometry: @@ -16,7 +16,7 @@ dependencies: dev_dependencies: - flame_test: ^1.1.0 + flame_test: ^1.3.0 flutter_test: sdk: flutter mocktail: ^0.2.0 diff --git a/packages/pinball_components/sandbox/pubspec.lock b/packages/pinball_components/sandbox/pubspec.lock index bb132da7..b0de4903 100644 --- a/packages/pinball_components/sandbox/pubspec.lock +++ b/packages/pinball_components/sandbox/pubspec.lock @@ -91,14 +91,14 @@ packages: name: flame url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-releasecandidate.6" + version: "1.1.0" flame_forge2d: dependency: "direct main" description: name: flame_forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.9.0-releasecandidate.6" + version: "0.10.0" flutter: dependency: "direct main" description: flutter @@ -134,7 +134,7 @@ packages: name: forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.9.0" + version: "0.10.0" freezed_annotation: dependency: transitive description: diff --git a/packages/pinball_components/sandbox/pubspec.yaml b/packages/pinball_components/sandbox/pubspec.yaml index 0c8267a8..94c0479b 100644 --- a/packages/pinball_components/sandbox/pubspec.yaml +++ b/packages/pinball_components/sandbox/pubspec.yaml @@ -8,8 +8,8 @@ environment: dependencies: dashbook: ^0.1.7 - flame: ^1.1.0-releasecandidate.6 - flame_forge2d: ^0.9.0-releasecandidate.6 + flame: ^1.1.0 + flame_forge2d: ^0.10.0 flutter: sdk: flutter pinball_components: diff --git a/packages/pinball_components/test/src/components/golden/spaceship.png b/packages/pinball_components/test/src/components/golden/spaceship.png index 579d955f..da665718 100644 Binary files a/packages/pinball_components/test/src/components/golden/spaceship.png and b/packages/pinball_components/test/src/components/golden/spaceship.png differ diff --git a/packages/pinball_components/test/src/components/spaceship_test.dart b/packages/pinball_components/test/src/components/spaceship_test.dart index 127d94a4..4c185675 100644 --- a/packages/pinball_components/test/src/components/spaceship_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_test.dart @@ -1,6 +1,5 @@ // ignore_for_file: cascade_invocations -import 'package:flame/game.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -39,38 +38,40 @@ void main() { }); group('Spaceship', () { - testWidgets('renders correctly', (tester) async { - final game = TestGame(); + final tester = FlameTester(TestGame.new); - // TODO(erickzanardo): This should be handled by flame test. - // refctor it when https://github.com/flame-engine/flame/pull/1501 is merged - await tester.runAsync(() async { - await tester.pumpWidget(GameWidget(game: game)); - await game.ready(); + tester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { await game.addFromBlueprint(Spaceship(position: Vector2(30, -30))); await game.ready(); await tester.pump(); - }); - - await expectLater( - find.byGame(), - matchesGoldenFile('golden/spaceship.png'), - ); - }); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/spaceship.png'), + ); + }, + ); }); group('SpaceshipEntranceBallContactCallback', () { test('changes the ball priority on contact', () { + when(() => entrance.onEnterElevation).thenReturn(3); + SpaceshipEntranceBallContactCallback().begin( entrance, ball, MockContact(), ); - verify(() => ball.priority = 3).called(1); + verify(() => ball.priority = entrance.onEnterElevation).called(1); }); test('re order the game children', () { + when(() => entrance.onEnterElevation).thenReturn(3); + SpaceshipEntranceBallContactCallback().begin( entrance, ball, @@ -83,16 +84,22 @@ void main() { group('SpaceshipHoleBallContactCallback', () { test('changes the ball priority on contact', () { + when(() => hole.outsideLayer).thenReturn(Layer.board); + when(() => hole.onExitElevation).thenReturn(1); + SpaceshipHoleBallContactCallback().begin( hole, ball, MockContact(), ); - verify(() => ball.priority = 1).called(1); + verify(() => ball.priority = hole.onExitElevation).called(1); }); test('re order the game children', () { + when(() => hole.outsideLayer).thenReturn(Layer.board); + when(() => hole.onExitElevation).thenReturn(1); + SpaceshipHoleBallContactCallback().begin( hole, ball, diff --git a/pubspec.lock b/pubspec.lock index 067559c4..ada9db4e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -182,28 +182,28 @@ packages: name: flame url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-releasecandidate.6" + version: "1.1.0" flame_bloc: dependency: "direct main" description: name: flame_bloc url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-releasecandidate.6" + version: "1.2.0" flame_forge2d: dependency: "direct main" description: name: flame_forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.9.0-releasecandidate.6" + version: "0.10.0" flame_test: dependency: "direct dev" description: name: flame_test url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.3.0" flutter: dependency: "direct main" description: flutter @@ -237,7 +237,7 @@ packages: name: forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.9.0" + version: "0.10.0" frontend_server_client: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d2b520a9..a0cca553 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,9 +10,9 @@ dependencies: bloc: ^8.0.2 cloud_firestore: ^3.1.10 equatable: ^2.0.3 - flame: ^1.1.0-releasecandidate.6 - flame_bloc: ^1.2.0-releasecandidate.6 - flame_forge2d: ^0.9.0-releasecandidate.6 + flame: ^1.1.0 + flame_bloc: ^1.2.0 + flame_forge2d: ^0.10.0 flutter: sdk: flutter flutter_bloc: ^8.0.1 @@ -30,7 +30,7 @@ dependencies: dev_dependencies: bloc_test: ^9.0.2 - flame_test: ^1.1.0 + flame_test: ^1.3.0 flutter_test: sdk: flutter mockingjay: ^0.2.0 diff --git a/test/game/components/ball_test.dart b/test/game/components/ball_test.dart index a872dc1f..f12b3569 100644 --- a/test/game/components/ball_test.dart +++ b/test/game/components/ball_test.dart @@ -27,11 +27,12 @@ void main() { final tester = flameBlocTester(gameBloc: () => gameBloc); - tester.widgetTest( + tester.testGameWidget( 'adds BallLost to GameBloc', - (game, tester) async { + setUp: (game, tester) async { await game.ready(); - + }, + verify: (game, tester) async { game.children.whereType().first.controller.lost(); await tester.pump(); @@ -39,14 +40,15 @@ void main() { }, ); - tester.widgetTest( + tester.testGameWidget( 'resets the ball if the game is not over', - (game, tester) async { + setUp: (game, tester) async { await game.ready(); game.children.whereType().first.controller.lost(); await game.ready(); // Making sure that all additions are done - + }, + verify: (game, tester) async { expect( game.children.whereType().length, equals(1), @@ -54,9 +56,9 @@ void main() { }, ); - tester.widgetTest( + tester.testGameWidget( 'no ball is added on game over', - (game, tester) async { + setUp: (game, tester) async { whenListen( gameBloc, const Stream.empty(), @@ -72,7 +74,8 @@ void main() { game.children.whereType().first.controller.lost(); await tester.pump(); - + }, + verify: (game, tester) async { expect( game.children.whereType().length, equals(0), diff --git a/test/game/components/bonus_word_test.dart b/test/game/components/bonus_word_test.dart index 001ccd46..42cf19c4 100644 --- a/test/game/components/bonus_word_test.dart +++ b/test/game/components/bonus_word_test.dart @@ -205,22 +205,24 @@ void main() { ); }); - tester.widgetTest( + tester.testGameWidget( 'adds BonusLetterActivated to GameBloc when not activated', - (game, tester) async { + setUp: (game, tester) async { await game.ready(); final bonusLetter = game.descendants().whereType().first; bonusLetter.activate(); await game.ready(); await tester.pump(); + }, + verify: (game, tester) async { verify(() => gameBloc.add(const BonusLetterActivated(0))).called(1); }, ); - tester.widgetTest( + tester.testGameWidget( "doesn't add BonusLetterActivated to GameBloc when already activated", - (game, tester) async { + setUp: (game, tester) async { const state = GameState( score: 0, balls: 2, @@ -238,14 +240,15 @@ void main() { final bonusLetter = game.descendants().whereType().first; bonusLetter.activate(); await game.ready(); - + }, + verify: (game, tester) async { verifyNever(() => gameBloc.add(const BonusLetterActivated(0))); }, ); - tester.widgetTest( + tester.testGameWidget( 'adds a ColorEffect', - (game, tester) async { + setUp: (game, tester) async { const state = GameState( score: 0, balls: 2, @@ -260,7 +263,9 @@ void main() { bonusLetter.onNewState(state); await tester.pump(); - + }, + verify: (game, tester) async { + final bonusLetter = game.descendants().whereType().first; expect( bonusLetter.children.whereType().length, equals(1), @@ -268,9 +273,14 @@ void main() { }, ); - tester.widgetTest( + tester.testGameWidget( 'only listens when there is a change on the letter status', - (game, tester) async { + setUp: (game, tester) async { + await game.ready(); + final bonusLetter = game.descendants().whereType().first; + bonusLetter.activate(); + }, + verify: (game, tester) async { const state = GameState( score: 0, balls: 2, @@ -278,11 +288,7 @@ void main() { activatedDashNests: {}, bonusHistory: [], ); - - await game.ready(); final bonusLetter = game.descendants().whereType().first; - bonusLetter.activate(); - expect( bonusLetter.listenWhen(const GameState.initial(), state), isTrue, diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index 659a4f13..3f4db6ff 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -30,8 +30,6 @@ void main() { 'a FlutterSignPost', (game) async { await game.ready(); - final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); expect( game.descendants().whereType().length, @@ -71,10 +69,12 @@ void main() { ); }); - tester.widgetTest( + tester.testGameWidget( 'listens when a Bonus.dashNest is added', - (game, tester) async { + setUp: (game, tester) async { await game.ready(); + }, + verify: (game, tester) async { final flutterForest = game.descendants().whereType().first; @@ -85,7 +85,6 @@ void main() { activatedDashNests: {}, bonusHistory: [GameBonus.dashNest], ); - expect( flutterForest.listenWhen(const GameState.initial(), state), isTrue, @@ -107,15 +106,16 @@ void main() { ); }); - tester.widgetTest( + final dashNestBumper = MockDashNestBumper(); + tester.testGameWidget( 'adds a DashNestActivated event with DashNestBumper.id', - (game, tester) async { - final contactCallback = DashNestBumperBallContactCallback(); + setUp: (game, tester) async { const id = '0'; - final dashNestBumper = MockDashNestBumper(); when(() => dashNestBumper.id).thenReturn(id); when(() => dashNestBumper.gameRef).thenReturn(game); - + }, + verify: (game, tester) async { + final contactCallback = DashNestBumperBallContactCallback(); contactCallback.begin(dashNestBumper, MockBall(), MockContact()); verify(() => gameBloc.add(DashNestActivated(dashNestBumper.id))) diff --git a/test/game/components/plunger_test.dart b/test/game/components/plunger_test.dart index 1cec7e0c..c6787be6 100644 --- a/test/game/components/plunger_test.dart +++ b/test/game/components/plunger_test.dart @@ -233,7 +233,7 @@ void main() { plunger: plunger, anchor: anchor, ); - game.world.createJoint(jointDef); + game.world.createJoint(PrismaticJoint(jointDef)); expect(jointDef.bodyB, equals(anchor.body)); }, @@ -250,7 +250,7 @@ void main() { plunger: plunger, anchor: anchor, ); - game.world.createJoint(jointDef); + game.world.createJoint(PrismaticJoint(jointDef)); expect(jointDef.enableLimit, isTrue); }, @@ -267,7 +267,7 @@ void main() { plunger: plunger, anchor: anchor, ); - game.world.createJoint(jointDef); + game.world.createJoint(PrismaticJoint(jointDef)); expect(jointDef.lowerTranslation, equals(double.negativeInfinity)); }, @@ -284,7 +284,7 @@ void main() { plunger: plunger, anchor: anchor, ); - game.world.createJoint(jointDef); + game.world.createJoint(PrismaticJoint(jointDef)); expect(jointDef.collideConnected, isTrue); }, @@ -292,11 +292,11 @@ void main() { }); testRawKeyUpEvents([LogicalKeyboardKey.space], (event) { - flameTester.widgetTest( + late final anchor = PlungerAnchor(plunger: plunger); + flameTester.testGameWidget( 'plunger cannot go below anchor', - (game, tester) async { + setUp: (game, tester) async { await game.ensureAdd(plunger); - final anchor = PlungerAnchor(plunger: plunger); await game.ensureAdd(anchor); // Giving anchor a shape for the plunger to collide with. @@ -306,19 +306,20 @@ void main() { plunger: plunger, anchor: anchor, ); - game.world.createJoint(jointDef); + game.world.createJoint(PrismaticJoint(jointDef)); await tester.pump(const Duration(seconds: 1)); - + }, + verify: (game, tester) async { expect(plunger.body.position.y > anchor.body.position.y, isTrue); }, ); }); testRawKeyUpEvents([LogicalKeyboardKey.space], (event) { - flameTester.widgetTest( + flameTester.testGameWidget( 'plunger cannot excessively exceed starting position', - (game, tester) async { + setUp: (game, tester) async { await game.ensureAdd(plunger); final anchor = PlungerAnchor(plunger: plunger); await game.ensureAdd(anchor); @@ -327,12 +328,13 @@ void main() { plunger: plunger, anchor: anchor, ); - game.world.createJoint(jointDef); + game.world.createJoint(PrismaticJoint(jointDef)); plunger.body.setTransform(Vector2(0, -1), 0); await tester.pump(const Duration(seconds: 1)); - + }, + verify: (game, tester) async { expect(plunger.body.position.y < 1, isTrue); }, ); diff --git a/test/game/components/spaceship_exit_rail_test.dart b/test/game/components/spaceship_exit_rail_test.dart new file mode 100644 index 00000000..99afc808 --- /dev/null +++ b/test/game/components/spaceship_exit_rail_test.dart @@ -0,0 +1,60 @@ +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_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('SpaceshipExitRail', () { + late PinballGame game; + late SpaceshipExitRailEnd exitRailEnd; + late Ball ball; + late Body body; + late Fixture fixture; + late Filter filterData; + + setUp(() { + game = MockPinballGame(); + + exitRailEnd = MockSpaceshipExitRailEnd(); + + ball = MockBall(); + body = MockBody(); + when(() => ball.gameRef).thenReturn(game); + when(() => ball.body).thenReturn(body); + + fixture = MockFixture(); + filterData = MockFilter(); + when(() => body.fixtures).thenReturn([fixture]); + when(() => fixture.filterData).thenReturn(filterData); + }); + + group('SpaceshipExitHoleBallContactCallback', () { + test('changes the ball priority on contact', () { + when(() => exitRailEnd.outsideLayer).thenReturn(Layer.board); + + SpaceshipExitRailEndBallContactCallback().begin( + exitRailEnd, + ball, + MockContact(), + ); + + verify(() => ball.priority = 1).called(1); + }); + + test('reorders the game children', () { + when(() => exitRailEnd.outsideLayer).thenReturn(Layer.board); + + SpaceshipExitRailEndBallContactCallback().begin( + exitRailEnd, + ball, + MockContact(), + ); + + verify(game.reorderChildren).called(1); + }); + }); + }); +} diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index 206b25a4..9453c93a 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -70,6 +70,8 @@ class MockSpaceshipEntrance extends Mock implements SpaceshipEntrance {} class MockSpaceshipHole extends Mock implements SpaceshipHole {} +class MockSpaceshipExitRailEnd extends Mock implements SpaceshipExitRailEnd {} + class MockComponentSet extends Mock implements ComponentSet {} class MockDashNestBumper extends Mock implements DashNestBumper {}