diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index e19c607c..61d0f3ca 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,13 +1,8 @@ export 'board.dart'; export 'bonus_word.dart'; -export 'chrome_dino.dart'; export 'controlled_ball.dart'; -export 'flipper_controller.dart'; +export 'controlled_flipper.dart'; export 'flutter_forest.dart'; -export 'jetpack_ramp.dart'; -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/controlled_ball.dart b/lib/game/components/controlled_ball.dart index 257d4f1d..1981e39c 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -88,7 +88,7 @@ class LaunchedBallController extends BallController void onNewState(GameState state) { super.onNewState(state); component.shouldRemove = true; - if (state.balls > 1) gameRef.spawnBall(); + if (state.balls > 0) gameRef.spawnBall(); } /// Removes the [Ball] from a [PinballGame]; spawning a new [Ball] if diff --git a/lib/game/components/flipper_controller.dart b/lib/game/components/controlled_flipper.dart similarity index 100% rename from lib/game/components/flipper_controller.dart rename to lib/game/components/controlled_flipper.dart diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart index 2d7bdf33..966b0a6b 100644 --- a/lib/game/components/flutter_forest.dart +++ b/lib/game/components/flutter_forest.dart @@ -68,7 +68,7 @@ class _FlutterForestController extends ComponentController void onNewState(GameState state) { super.onNewState(state); - component.add( + gameRef.add( ControlledBall.bonus(theme: gameRef.theme) ..initialPosition = Vector2(17.2, 52.7), ); diff --git a/lib/game/components/jetpack_ramp.dart b/lib/game/components/jetpack_ramp.dart deleted file mode 100644 index aa5a9dbd..00000000 --- a/lib/game/components/jetpack_ramp.dart +++ /dev/null @@ -1,127 +0,0 @@ -// ignore_for_file: public_member_api_docs, avoid_renaming_method_parameters - -import 'dart:math' as math; - -import 'package:flame/extensions.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'; - -/// A [Blueprint] which creates the [JetpackRamp]. -class Jetpack extends Forge2DBlueprint { - @override - void build(_) { - final position = Vector2( - BoardDimensions.bounds.left + 40.5, - BoardDimensions.bounds.top - 31.5, - ); - - addAllContactCallback([ - RampOpeningBallContactCallback<_JetpackRampOpening>(), - ]); - - final rightOpening = _JetpackRampOpening( - rotation: math.pi, - ) - ..initialPosition = position + Vector2(12.9, -20) - ..layer = Layer.opening; - final leftOpening = _JetpackRampOpening( - outsideLayer: Layer.spaceship, - rotation: math.pi, - ) - ..initialPosition = position + Vector2(-2.5, -20) - ..layer = Layer.jetpack; - - final jetpackRamp = JetpackRamp() - ..initialPosition = position + Vector2(5, -20.2) - ..layer = Layer.jetpack; - - addAll([ - rightOpening, - leftOpening, - jetpackRamp, - ]); - } -} - -/// {@template jetpack_ramp} -/// Represents the upper left blue ramp of the [Board]. -/// {@endtemplate} -class JetpackRamp extends BodyComponent with InitialPosition, Layered { - JetpackRamp() : super(priority: 2) { - layer = Layer.jetpack; - paint = Paint() - ..color = const Color.fromARGB(255, 8, 218, 241) - ..style = PaintingStyle.stroke; - } - - /// Radius of the external arc. - static const _externalRadius = 18.0; - - /// Width between walls of the ramp. - static const width = 5.0; - - List _createFixtureDefs() { - final fixturesDef = []; - - final externalCurveShape = ArcShape( - center: initialPosition, - arcRadius: _externalRadius, - angle: math.pi, - rotation: math.pi, - ); - final externalFixtureDef = FixtureDef(externalCurveShape); - fixturesDef.add(externalFixtureDef); - - final internalCurveShape = externalCurveShape.copyWith( - arcRadius: _externalRadius - width, - ); - final internalFixtureDef = FixtureDef(internalCurveShape); - fixturesDef.add(internalFixtureDef); - - return fixturesDef; - } - - @override - Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; - - final body = world.createBody(bodyDef); - _createFixtureDefs().forEach(body.createFixture); - - return body; - } -} - -/// {@template jetpack_ramp_opening} -/// [RampOpening] with [Layer.jetpack] to filter [Ball] collisions -/// inside [JetpackRamp]. -/// {@endtemplate} -class _JetpackRampOpening extends RampOpening { - /// {@macro jetpack_ramp_opening} - _JetpackRampOpening({ - Layer? outsideLayer, - required double rotation, - }) : _rotation = rotation, - super( - insideLayer: Layer.jetpack, - outsideLayer: outsideLayer, - orientation: RampOrientation.down, - ); - - final double _rotation; - - static final Vector2 _size = Vector2(JetpackRamp.width / 4, .1); - - @override - Shape get shape => PolygonShape() - ..setAsBox( - _size.x, - _size.y, - initialPosition, - _rotation, - ); -} diff --git a/lib/game/components/launcher_ramp.dart b/lib/game/components/launcher_ramp.dart deleted file mode 100644 index c05f8aa2..00000000 --- a/lib/game/components/launcher_ramp.dart +++ /dev/null @@ -1,141 +0,0 @@ -// ignore_for_file: public_member_api_docs, avoid_renaming_method_parameters - -import 'dart:math' as math; - -import 'package:flame/extensions.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'; - -/// A [Blueprint] which creates the [LauncherRamp]. -class Launcher extends Forge2DBlueprint { - @override - void build(_) { - final position = Vector2( - BoardDimensions.bounds.right - 31.3, - BoardDimensions.bounds.bottom + 33, - ); - - addAllContactCallback([ - RampOpeningBallContactCallback<_LauncherRampOpening>(), - ]); - - final leftOpening = _LauncherRampOpening(rotation: math.pi / 2) - ..initialPosition = position + Vector2(-11.8, 72.7) - ..layer = Layer.opening; - final rightOpening = _LauncherRampOpening(rotation: 0) - ..initialPosition = position + Vector2(-5.4, 65.4) - ..layer = Layer.opening; - - final launcherRamp = LauncherRamp() - ..initialPosition = position + Vector2(1.7, 0) - ..layer = Layer.launcher; - - addAll([ - leftOpening, - rightOpening, - launcherRamp, - ]); - } -} - -/// {@template launcher_ramp} -/// The yellow right ramp, where the [Ball] goes through when launched from the -/// [Plunger]. -/// {@endtemplate} -class LauncherRamp extends BodyComponent with InitialPosition, Layered { - /// {@macro launcher_ramp} - LauncherRamp() : super(priority: 2) { - layer = Layer.launcher; - paint = Paint() - ..color = const Color.fromARGB(255, 251, 255, 0) - ..style = PaintingStyle.stroke; - } - - /// Width between walls of the ramp. - static const width = 5.0; - - /// Radius of the external arc at the top of the ramp. - static const _externalRadius = 16.3; - - List _createFixtureDefs() { - final fixturesDef = []; - - final startPosition = initialPosition + Vector2(0, 3); - final endPosition = initialPosition + Vector2(0, 130); - - final rightStraightShape = EdgeShape() - ..set( - startPosition..rotate(BoardDimensions.perspectiveAngle), - endPosition..rotate(BoardDimensions.perspectiveAngle), - ); - final rightStraightFixtureDef = FixtureDef(rightStraightShape); - fixturesDef.add(rightStraightFixtureDef); - - final leftStraightShape = EdgeShape() - ..set( - startPosition - Vector2(width, 0), - endPosition - Vector2(width, 0), - ); - final leftStraightFixtureDef = FixtureDef(leftStraightShape); - fixturesDef.add(leftStraightFixtureDef); - - final externalCurveShape = ArcShape( - center: initialPosition + Vector2(-28.2, 132), - arcRadius: _externalRadius, - angle: math.pi / 2, - rotation: 3 * math.pi / 2, - ); - final externalCurveFixtureDef = FixtureDef(externalCurveShape); - fixturesDef.add(externalCurveFixtureDef); - - final internalCurveShape = externalCurveShape.copyWith( - arcRadius: _externalRadius - width, - ); - final internalCurveFixtureDef = FixtureDef(internalCurveShape); - fixturesDef.add(internalCurveFixtureDef); - - return fixturesDef; - } - - @override - Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; - - final body = world.createBody(bodyDef); - _createFixtureDefs().forEach(body.createFixture); - - return body; - } -} - -/// {@template launcher_ramp_opening} -/// [RampOpening] with [Layer.launcher] to filter [Ball]s collisions -/// inside [LauncherRamp]. -/// {@endtemplate} -class _LauncherRampOpening extends RampOpening { - /// {@macro launcher_ramp_opening} - _LauncherRampOpening({ - required double rotation, - }) : _rotation = rotation, - super( - insideLayer: Layer.launcher, - orientation: RampOrientation.down, - ); - - final double _rotation; - - static final Vector2 _size = Vector2(LauncherRamp.width / 3, .1); - - @override - Shape get shape => PolygonShape() - ..setAsBox( - _size.x, - _size.y, - initialPosition, - _rotation, - ); -} diff --git a/lib/game/components/plunger.dart b/lib/game/components/plunger.dart index 60e29a4d..cc5797c0 100644 --- a/lib/game/components/plunger.dart +++ b/lib/game/components/plunger.dart @@ -1,5 +1,6 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -13,7 +14,11 @@ class Plunger extends BodyComponent with KeyboardHandler, InitialPosition { /// {@macro plunger} Plunger({ required this.compressionDistance, - }); + }) : super( + priority: 5, + // TODO(allisonryan0002): remove paint after asset is added. + paint: Paint()..color = const Color.fromARGB(255, 241, 8, 8), + ); /// Distance the plunger can lower. final double compressionDistance; diff --git a/lib/game/components/spaceship_exit_rail.dart b/lib/game/components/spaceship_exit_rail.dart deleted file mode 100644 index 4a6c44cd..00000000 --- a/lib/game/components/spaceship_exit_rail.dart +++ /dev/null @@ -1,198 +0,0 @@ -// 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( - insideLayer: 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/game_assets.dart b/lib/game/game_assets.dart index cc8aac9c..6bafe37e 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -13,6 +13,10 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.flipper.right.keyName), images.load(components.Assets.images.baseboard.left.keyName), images.load(components.Assets.images.baseboard.right.keyName), + images.load(components.Assets.images.launchRamp.ramp.keyName), + images.load( + components.Assets.images.launchRamp.foregroundRailing.keyName, + ), images.load(components.Assets.images.dino.dinoLandTop.keyName), images.load(components.Assets.images.dino.dinoLandBottom.keyName), images.load(components.Assets.images.dashBumper.a.active.keyName), @@ -21,6 +25,21 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.dashBumper.b.inactive.keyName), images.load(components.Assets.images.dashBumper.main.active.keyName), images.load(components.Assets.images.dashBumper.main.inactive.keyName), + images.load(components.Assets.images.boundary.bottom.keyName), + images.load(components.Assets.images.boundary.outer.keyName), + images.load(components.Assets.images.spaceship.saucer.keyName), + images.load(components.Assets.images.spaceship.bridge.keyName), + images.load(components.Assets.images.spaceship.ramp.main.keyName), + images.load( + components.Assets.images.spaceship.ramp.railingBackground.keyName, + ), + images.load( + components.Assets.images.spaceship.ramp.railingForeground.keyName, + ), + images.load(components.Assets.images.spaceship.rail.main.keyName), + images.load(components.Assets.images.spaceship.rail.foreground.keyName), + images.load(components.Assets.images.chromeDino.mouth.keyName), + images.load(components.Assets.images.chromeDino.head.keyName), images.load(Assets.images.components.background.path), ]); } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 437adc10..e09ab461 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -36,9 +36,10 @@ class PinballGame extends Forge2DGame await _addGameBoundaries(); unawaited(add(Board())); + unawaited(addFromBlueprint(Boundaries())); unawaited(_addPlunger()); unawaited(_addBonusWord()); - unawaited(_addPaths()); + unawaited(_addRamps()); unawaited( addFromBlueprint( Spaceship( @@ -48,9 +49,7 @@ class PinballGame extends Forge2DGame ); unawaited( addFromBlueprint( - SpaceshipExitRail( - position: Vector2(-34.3, 23.8), - ), + SpaceshipRail(), ), ); @@ -96,9 +95,9 @@ class PinballGame extends Forge2DGame ); } - Future _addPaths() async { - unawaited(addFromBlueprint(Jetpack())); - unawaited(addFromBlueprint(Launcher())); + Future _addRamps() async { + unawaited(addFromBlueprint(SpaceshipRamp())); + unawaited(addFromBlueprint(LaunchRamp())); } void spawnBall() { @@ -139,7 +138,7 @@ class DebugPinballGame extends PinballGame with TapDetector { anchor: Anchor.center, ) ..position = Vector2(0, -7.8) - ..priority = -1; + ..priority = -2; await add(spriteComponent); } diff --git a/packages/geometry/test/src/geometry_test.dart b/packages/geometry/test/src/geometry_test.dart index e702f043..eb5e8d74 100644 --- a/packages/geometry/test/src/geometry_test.dart +++ b/packages/geometry/test/src/geometry_test.dart @@ -114,9 +114,9 @@ void main() { Vector2(0, 0), Vector2(10, 10), ], - step: 0.001, + step: 0.02, ); - expect(points.length, 1000); + expect(points.length, 50); }); }); diff --git a/packages/pinball_components/assets/images/boundary/bottom.png b/packages/pinball_components/assets/images/boundary/bottom.png new file mode 100644 index 00000000..7caf3e2f Binary files /dev/null and b/packages/pinball_components/assets/images/boundary/bottom.png differ diff --git a/packages/pinball_components/assets/images/boundary/outer.png b/packages/pinball_components/assets/images/boundary/outer.png new file mode 100644 index 00000000..c0b81e96 Binary files /dev/null and b/packages/pinball_components/assets/images/boundary/outer.png differ diff --git a/packages/pinball_components/assets/images/chrome_dino/head.png b/packages/pinball_components/assets/images/chrome_dino/head.png new file mode 100644 index 00000000..15be6fcd Binary files /dev/null and b/packages/pinball_components/assets/images/chrome_dino/head.png differ diff --git a/packages/pinball_components/assets/images/chrome_dino/mouth.png b/packages/pinball_components/assets/images/chrome_dino/mouth.png new file mode 100644 index 00000000..3d9caeae Binary files /dev/null and b/packages/pinball_components/assets/images/chrome_dino/mouth.png differ diff --git a/packages/pinball_components/assets/images/launch_ramp/foreground-railing.png b/packages/pinball_components/assets/images/launch_ramp/foreground-railing.png new file mode 100644 index 00000000..7dd0de96 Binary files /dev/null and b/packages/pinball_components/assets/images/launch_ramp/foreground-railing.png differ diff --git a/packages/pinball_components/assets/images/launch_ramp/ramp.png b/packages/pinball_components/assets/images/launch_ramp/ramp.png new file mode 100644 index 00000000..c811dd83 Binary files /dev/null and b/packages/pinball_components/assets/images/launch_ramp/ramp.png differ diff --git a/packages/pinball_components/assets/images/spaceship_bridge.png b/packages/pinball_components/assets/images/spaceship/bridge.png similarity index 100% rename from packages/pinball_components/assets/images/spaceship_bridge.png rename to packages/pinball_components/assets/images/spaceship/bridge.png diff --git a/packages/pinball_components/assets/images/spaceship/rail/foreground.png b/packages/pinball_components/assets/images/spaceship/rail/foreground.png new file mode 100644 index 00000000..4d11e865 Binary files /dev/null and b/packages/pinball_components/assets/images/spaceship/rail/foreground.png differ diff --git a/packages/pinball_components/assets/images/spaceship/rail/main.png b/packages/pinball_components/assets/images/spaceship/rail/main.png new file mode 100644 index 00000000..4b299c2c Binary files /dev/null and b/packages/pinball_components/assets/images/spaceship/rail/main.png differ diff --git a/packages/pinball_components/assets/images/spaceship/ramp/main.png b/packages/pinball_components/assets/images/spaceship/ramp/main.png new file mode 100644 index 00000000..81498965 Binary files /dev/null and b/packages/pinball_components/assets/images/spaceship/ramp/main.png differ diff --git a/packages/pinball_components/assets/images/spaceship/ramp/railing-background.png b/packages/pinball_components/assets/images/spaceship/ramp/railing-background.png new file mode 100644 index 00000000..2298f799 Binary files /dev/null and b/packages/pinball_components/assets/images/spaceship/ramp/railing-background.png differ diff --git a/packages/pinball_components/assets/images/spaceship/ramp/railing-foreground.png b/packages/pinball_components/assets/images/spaceship/ramp/railing-foreground.png new file mode 100644 index 00000000..e788fde0 Binary files /dev/null and b/packages/pinball_components/assets/images/spaceship/ramp/railing-foreground.png differ diff --git a/packages/pinball_components/assets/images/spaceship_saucer.png b/packages/pinball_components/assets/images/spaceship/saucer.png similarity index 100% rename from packages/pinball_components/assets/images/spaceship_saucer.png rename to packages/pinball_components/assets/images/spaceship/saucer.png diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index cf32e986..c928df79 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -14,6 +14,9 @@ class $AssetsImagesGen { AssetGenImage get ball => const AssetGenImage('assets/images/ball.png'); $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); + $AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen(); + $AssetsImagesChromeDinoGen get chromeDino => + const $AssetsImagesChromeDinoGen(); $AssetsImagesDashBumperGen get dashBumper => const $AssetsImagesDashBumperGen(); $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); @@ -23,13 +26,9 @@ class $AssetsImagesGen { AssetGenImage get flutterSignPost => const AssetGenImage('assets/images/flutter_sign_post.png'); - /// File path: assets/images/spaceship_bridge.png - AssetGenImage get spaceshipBridge => - const AssetGenImage('assets/images/spaceship_bridge.png'); - - /// File path: assets/images/spaceship_saucer.png - AssetGenImage get spaceshipSaucer => - const AssetGenImage('assets/images/spaceship_saucer.png'); + $AssetsImagesLaunchRampGen get launchRamp => + const $AssetsImagesLaunchRampGen(); + $AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen(); } class $AssetsImagesBaseboardGen { @@ -44,6 +43,30 @@ class $AssetsImagesBaseboardGen { const AssetGenImage('assets/images/baseboard/right.png'); } +class $AssetsImagesBoundaryGen { + const $AssetsImagesBoundaryGen(); + + /// File path: assets/images/boundary/bottom.png + AssetGenImage get bottom => + const AssetGenImage('assets/images/boundary/bottom.png'); + + /// File path: assets/images/boundary/outer.png + AssetGenImage get outer => + const AssetGenImage('assets/images/boundary/outer.png'); +} + +class $AssetsImagesChromeDinoGen { + const $AssetsImagesChromeDinoGen(); + + /// File path: assets/images/chrome_dino/head.png + AssetGenImage get head => + const AssetGenImage('assets/images/chrome_dino/head.png'); + + /// File path: assets/images/chrome_dino/mouth.png + AssetGenImage get mouth => + const AssetGenImage('assets/images/chrome_dino/mouth.png'); +} + class $AssetsImagesDashBumperGen { const $AssetsImagesDashBumperGen(); @@ -77,6 +100,35 @@ class $AssetsImagesFlipperGen { const AssetGenImage('assets/images/flipper/right.png'); } +class $AssetsImagesLaunchRampGen { + const $AssetsImagesLaunchRampGen(); + + /// File path: assets/images/launch_ramp/foreground-railing.png + AssetGenImage get foregroundRailing => + const AssetGenImage('assets/images/launch_ramp/foreground-railing.png'); + + /// File path: assets/images/launch_ramp/ramp.png + AssetGenImage get ramp => + const AssetGenImage('assets/images/launch_ramp/ramp.png'); +} + +class $AssetsImagesSpaceshipGen { + const $AssetsImagesSpaceshipGen(); + + /// File path: assets/images/spaceship/bridge.png + AssetGenImage get bridge => + const AssetGenImage('assets/images/spaceship/bridge.png'); + + $AssetsImagesSpaceshipRailGen get rail => + const $AssetsImagesSpaceshipRailGen(); + $AssetsImagesSpaceshipRampGen get ramp => + const $AssetsImagesSpaceshipRampGen(); + + /// File path: assets/images/spaceship/saucer.png + AssetGenImage get saucer => + const AssetGenImage('assets/images/spaceship/saucer.png'); +} + class $AssetsImagesDashBumperAGen { const $AssetsImagesDashBumperAGen(); @@ -113,6 +165,34 @@ class $AssetsImagesDashBumperMainGen { const AssetGenImage('assets/images/dash_bumper/main/inactive.png'); } +class $AssetsImagesSpaceshipRailGen { + const $AssetsImagesSpaceshipRailGen(); + + /// File path: assets/images/spaceship/rail/foreground.png + AssetGenImage get foreground => + const AssetGenImage('assets/images/spaceship/rail/foreground.png'); + + /// File path: assets/images/spaceship/rail/main.png + AssetGenImage get main => + const AssetGenImage('assets/images/spaceship/rail/main.png'); +} + +class $AssetsImagesSpaceshipRampGen { + const $AssetsImagesSpaceshipRampGen(); + + /// File path: assets/images/spaceship/ramp/main.png + AssetGenImage get main => + const AssetGenImage('assets/images/spaceship/ramp/main.png'); + + /// File path: assets/images/spaceship/ramp/railing-background.png + AssetGenImage get railingBackground => const AssetGenImage( + 'assets/images/spaceship/ramp/railing-background.png'); + + /// File path: assets/images/spaceship/ramp/railing-foreground.png + AssetGenImage get railingForeground => const AssetGenImage( + 'assets/images/spaceship/ramp/railing-foreground.png'); +} + class Assets { Assets._(); diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart new file mode 100644 index 00000000..79b3f909 --- /dev/null +++ b/packages/pinball_components/lib/src/components/boundaries.dart @@ -0,0 +1,156 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template boundaries} +/// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary]. +///{@endtemplate boundaries} +class Boundaries extends Forge2DBlueprint { + @override + void build(_) { + final bottomBoundary = _BottomBoundary(); + final outerBoundary = _OuterBoundary(); + + addAll([bottomBoundary, outerBoundary]); + } +} + +/// {@template bottom_boundary} +/// Curved boundary at the bottom of the board where the [Ball] exits the field +/// of play. +/// {@endtemplate bottom_boundary} +class _BottomBoundary extends BodyComponent with InitialPosition { + /// {@macro bottom_boundary} + _BottomBoundary() : super(priority: 2); + + List _createFixtureDefs() { + final fixturesDefs = []; + + final bottomLeftCurve = BezierCurveShape( + controlPoints: [ + Vector2(-43.6, -44.4), + Vector2(-31, -43.4), + Vector2(-18.7, -52.1), + ], + ); + final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurve); + fixturesDefs.add(bottomLeftCurveFixtureDef); + + final bottomRightCurve = BezierCurveShape( + controlPoints: [ + Vector2(31.8, -44.1), + Vector2(21.95, -47), + Vector2(12.3, -51.4), + ], + ); + final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurve); + fixturesDefs.add(bottomRightCurveFixtureDef); + + return fixturesDefs; + } + + @override + Body createBody() { + final bodyDef = BodyDef()..position = initialPosition; + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + renderBody = false; + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.boundary.bottom.keyName, + ); + + await add( + SpriteComponent( + sprite: sprite, + size: sprite.originalSize / 10, + anchor: Anchor.center, + position: Vector2(-5.4, 57.4), + ), + ); + } +} + +/// {@template outer_boundary} +/// Boundary enclosing the top and left side of the board. The right side of the +/// board is closed by the barrier the [LaunchRamp] creates. +/// {@endtemplate outer_boundary} +class _OuterBoundary extends BodyComponent with InitialPosition { + /// {@macro outer_boundary} + _OuterBoundary() : super(priority: -1); + + List _createFixtureDefs() { + final fixturesDefs = []; + + final topWall = EdgeShape() + ..set( + Vector2(3.6, 70.2), + Vector2(-14.1, 70.2), + ); + final topWallFixtureDef = FixtureDef(topWall); + fixturesDefs.add(topWallFixtureDef); + + final topLeftCurve = BezierCurveShape( + controlPoints: [ + Vector2(-32.3, 57.2), + Vector2(-31.5, 69.9), + Vector2(-14.1, 70.2), + ], + ); + final topLeftCurveFixtureDef = FixtureDef(topLeftCurve); + fixturesDefs.add(topLeftCurveFixtureDef); + + final leftWall = EdgeShape() + ..set( + Vector2(-32.3, 57.2), + Vector2(-44.1, -44.4), + ); + final leftWallFixtureDef = FixtureDef(leftWall); + fixturesDefs.add(leftWallFixtureDef); + + return fixturesDefs; + } + + @override + Body createBody() { + final bodyDef = BodyDef()..position = initialPosition; + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + renderBody = false; + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.boundary.outer.keyName, + ); + + await add( + SpriteComponent( + sprite: sprite, + size: sprite.originalSize / 10, + anchor: Anchor.center, + position: Vector2(-0.2, -1.4), + ), + ); + } +} diff --git a/lib/game/components/chrome_dino.dart b/packages/pinball_components/lib/src/components/chrome_dino.dart similarity index 98% rename from lib/game/components/chrome_dino.dart rename to packages/pinball_components/lib/src/components/chrome_dino.dart index af086e0e..2caa40c8 100644 --- a/lib/game/components/chrome_dino.dart +++ b/packages/pinball_components/lib/src/components/chrome_dino.dart @@ -43,7 +43,7 @@ class ChromeDino extends BodyComponent with InitialPosition { await add( TimerComponent( period: 1, - onTick: joint.swivel, + onTick: joint._swivel, repeat: true, ), ); @@ -150,7 +150,7 @@ class _ChromeDinoJoint extends RevoluteJoint { _ChromeDinoJoint(_ChromeDinoAnchorRevoluteJointDef def) : super(def); /// Sweeps the [ChromeDino] up and down repeatedly. - void swivel() { + void _swivel() { setMotorSpeed(-motorSpeed); } } diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index bbb2c29c..bf578ea7 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -2,6 +2,8 @@ export 'ball.dart'; export 'baseboard.dart'; export 'board_dimensions.dart'; export 'board_side.dart'; +export 'boundaries.dart'; +export 'chrome_dino.dart'; export 'dash_nest_bumper.dart'; export 'dino_walls.dart'; export 'fire_effect.dart'; @@ -9,7 +11,11 @@ export 'flipper.dart'; export 'flutter_sign_post.dart'; export 'initial_position.dart'; export 'joint_anchor.dart'; +export 'kicker.dart'; +export 'launch_ramp.dart'; export 'layer.dart'; export 'ramp_opening.dart'; export 'shapes/shapes.dart'; export 'spaceship.dart'; +export 'spaceship_rail.dart'; +export 'spaceship_ramp.dart'; 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 a2b9b982..447b4156 100644 --- a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart @@ -67,6 +67,7 @@ class BigDashNestBumper extends DashNestBumper { inactiveAssetPath: Assets.images.dashBumper.main.inactive.keyName, spriteComponent: SpriteComponent( anchor: Anchor.center, + position: Vector2(0, -0.3), ), ); @@ -74,9 +75,9 @@ class BigDashNestBumper extends DashNestBumper { Body createBody() { final shape = EllipseShape( center: Vector2.zero(), - majorRadius: 4.85, - minorRadius: 3.95, - )..rotate(math.pi / 2); + majorRadius: 5.1, + minorRadius: 3.75, + )..rotate(math.pi / 2.1); final fixtureDef = FixtureDef(shape); final bodyDef = BodyDef() diff --git a/lib/game/components/kicker.dart b/packages/pinball_components/lib/src/components/kicker.dart similarity index 100% rename from lib/game/components/kicker.dart rename to packages/pinball_components/lib/src/components/kicker.dart diff --git a/packages/pinball_components/lib/src/components/launch_ramp.dart b/packages/pinball_components/lib/src/components/launch_ramp.dart new file mode 100644 index 00000000..5f7ee098 --- /dev/null +++ b/packages/pinball_components/lib/src/components/launch_ramp.dart @@ -0,0 +1,243 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'dart:math' as math; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template launch_ramp} +/// A [Blueprint] which creates the [_LaunchRampBase] and +/// [_LaunchRampForegroundRailing]. +/// {@endtemplate} +class LaunchRamp extends Forge2DBlueprint { + @override + void build(_) { + addAllContactCallback([ + RampOpeningBallContactCallback<_LaunchRampExit>(), + ]); + + final launchRampBase = _LaunchRampBase()..layer = Layer.launcher; + + final launchRampForegroundRailing = _LaunchRampForegroundRailing() + ..layer = Layer.launcher; + + final launchRampExit = _LaunchRampExit(rotation: math.pi / 2) + ..initialPosition = Vector2(1.8, 34.2) + ..layer = Layer.opening + ..renderBody = false; + + addAll([ + launchRampBase, + launchRampForegroundRailing, + launchRampExit, + ]); + } +} + +/// {@template launch_ramp_base} +/// Ramp the [Ball] is launched from at the beginning of each ball life. +/// {@endtemplate} +class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { + /// {@macro launch_ramp_base} + _LaunchRampBase() : super(priority: -1) { + layer = Layer.launcher; + } + + List _createFixtureDefs() { + final fixturesDef = []; + + final rightStraightShape = EdgeShape() + ..set( + Vector2(31.4, 61.4), + Vector2(46.5, -68.4), + ); + final rightStraightFixtureDef = FixtureDef(rightStraightShape); + fixturesDef.add(rightStraightFixtureDef); + + final leftStraightShape = EdgeShape() + ..set( + Vector2(27.8, 61.4), + Vector2(41.5, -68.4), + ); + final leftStraightFixtureDef = FixtureDef(leftStraightShape); + fixturesDef.add(leftStraightFixtureDef); + + final topCurveShape = ArcShape( + center: Vector2(20.5, 61.1), + arcRadius: 11, + angle: 1.6, + rotation: -1.65, + ); + final topCurveFixtureDef = FixtureDef(topCurveShape); + fixturesDef.add(topCurveFixtureDef); + + final bottomCurveShape = ArcShape( + center: Vector2(19.3, 60.3), + arcRadius: 8.5, + angle: 1.48, + rotation: -1.58, + ); + final bottomCurveFixtureDef = FixtureDef(bottomCurveShape); + fixturesDef.add(bottomCurveFixtureDef); + + final topStraightShape = EdgeShape() + ..set( + Vector2(3.7, 70.1), + Vector2(19.1, 72.1), + ); + final topStraightFixtureDef = FixtureDef(topStraightShape); + fixturesDef.add(topStraightFixtureDef); + + final bottomStraightShape = EdgeShape() + ..set( + Vector2(3.7, 66.9), + Vector2(19.1, 68.8), + ); + final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); + fixturesDef.add(bottomStraightFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + renderBody = false; + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.launchRamp.ramp.keyName, + ); + + await add( + SpriteComponent( + sprite: sprite, + size: sprite.originalSize / 10, + anchor: Anchor.center, + position: Vector2(25.65, 0), + ), + ); + } +} + +/// {@template launch_ramp_foreground_railing} +/// Foreground railing for the [_LaunchRampBase] to render in front of the +/// [Ball]. +/// {@endtemplate} +class _LaunchRampForegroundRailing extends BodyComponent + with InitialPosition, Layered { + /// {@macro launch_ramp_foreground_railing} + _LaunchRampForegroundRailing() : super(priority: 4) { + layer = Layer.launcher; + } + + List _createFixtureDefs() { + final fixturesDef = []; + + final rightStraightShape = EdgeShape() + ..set( + Vector2(27.6, 57.9), + Vector2(30, 35.1), + ); + final rightStraightFixtureDef = FixtureDef(rightStraightShape); + fixturesDef.add(rightStraightFixtureDef); + + final curveShape = ArcShape( + center: Vector2(20.1, 59.3), + arcRadius: 7.5, + angle: 1.8, + rotation: -1.63, + ); + final curveFixtureDef = FixtureDef(curveShape); + fixturesDef.add(curveFixtureDef); + + final topStraightShape = EdgeShape() + ..set( + Vector2(3.7, 66.8), + Vector2(19.7, 66.8), + ); + final topStraightFixtureDef = FixtureDef(topStraightShape); + fixturesDef.add(topStraightFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + renderBody = false; + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.launchRamp.foregroundRailing.keyName, + ); + + await add( + SpriteComponent( + sprite: sprite, + size: sprite.originalSize / 10, + anchor: Anchor.center, + position: Vector2(22.8, 0), + priority: 4, + ), + ); + } +} + +/// {@template launch_ramp_exit} +/// [RampOpening] with [Layer.launcher] to filter [Ball]s exiting the +/// [LaunchRamp]. +/// {@endtemplate} +class _LaunchRampExit extends RampOpening { + /// {@macro launch_ramp_exit} + _LaunchRampExit({ + required double rotation, + }) : _rotation = rotation, + super( + insideLayer: Layer.launcher, + orientation: RampOrientation.down, + insidePriority: 3, + ); + + final double _rotation; + + static final Vector2 _size = Vector2(1.6, 0.1); + + @override + Shape get shape => PolygonShape() + ..setAsBox( + _size.x, + _size.y, + initialPosition, + _rotation, + ); +} diff --git a/packages/pinball_components/lib/src/components/layer.dart b/packages/pinball_components/lib/src/components/layer.dart index e0e64ddc..10477eff 100644 --- a/packages/pinball_components/lib/src/components/layer.dart +++ b/packages/pinball_components/lib/src/components/layer.dart @@ -53,8 +53,8 @@ enum Layer { /// Collide only with ramps opening elements. opening, - /// Collide only with Jetpack group elements. - jetpack, + /// Collide only with Spaceship entrance ramp group elements. + spaceshipEntranceRamp, /// Collide only with Launcher group elements. launcher, @@ -86,7 +86,7 @@ extension LayerMaskBits on Layer { return 0x0001; case Layer.opening: return 0x0007; - case Layer.jetpack: + case Layer.spaceshipEntranceRamp: return 0x0002; case Layer.launcher: return 0x0005; diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index 4d84eb68..f1b58db0 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -37,7 +37,7 @@ class Spaceship extends Forge2DBlueprint { AndroidHead()..initialPosition = position, SpaceshipHole( outsideLayer: Layer.spaceshipExitRail, - outsidePriority: 2, + outsidePriority: SpaceshipRail.ballPriorityWhenOnSpaceshipRail, )..initialPosition = position - Vector2(5.2, 4.8), SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8), SpaceshipWall()..initialPosition = position, @@ -59,7 +59,7 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { Future onLoad() async { await super.onLoad(); final sprite = await gameRef.loadSprite( - Assets.images.spaceshipSaucer.keyName, + Assets.images.spaceship.saucer.keyName, ); await add( @@ -106,7 +106,7 @@ class AndroidHead extends BodyComponent with InitialPosition, Layered { renderBody = false; final sprite = await gameRef.images.load( - Assets.images.spaceshipBridge.keyName, + Assets.images.spaceship.bridge.keyName, ); await add( @@ -185,8 +185,9 @@ class SpaceshipHole extends RampOpening { : super( insideLayer: Layer.spaceship, outsideLayer: outsideLayer, - outsidePriority: outsidePriority, orientation: RampOrientation.up, + insidePriority: 4, + outsidePriority: outsidePriority, ) { renderBody = false; layer = Layer.spaceship; @@ -195,8 +196,8 @@ class SpaceshipHole extends RampOpening { @override Shape get shape { return ArcShape( - center: Vector2(0, 4.2), - arcRadius: 6, + center: Vector2(0, 3.2), + arcRadius: 5, angle: 1, rotation: 60 * pi / 180, ); diff --git a/packages/pinball_components/lib/src/components/spaceship_rail.dart b/packages/pinball_components/lib/src/components/spaceship_rail.dart new file mode 100644 index 00000000..ace12e61 --- /dev/null +++ b/packages/pinball_components/lib/src/components/spaceship_rail.dart @@ -0,0 +1,246 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'dart:math' as math; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/gen/assets.gen.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; + +/// {@template spaceship_rail} +/// A [Blueprint] for the spaceship drop tube. +/// {@endtemplate} +class SpaceshipRail extends Forge2DBlueprint { + /// {@macro spaceship_rail} + SpaceshipRail(); + + /// Base priority for ball while be in [_SpaceshipRailRamp]. + static const ballPriorityWhenOnSpaceshipRail = 2; + + @override + void build(_) { + addAllContactCallback([ + SpaceshipRailExitBallContactCallback(), + ]); + + final railRamp = _SpaceshipRailRamp(); + final railEnd = SpaceshipRailExit(); + final topBase = _SpaceshipRailBase(radius: 0.55) + ..initialPosition = Vector2(-26.15, 18.65); + final bottomBase = _SpaceshipRailBase(radius: 0.8) + ..initialPosition = Vector2(-25.5, -12.9); + final railForeground = _SpaceshipRailForeground(); + + addAll([ + railRamp, + railEnd, + topBase, + bottomBase, + railForeground, + ]); + } +} + +/// Represents the spaceship drop rail from the [Spaceship]. +class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { + _SpaceshipRailRamp() + : super( + priority: SpaceshipRail.ballPriorityWhenOnSpaceshipRail - 1, + ) { + renderBody = false; + layer = Layer.spaceshipExitRail; + } + + List _createFixtureDefs() { + final fixturesDefs = []; + + final topArcShape = ArcShape( + center: Vector2(-35.5, 30.9), + arcRadius: 2.5, + angle: math.pi, + rotation: 2.9, + ); + final topArcFixtureDef = FixtureDef(topArcShape); + fixturesDefs.add(topArcFixtureDef); + + final topLeftCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-37.9, 30.4), + Vector2(-38, 23.9), + Vector2(-30.93, 18.2), + ], + ); + final topLeftCurveFixtureDef = FixtureDef(topLeftCurveShape); + fixturesDefs.add(topLeftCurveFixtureDef); + + final middleLeftCurveShape = BezierCurveShape( + controlPoints: [ + topLeftCurveShape.vertices.last, + Vector2(-22.6, 10.3), + Vector2(-30, 0.2), + ], + ); + final middleLeftCurveFixtureDef = FixtureDef(middleLeftCurveShape); + fixturesDefs.add(middleLeftCurveFixtureDef); + + final bottomLeftCurveShape = BezierCurveShape( + controlPoints: [ + middleLeftCurveShape.vertices.last, + Vector2(-36, -8.6), + Vector2(-32.04, -18.3), + ], + ); + final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurveShape); + fixturesDefs.add(bottomLeftCurveFixtureDef); + + final topRightStraightShape = EdgeShape() + ..set( + Vector2(-33, 31.3), + Vector2(-27.2, 21.3), + ); + final topRightStraightFixtureDef = FixtureDef(topRightStraightShape); + fixturesDefs.add(topRightStraightFixtureDef); + + final middleRightCurveShape = BezierCurveShape( + controlPoints: [ + topRightStraightShape.vertex1, + Vector2(-16.5, 11.4), + Vector2(-25.29, -1.7), + ], + ); + final middleRightCurveFixtureDef = FixtureDef(middleRightCurveShape); + fixturesDefs.add(middleRightCurveFixtureDef); + + final bottomRightCurveShape = BezierCurveShape( + controlPoints: [ + middleRightCurveShape.vertices.last, + Vector2(-29.91, -8.5), + Vector2(-26.8, -15.7), + ], + ); + final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurveShape); + fixturesDefs.add(bottomRightCurveFixtureDef); + + return fixturesDefs; + } + + @override + Body createBody() { + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprite(); + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.spaceship.rail.main.keyName, + ); + final spriteComponent = SpriteComponent( + sprite: sprite, + size: Vector2(17.5, 55.7), + anchor: Anchor.center, + position: Vector2(-29.4, -5.7), + ); + + await add(spriteComponent); + } +} + +class _SpaceshipRailForeground extends SpriteComponent with HasGameRef { + _SpaceshipRailForeground() + : super( + anchor: Anchor.center, + position: Vector2(-28.5, 19.7), + priority: SpaceshipRail.ballPriorityWhenOnSpaceshipRail + 1, + ); + + @override + Future onLoad() async { + await super.onLoad(); + + final sprite = await gameRef.loadSprite( + Assets.images.spaceship.rail.foreground.keyName, + ); + this.sprite = sprite; + size = sprite.originalSize / 10; + } +} + +/// Represents the ground bases of the [_SpaceshipRailRamp]. +class _SpaceshipRailBase extends BodyComponent with InitialPosition, Layered { + _SpaceshipRailBase({required this.radius}) + : super( + priority: SpaceshipRail.ballPriorityWhenOnSpaceshipRail + 1, + ) { + renderBody = false; + layer = Layer.board; + } + + final double radius; + + @override + Body createBody() { + final shape = CircleShape()..radius = radius; + + final fixtureDef = FixtureDef(shape); + + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + +/// {@template spaceship_rail_exit} +/// A sensor [BodyComponent] responsible for sending the [Ball] +/// back to the board. +/// {@endtemplate} +class SpaceshipRailExit extends RampOpening { + /// {@macro spaceship_rail_exit} + SpaceshipRailExit() + : super( + insideLayer: Layer.spaceshipExitRail, + orientation: RampOrientation.down, + insidePriority: 3, + ) { + renderBody = false; + layer = Layer.spaceshipExitRail; + } + + @override + Shape get shape { + return ArcShape( + center: Vector2(-28, -19), + arcRadius: 2.5, + angle: math.pi * 0.4, + rotation: -0.16, + ); + } +} + +/// [ContactCallback] that handles the contact between the [Ball] +/// and a [SpaceshipRailExit]. +/// +/// It resets the [Ball] priority and filter data so it will "be back" on the +/// board. +class SpaceshipRailExitBallContactCallback + extends ContactCallback { + @override + void begin(SpaceshipRailExit exitRail, Ball ball, _) { + ball + ..sendTo(exitRail.outsidePriority) + ..layer = exitRail.outsideLayer; + } +} diff --git a/packages/pinball_components/lib/src/components/spaceship_ramp.dart b/packages/pinball_components/lib/src/components/spaceship_ramp.dart new file mode 100644 index 00000000..6fb6cda0 --- /dev/null +++ b/packages/pinball_components/lib/src/components/spaceship_ramp.dart @@ -0,0 +1,282 @@ +// ignore_for_file: avoid_renaming_method_parameters, comment_references + +import 'dart:math' as math; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/gen/assets.gen.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; + +/// {@template spaceship_ramp} +/// A [Blueprint] which creates the [_SpaceshipRampBackground]. +/// {@endtemplate} +class SpaceshipRamp extends Forge2DBlueprint { + /// {@macro spaceship_ramp} + SpaceshipRamp(); + + /// Base priority for wall while be in the ramp. + static const int ballPriorityInsideRamp = 4; + + @override + void build(_) { + addAllContactCallback([ + RampOpeningBallContactCallback<_SpaceshipRampOpening>(), + ]); + + final rightOpening = _SpaceshipRampOpening( + // TODO(ruimiguel): set Board priority when defined. + outsidePriority: 1, + rotation: math.pi, + ) + ..initialPosition = Vector2(1.7, 19) + ..layer = Layer.opening; + final leftOpening = _SpaceshipRampOpening( + outsideLayer: Layer.spaceship, + outsidePriority: Spaceship.ballPriorityWhenOnSpaceship, + rotation: math.pi, + ) + ..initialPosition = Vector2(-13.7, 19) + ..layer = Layer.spaceshipEntranceRamp; + + final spaceshipRamp = _SpaceshipRampBackground(); + + final spaceshipRampForegroundRailing = _SpaceshipRampForegroundRailing(); + + final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, 20); + + addAll([ + rightOpening, + leftOpening, + baseRight, + spaceshipRamp, + spaceshipRampForegroundRailing, + ]); + } +} + +/// Represents the upper left blue ramp of the [Board] with its background +/// railing. +class _SpaceshipRampBackground extends BodyComponent + with InitialPosition, Layered { + _SpaceshipRampBackground() + : super(priority: SpaceshipRamp.ballPriorityInsideRamp - 1) { + layer = Layer.spaceshipEntranceRamp; + } + + /// Width between walls of the ramp. + static const width = 5.0; + + List _createFixtureDefs() { + final fixturesDef = []; + + final outerLeftCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-30.95, 38), + Vector2(-32.5, 71.25), + Vector2(-14.2, 71.25), + ], + ); + + final outerLeftCurveFixtureDef = FixtureDef(outerLeftCurveShape); + fixturesDef.add(outerLeftCurveFixtureDef); + + final outerRightCurveShape = BezierCurveShape( + controlPoints: [ + outerLeftCurveShape.vertices.last, + Vector2(4.7, 71.25), + Vector2(6.3, 40), + ], + ); + + final outerRightCurveFixtureDef = FixtureDef(outerRightCurveShape); + fixturesDef.add(outerRightCurveFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + renderBody = false; + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprites(); + } + + Future _loadSprites() async { + final spriteRamp = await gameRef.loadSprite( + Assets.images.spaceship.ramp.main.keyName, + ); + + final spriteRampComponent = SpriteComponent( + sprite: spriteRamp, + size: Vector2(38.1, 33.8), + anchor: Anchor.center, + position: Vector2(-12.2, -53.5), + ); + + final spriteRailingBg = await gameRef.loadSprite( + Assets.images.spaceship.ramp.railingBackground.keyName, + ); + final spriteRailingBgComponent = SpriteComponent( + sprite: spriteRailingBg, + size: Vector2(38.3, 35.1), + anchor: Anchor.center, + position: spriteRampComponent.position + Vector2(0, -1), + ); + + await addAll([ + spriteRailingBgComponent, + spriteRampComponent, + ]); + } +} + +/// Represents the foreground of the railing upper left blue ramp. +class _SpaceshipRampForegroundRailing extends BodyComponent + with InitialPosition, Layered { + _SpaceshipRampForegroundRailing() + : super(priority: SpaceshipRamp.ballPriorityInsideRamp + 1) { + layer = Layer.spaceshipEntranceRamp; + } + + List _createFixtureDefs() { + final fixturesDef = []; + + final innerLeftCurveShape = BezierCurveShape( + controlPoints: [ + Vector2(-24.5, 38), + Vector2(-26.3, 64), + Vector2(-13.8, 64.5), + ], + ); + + final innerLeftCurveFixtureDef = FixtureDef(innerLeftCurveShape); + fixturesDef.add(innerLeftCurveFixtureDef); + + final innerRightCurveShape = BezierCurveShape( + controlPoints: [ + innerLeftCurveShape.vertices.last, + Vector2(-1, 64.5), + Vector2(0.1, 39.5), + ], + ); + + final innerRightCurveFixtureDef = FixtureDef(innerRightCurveShape); + fixturesDef.add(innerRightCurveFixtureDef); + + return fixturesDef; + } + + @override + Body createBody() { + renderBody = false; + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + final body = world.createBody(bodyDef); + _createFixtureDefs().forEach(body.createFixture); + + return body; + } + + @override + Future onLoad() async { + await super.onLoad(); + await _loadSprites(); + } + + Future _loadSprites() async { + final spriteRailingFg = await gameRef.loadSprite( + Assets.images.spaceship.ramp.railingForeground.keyName, + ); + final spriteRailingFgComponent = SpriteComponent( + sprite: spriteRailingFg, + size: Vector2(26.1, 28.3), + anchor: Anchor.center, + position: Vector2(-12.2, -52.5), + ); + + await add(spriteRailingFgComponent); + } +} + +/// Represents the ground right base of the [SpaceshipRamp]. +class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered { + _SpaceshipRampBase() { + layer = Layer.board; + } + + @override + Body createBody() { + renderBody = false; + + const baseWidth = 6; + final baseShape = BezierCurveShape( + controlPoints: [ + Vector2(initialPosition.x - baseWidth / 2, initialPosition.y), + Vector2(initialPosition.x - baseWidth / 2, initialPosition.y) + + Vector2(2, 2), + Vector2(initialPosition.x + baseWidth / 2, initialPosition.y) + + Vector2(-2, 2), + Vector2(initialPosition.x + baseWidth / 2, initialPosition.y) + ], + ); + final fixtureDef = FixtureDef(baseShape); + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + +/// {@template spaceship_ramp_opening} +/// [RampOpening] with [Layer.spaceshipEntranceRamp] to filter [Ball] collisions +/// inside [_SpaceshipRampBackground]. +/// {@endtemplate} +class _SpaceshipRampOpening extends RampOpening { + /// {@macro spaceship_ramp_opening} + _SpaceshipRampOpening({ + Layer? outsideLayer, + int? outsidePriority, + required double rotation, + }) : _rotation = rotation, + super( + insideLayer: Layer.spaceshipEntranceRamp, + outsideLayer: outsideLayer, + orientation: RampOrientation.down, + insidePriority: SpaceshipRamp.ballPriorityInsideRamp, + outsidePriority: outsidePriority, + ); + + final double _rotation; + + static final Vector2 _size = Vector2(_SpaceshipRampBackground.width / 4, .1); + + @override + Shape get shape { + renderBody = false; + return PolygonShape() + ..setAsBox( + _size.x, + _size.y, + initialPosition, + _rotation, + ); + } +} diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 8fc9c6f8..0e5eb37a 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -27,11 +27,17 @@ flutter: assets: - assets/images/ - assets/images/baseboard/ + - assets/images/boundary/ - assets/images/dino/ - assets/images/flipper/ + - assets/images/launch_ramp/ - assets/images/dash_bumper/a/ - assets/images/dash_bumper/b/ - assets/images/dash_bumper/main/ + - assets/images/spaceship/ + - assets/images/spaceship/rail/ + - assets/images/spaceship/ramp/ + - assets/images/chrome_dino/ flutter_gen: line_length: 80 diff --git a/packages/pinball_components/sandbox/lib/common/common.dart b/packages/pinball_components/sandbox/lib/common/common.dart index 578c9b38..bb232e24 100644 --- a/packages/pinball_components/sandbox/lib/common/common.dart +++ b/packages/pinball_components/sandbox/lib/common/common.dart @@ -1,2 +1,3 @@ export 'games.dart'; export 'methods.dart'; +export 'trace.dart'; diff --git a/packages/pinball_components/sandbox/lib/common/trace.dart b/packages/pinball_components/sandbox/lib/common/trace.dart new file mode 100644 index 00000000..d64a5c6c --- /dev/null +++ b/packages/pinball_components/sandbox/lib/common/trace.dart @@ -0,0 +1,19 @@ +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; + +extension BodyTrace on BodyComponent { + void trace({Color color = const Color(0xFFFF0000)}) { + paint = Paint()..color = color; + renderBody = true; + + unawaited( + mounted.whenComplete(() { + final sprite = children.whereType().first; + sprite.paint.color = sprite.paint.color.withOpacity(0.5); + }), + ); + } +} diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 62ff7022..59066fca 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -6,7 +6,6 @@ // https://opensource.org/licenses/MIT. import 'package:dashbook/dashbook.dart'; import 'package:flutter/material.dart'; -import 'package:sandbox/stories/spaceship/spaceship.dart'; import 'package:sandbox/stories/stories.dart'; void main() { @@ -18,5 +17,7 @@ void main() { addFlipperStories(dashbook); addSpaceshipStories(dashbook); addBaseboardStories(dashbook); + addChromeDinoStories(dashbook); + addDashNestBumperStories(dashbook); runApp(dashbook); } diff --git a/packages/pinball_components/sandbox/lib/stories/ball/ball_booster.dart b/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart similarity index 71% rename from packages/pinball_components/sandbox/lib/stories/ball/ball_booster.dart rename to packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart index 9f78953a..3b8fe149 100644 --- a/packages/pinball_components/sandbox/lib/stories/ball/ball_booster.dart +++ b/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart @@ -3,8 +3,12 @@ import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/common/common.dart'; -class BallBoosterExample extends LineGame { - static const info = ''; +class BallBoosterGame extends LineGame { + static const info = ''' + Shows how a Ball with a boost works. + + Drag to launch a boosted Ball. +'''; @override void onLine(Vector2 line) { diff --git a/packages/pinball_components/sandbox/lib/stories/ball/basic.dart b/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart similarity index 82% rename from packages/pinball_components/sandbox/lib/stories/ball/basic.dart rename to packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart index 73890519..46cfb154 100644 --- a/packages/pinball_components/sandbox/lib/stories/ball/basic.dart +++ b/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart @@ -7,9 +7,9 @@ class BasicBallGame extends BasicGame with TapDetector { BasicBallGame({required this.color}); static const info = ''' - Basic example of how a Ball works. + Shows how a Ball works. - Tap anywhere on the screen to spawn a ball into the game. + Tap anywhere on the screen to spawn a ball into the game. '''; final Color color; diff --git a/packages/pinball_components/sandbox/lib/stories/ball/ball.dart b/packages/pinball_components/sandbox/lib/stories/ball/stories.dart similarity index 76% rename from packages/pinball_components/sandbox/lib/stories/ball/ball.dart rename to packages/pinball_components/sandbox/lib/stories/ball/stories.dart index 35b29499..2e945c47 100644 --- a/packages/pinball_components/sandbox/lib/stories/ball/ball.dart +++ b/packages/pinball_components/sandbox/lib/stories/ball/stories.dart @@ -2,8 +2,8 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/ball/ball_booster.dart'; -import 'package:sandbox/stories/ball/basic.dart'; +import 'package:sandbox/stories/ball/ball_booster_game.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; void addBallStories(Dashbook dashbook) { dashbook.storiesOf('Ball') @@ -20,9 +20,9 @@ void addBallStories(Dashbook dashbook) { ..add( 'Booster', (context) => GameWidget( - game: BallBoosterExample(), + game: BallBoosterGame(), ), codeLink: buildSourceLink('ball/ball_booster.dart'), - info: BallBoosterExample.info, + info: BallBoosterGame.info, ); } diff --git a/packages/pinball_components/sandbox/lib/stories/baseboard/basic.dart b/packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart similarity index 89% rename from packages/pinball_components/sandbox/lib/stories/baseboard/basic.dart rename to packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart index 127c1dec..0650fa13 100644 --- a/packages/pinball_components/sandbox/lib/stories/baseboard/basic.dart +++ b/packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart @@ -3,9 +3,7 @@ import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/common/common.dart'; class BasicBaseboardGame extends BasicGame { - static const info = ''' - Basic example of how a Baseboard works. -'''; + static const info = 'Shows how a Baseboard works.'; @override Future onLoad() async { diff --git a/packages/pinball_components/sandbox/lib/stories/baseboard/baseboard.dart b/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart similarity index 85% rename from packages/pinball_components/sandbox/lib/stories/baseboard/baseboard.dart rename to packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart index 96d89928..b3982af4 100644 --- a/packages/pinball_components/sandbox/lib/stories/baseboard/baseboard.dart +++ b/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart @@ -1,7 +1,7 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/baseboard/basic.dart'; +import 'package:sandbox/stories/baseboard/basic_baseboard_game.dart'; void addBaseboardStories(Dashbook dashbook) { dashbook.storiesOf('Baseboard').add( diff --git a/packages/pinball_components/sandbox/lib/stories/chrome_dino/chrome_dino_game.dart b/packages/pinball_components/sandbox/lib/stories/chrome_dino/chrome_dino_game.dart new file mode 100644 index 00000000..94bf6e44 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/chrome_dino/chrome_dino_game.dart @@ -0,0 +1,14 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; + +class ChromeDinoGame extends Forge2DGame { + static const info = 'Shows how a ChromeDino is rendered.'; + + @override + Future onLoad() async { + await super.onLoad(); + + camera.followVector2(Vector2.zero()); + await add(ChromeDino()); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/chrome_dino/stories.dart b/packages/pinball_components/sandbox/lib/stories/chrome_dino/stories.dart new file mode 100644 index 00000000..fb7c2ee1 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/chrome_dino/stories.dart @@ -0,0 +1,15 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:flame/game.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/chrome_dino/chrome_dino_game.dart'; + +void addChromeDinoStories(Dashbook dashbook) { + dashbook.storiesOf('Chrome Dino').add( + 'Basic', + (context) => GameWidget( + game: ChromeDinoGame(), + ), + codeLink: buildSourceLink('chrome_dino/basic.dart'), + info: ChromeDinoGame.info, + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/dash_nest_bumper/big_dash_nest_bumper_game.dart b/packages/pinball_components/sandbox/lib/stories/dash_nest_bumper/big_dash_nest_bumper_game.dart new file mode 100644 index 00000000..9638c36d --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/dash_nest_bumper/big_dash_nest_bumper_game.dart @@ -0,0 +1,33 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class BigDashNestBumperGame extends BasicBallGame { + BigDashNestBumperGame({ + required this.trace, + }) : super(color: const Color(0xFF0000FF)); + + static const info = ''' + Shows how a BigDashNestBumper is rendered. + + Activate the "trace" parameter to overlay the body. +'''; + + final bool trace; + + @override + Future onLoad() async { + await super.onLoad(); + + final center = screenToWorld(camera.viewport.canvasSize! / 2); + final bigDashNestBumper = BigDashNestBumper() + ..initialPosition = center + ..priority = 1; + await add(bigDashNestBumper); + + if (trace) bigDashNestBumper.trace(); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/dash_nest_bumper/stories.dart b/packages/pinball_components/sandbox/lib/stories/dash_nest_bumper/stories.dart new file mode 100644 index 00000000..95f3cd2a --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/dash_nest_bumper/stories.dart @@ -0,0 +1,18 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:flame/game.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; +import 'package:sandbox/stories/dash_nest_bumper/big_dash_nest_bumper_game.dart'; + +void addDashNestBumperStories(Dashbook dashbook) { + dashbook.storiesOf('Dash Nest Bumpers').add( + 'Big', + (context) => GameWidget( + game: BigDashNestBumperGame( + trace: context.boolProperty('Trace', true), + ), + ), + codeLink: buildSourceLink('dash_nest_bumper/big.dart'), + info: BasicBallGame.info, + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/effects/fire_effect.dart b/packages/pinball_components/sandbox/lib/stories/effects/fire_effect_game.dart similarity index 83% rename from packages/pinball_components/sandbox/lib/stories/effects/fire_effect.dart rename to packages/pinball_components/sandbox/lib/stories/effects/fire_effect_game.dart index 1262af11..ecc22910 100644 --- a/packages/pinball_components/sandbox/lib/stories/effects/fire_effect.dart +++ b/packages/pinball_components/sandbox/lib/stories/effects/fire_effect_game.dart @@ -2,9 +2,12 @@ import 'package:flame/components.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/common/common.dart'; -class FireEffectExample extends LineGame { - static const info = 'Demonstrate the fire trail effect ' - 'drag a line to define the trail direction'; +class FireEffectGame extends LineGame { + static const info = ''' + Shows how the FireEffect renders. + + Drag a line to define the trail direction. +'''; @override void onLine(Vector2 line) { diff --git a/packages/pinball_components/sandbox/lib/stories/effects/effects.dart b/packages/pinball_components/sandbox/lib/stories/effects/stories.dart similarity index 65% rename from packages/pinball_components/sandbox/lib/stories/effects/effects.dart rename to packages/pinball_components/sandbox/lib/stories/effects/stories.dart index 3a89c73b..37ba434e 100644 --- a/packages/pinball_components/sandbox/lib/stories/effects/effects.dart +++ b/packages/pinball_components/sandbox/lib/stories/effects/stories.dart @@ -1,13 +1,13 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/effects/fire_effect.dart'; +import 'package:sandbox/stories/effects/fire_effect_game.dart'; void addEffectsStories(Dashbook dashbook) { dashbook.storiesOf('Effects').add( 'Fire Effect', - (context) => GameWidget(game: FireEffectExample()), + (context) => GameWidget(game: FireEffectGame()), codeLink: buildSourceLink('effects/fire_effect.dart'), - info: FireEffectExample.info, + info: FireEffectGame.info, ); } diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/basic.dart b/packages/pinball_components/sandbox/lib/stories/flipper/basic.dart deleted file mode 100644 index 0e5587ea..00000000 --- a/packages/pinball_components/sandbox/lib/stories/flipper/basic.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball_components/pinball_components.dart'; -import 'package:sandbox/common/common.dart'; - -class BasicFlipperGame extends BasicGame { - static const info = ''' - Basic example of how a Flipper works. -'''; - - @override - Future onLoad() async { - await super.onLoad(); - - final center = screenToWorld(camera.viewport.canvasSize! / 2); - - final leftFlipper = Flipper(side: BoardSide.left) - ..initialPosition = center - Vector2(Flipper.size.x, 0); - final rightFlipper = Flipper(side: BoardSide.right) - ..initialPosition = center + Vector2(Flipper.size.x, 0); - - await addAll([ - leftFlipper, - rightFlipper, - ]); - } -} diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/basic_flipper_game.dart b/packages/pinball_components/sandbox/lib/stories/flipper/basic_flipper_game.dart new file mode 100644 index 00000000..78959374 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/flipper/basic_flipper_game.dart @@ -0,0 +1,70 @@ +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class BasicFlipperGame extends BasicBallGame with KeyboardEvents { + BasicFlipperGame() : super(color: Colors.blue); + + static const info = 'Shows how a Flipper works.'; + + static const _leftFlipperKeys = [ + LogicalKeyboardKey.arrowLeft, + LogicalKeyboardKey.keyA, + ]; + + static const _rightFlipperKeys = [ + LogicalKeyboardKey.arrowRight, + LogicalKeyboardKey.keyD, + ]; + + late Flipper leftFlipper; + late Flipper rightFlipper; + + @override + Future onLoad() async { + await super.onLoad(); + + final center = screenToWorld(camera.viewport.canvasSize! / 2); + + leftFlipper = Flipper(side: BoardSide.left) + ..initialPosition = center - Vector2(Flipper.size.x, 0); + rightFlipper = Flipper(side: BoardSide.right) + ..initialPosition = center + Vector2(Flipper.size.x, 0); + + await addAll([ + leftFlipper, + rightFlipper, + ]); + } + + @override + KeyEventResult onKeyEvent( + RawKeyEvent event, + Set keysPressed, + ) { + final movedLeftFlipper = _leftFlipperKeys.contains(event.logicalKey); + if (movedLeftFlipper) { + if (event is RawKeyDownEvent) { + leftFlipper.moveUp(); + } else if (event is RawKeyUpEvent) { + leftFlipper.moveDown(); + } + } + + final movedRightFlipper = _rightFlipperKeys.contains(event.logicalKey); + if (movedRightFlipper) { + if (event is RawKeyDownEvent) { + rightFlipper.moveUp(); + } else if (event is RawKeyUpEvent) { + rightFlipper.moveDown(); + } + } + + return movedLeftFlipper || movedRightFlipper + ? KeyEventResult.handled + : KeyEventResult.ignored; + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/flipper_tracing_game.dart b/packages/pinball_components/sandbox/lib/stories/flipper/flipper_tracing_game.dart new file mode 100644 index 00000000..482440cb --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/flipper/flipper_tracing_game.dart @@ -0,0 +1,26 @@ +import 'dart:async'; + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/flipper/basic_flipper_game.dart'; + +class FlipperTracingGame extends BasicFlipperGame { + static const info = ''' + Basic example of how the Flipper body overlays the sprite. +'''; + + @override + Future onLoad() async { + await super.onLoad(); + + leftFlipper.trace(); + leftFlipper.body.joints.whereType().forEach( + (joint) => joint.setLimits(0, 0), + ); + + rightFlipper.trace(); + rightFlipper.body.joints.whereType().forEach( + (joint) => joint.setLimits(0, 0), + ); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/flipper.dart b/packages/pinball_components/sandbox/lib/stories/flipper/stories.dart similarity index 81% rename from packages/pinball_components/sandbox/lib/stories/flipper/flipper.dart rename to packages/pinball_components/sandbox/lib/stories/flipper/stories.dart index 7c8465da..3cad3ade 100644 --- a/packages/pinball_components/sandbox/lib/stories/flipper/flipper.dart +++ b/packages/pinball_components/sandbox/lib/stories/flipper/stories.dart @@ -1,8 +1,8 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/flipper/basic.dart'; -import 'package:sandbox/stories/flipper/tracing.dart'; +import 'package:sandbox/stories/flipper/basic_flipper_game.dart'; +import 'package:sandbox/stories/flipper/flipper_tracing_game.dart'; void addFlipperStories(Dashbook dashbook) { dashbook.storiesOf('Flipper') diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/tracing.dart b/packages/pinball_components/sandbox/lib/stories/flipper/tracing.dart deleted file mode 100644 index d6c5d3df..00000000 --- a/packages/pinball_components/sandbox/lib/stories/flipper/tracing.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'dart:async'; - -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:sandbox/common/common.dart'; - -class FlipperTracingGame extends BasicGame { - static const info = ''' - Basic example of how the Flipper body overlays the sprite. -'''; - - @override - Future onLoad() async { - await super.onLoad(); - final center = screenToWorld(camera.viewport.canvasSize! / 2); - - final leftFlipper = Flipper(side: BoardSide.left) - ..initialPosition = center - Vector2(Flipper.size.x, 0); - final rightFlipper = Flipper(side: BoardSide.right) - ..initialPosition = center + Vector2(Flipper.size.x, 0); - - await addAll([ - leftFlipper, - rightFlipper, - ]); - leftFlipper.trace(); - rightFlipper.trace(); - } -} - -extension on BodyComponent { - void trace({Color color = Colors.red}) { - paint = Paint()..color = color; - renderBody = true; - body.joints.whereType().forEach( - (joint) => joint.setLimits(0, 0), - ); - body.setType(BodyType.static); - - unawaited( - mounted.whenComplete(() { - final sprite = children.whereType().first; - sprite.paint.color = sprite.paint.color.withOpacity(0.5); - }), - ); - } -} diff --git a/packages/pinball_components/sandbox/lib/stories/layer/basic.dart b/packages/pinball_components/sandbox/lib/stories/layer/basic_layer_game.dart similarity index 93% rename from packages/pinball_components/sandbox/lib/stories/layer/basic.dart rename to packages/pinball_components/sandbox/lib/stories/layer/basic_layer_game.dart index ccbd67d9..a6361094 100644 --- a/packages/pinball_components/sandbox/lib/stories/layer/basic.dart +++ b/packages/pinball_components/sandbox/lib/stories/layer/basic_layer_game.dart @@ -8,9 +8,9 @@ class BasicLayerGame extends BasicGame with TapDetector { BasicLayerGame({required this.color}); static const info = ''' - Basic example of how layers work when a Ball hits other components. + Shows how Layers work when a Ball hits other components. - Tap anywhere on the screen to spawn a ball into the game. + Tap anywhere on the screen to spawn a Ball into the game. '''; final Color color; @@ -35,7 +35,7 @@ class BigSquare extends BodyComponent with InitialPosition, Layered { paint = Paint() ..color = const Color.fromARGB(255, 8, 218, 241) ..style = PaintingStyle.stroke; - layer = Layer.jetpack; + layer = Layer.spaceshipEntranceRamp; } @override diff --git a/packages/pinball_components/sandbox/lib/stories/layer/layer.dart b/packages/pinball_components/sandbox/lib/stories/layer/stories.dart similarity index 89% rename from packages/pinball_components/sandbox/lib/stories/layer/layer.dart rename to packages/pinball_components/sandbox/lib/stories/layer/stories.dart index 6d3538dd..12ac028b 100644 --- a/packages/pinball_components/sandbox/lib/stories/layer/layer.dart +++ b/packages/pinball_components/sandbox/lib/stories/layer/stories.dart @@ -2,7 +2,7 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/layer/basic.dart'; +import 'package:sandbox/stories/layer/basic_layer_game.dart'; void addLayerStories(Dashbook dashbook) { dashbook.storiesOf('Layer').add( diff --git a/packages/pinball_components/sandbox/lib/stories/spaceship/basic.dart b/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart similarity index 69% rename from packages/pinball_components/sandbox/lib/stories/spaceship/basic.dart rename to packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart index 6f33f444..97124c3f 100644 --- a/packages/pinball_components/sandbox/lib/stories/spaceship/basic.dart +++ b/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart @@ -5,9 +5,12 @@ import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/common/common.dart'; -class BasicSpaceship extends BasicGame with TapDetector { - static String info = 'Renders a spaceship and allows balls to be ' - 'spawned upon click to test their interactions'; +class BasicSpaceshipGame extends BasicGame with TapDetector { + static const info = ''' + Shows how a Spaceship works. + + Tap anywhere on the screen to spawn a Ball into the game. +'''; @override Future onLoad() async { @@ -25,7 +28,7 @@ class BasicSpaceship extends BasicGame with TapDetector { add( Ball(baseColor: Colors.blue) ..initialPosition = info.eventPosition.game - ..layer = Layer.jetpack, + ..layer = Layer.spaceshipEntranceRamp, ); } } diff --git a/packages/pinball_components/sandbox/lib/stories/spaceship/spaceship.dart b/packages/pinball_components/sandbox/lib/stories/spaceship/stories.dart similarity index 60% rename from packages/pinball_components/sandbox/lib/stories/spaceship/spaceship.dart rename to packages/pinball_components/sandbox/lib/stories/spaceship/stories.dart index 635439ee..ac7720a0 100644 --- a/packages/pinball_components/sandbox/lib/stories/spaceship/spaceship.dart +++ b/packages/pinball_components/sandbox/lib/stories/spaceship/stories.dart @@ -1,13 +1,15 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/spaceship/basic.dart'; +import 'package:sandbox/stories/spaceship/basic_spaceship_game.dart'; void addSpaceshipStories(Dashbook dashbook) { dashbook.storiesOf('Spaceship').add( 'Basic', - (context) => GameWidget(game: BasicSpaceship()), + (context) => GameWidget( + game: BasicSpaceshipGame(), + ), codeLink: buildSourceLink('spaceship/basic.dart'), - info: BasicSpaceship.info, + info: BasicSpaceshipGame.info, ); } diff --git a/packages/pinball_components/sandbox/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index 90b93723..108cca05 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -1,5 +1,8 @@ -export 'ball/ball.dart'; -export 'baseboard/baseboard.dart'; -export 'effects/effects.dart'; -export 'flipper/flipper.dart'; -export 'layer/layer.dart'; +export 'ball/stories.dart'; +export 'baseboard/stories.dart'; +export 'chrome_dino/stories.dart'; +export 'dash_nest_bumper/stories.dart'; +export 'effects/stories.dart'; +export 'flipper/stories.dart'; +export 'layer/stories.dart'; +export 'spaceship/stories.dart'; diff --git a/packages/pinball_components/test/helpers/mocks.dart b/packages/pinball_components/test/helpers/mocks.dart index 7771d1e1..21d5d01a 100644 --- a/packages/pinball_components/test/helpers/mocks.dart +++ b/packages/pinball_components/test/helpers/mocks.dart @@ -17,6 +17,8 @@ class MockSpaceshipEntrance extends Mock implements SpaceshipEntrance {} class MockSpaceshipHole extends Mock implements SpaceshipHole {} +class MockSpaceshipRailExit extends Mock implements SpaceshipRailExit {} + class MockContact extends Mock implements Contact {} class MockContactCallback extends Mock diff --git a/packages/pinball_components/test/src/components/boundaries_test.dart b/packages/pinball_components/test/src/components/boundaries_test.dart new file mode 100644 index 00000000..2c6fe1da --- /dev/null +++ b/packages/pinball_components/test/src/components/boundaries_test.dart @@ -0,0 +1,31 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('Boundaries', () { + final tester = FlameTester(TestGame.new); + + tester.testGameWidget( + 'render correctly', + setUp: (game, tester) async { + await game.addFromBlueprint(Boundaries()); + await game.ready(); + game.camera.followVector2(Vector2.zero()); + game.camera.zoom = 3.9; + }, + // TODO(allisonryan0002): enable test when workflows are fixed. + // verify: (game, tester) async { + // await expectLater( + // find.byGame(), + // matchesGoldenFile('golden/boundaries.png'), + // ); + // }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/chrome_dino_test.dart b/packages/pinball_components/test/src/components/chrome_dino_test.dart new file mode 100644 index 00000000..8a0adb85 --- /dev/null +++ b/packages/pinball_components/test/src/components/chrome_dino_test.dart @@ -0,0 +1,38 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(Forge2DGame.new); + + group('ChromeDino', () { + flameTester.test( + 'loads correctly', + (game) async { + final chromeDino = ChromeDino(); + await game.ensureAdd(chromeDino); + + expect(game.contains(chromeDino), isTrue); + }, + ); + + flameTester.test( + 'swivels', + (game) async { + // TODO(alestiago): Write golden tests to check the + // swivel animation. + final chromeDino = ChromeDino(); + await game.ensureAdd(chromeDino); + + final previousPosition = chromeDino.body.position.clone(); + game.update(64); + + expect(chromeDino.body.position, isNot(equals(previousPosition))); + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/golden/boundaries.png b/packages/pinball_components/test/src/components/golden/boundaries.png new file mode 100644 index 00000000..d1ef9ce1 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/boundaries.png differ diff --git a/packages/pinball_components/test/src/components/golden/launch-ramp.png b/packages/pinball_components/test/src/components/golden/launch-ramp.png new file mode 100644 index 00000000..e872c533 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/launch-ramp.png differ diff --git a/test/game/components/kicker_test.dart b/packages/pinball_components/test/src/components/kicker_test.dart similarity index 97% rename from test/game/components/kicker_test.dart rename to packages/pinball_components/test/src/components/kicker_test.dart index 333c7fbe..4af7b8b1 100644 --- a/test/game/components/kicker_test.dart +++ b/packages/pinball_components/test/src/components/kicker_test.dart @@ -3,7 +3,6 @@ import 'package:flame_forge2d/flame_forge2d.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'; void main() { diff --git a/packages/pinball_components/test/src/components/launch_ramp_test.dart b/packages/pinball_components/test/src/components/launch_ramp_test.dart new file mode 100644 index 00000000..c7c7b1b2 --- /dev/null +++ b/packages/pinball_components/test/src/components/launch_ramp_test.dart @@ -0,0 +1,31 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('LaunchRamp', () { + final tester = FlameTester(TestGame.new); + + tester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.addFromBlueprint(LaunchRamp()); + await game.ready(); + game.camera.followVector2(Vector2.zero()); + game.camera.zoom = 4.1; + }, + // TODO(allisonryan0002): enable test when workflows are fixed. + // verify: (game, tester) async { + // await expectLater( + // find.byGame(), + // matchesGoldenFile('golden/launch-ramp.png'), + // ); + // }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/layer_test.dart b/packages/pinball_components/test/src/components/layer_test.dart index 537e01b6..f2372c45 100644 --- a/packages/pinball_components/test/src/components/layer_test.dart +++ b/packages/pinball_components/test/src/components/layer_test.dart @@ -46,14 +46,15 @@ void main() { }); test('correctly sets and gets', () { - final component = TestLayeredBodyComponent()..layer = Layer.jetpack; - expect(component.layer, Layer.jetpack); + final component = TestLayeredBodyComponent() + ..layer = Layer.spaceshipEntranceRamp; + expect(component.layer, Layer.spaceshipEntranceRamp); }); flameTester.test( 'layers correctly before being loaded', (game) async { - const expectedLayer = Layer.jetpack; + const expectedLayer = Layer.spaceshipEntranceRamp; final component = TestLayeredBodyComponent()..layer = expectedLayer; await game.ensureAdd(component); // TODO(alestiago): modify once component.loaded is available. @@ -71,7 +72,8 @@ void main() { 'when multiple different sets', (game) async { const expectedLayer = Layer.launcher; - final component = TestLayeredBodyComponent()..layer = Layer.jetpack; + final component = TestLayeredBodyComponent() + ..layer = Layer.spaceshipEntranceRamp; expect(component.layer, isNot(equals(expectedLayer))); component.layer = expectedLayer; @@ -90,7 +92,7 @@ void main() { flameTester.test( 'layers correctly after being loaded', (game) async { - const expectedLayer = Layer.jetpack; + const expectedLayer = Layer.spaceshipEntranceRamp; final component = TestLayeredBodyComponent(); await game.ensureAdd(component); component.layer = expectedLayer; @@ -109,7 +111,7 @@ void main() { final component = TestLayeredBodyComponent(); await game.ensureAdd(component); - component.layer = Layer.jetpack; + component.layer = Layer.spaceshipEntranceRamp; expect(component.layer, isNot(equals(expectedLayer))); component.layer = expectedLayer; @@ -133,7 +135,7 @@ void main() { flameTester.test( 'nested Layered children will keep their layer', (game) async { - const parentLayer = Layer.jetpack; + const parentLayer = Layer.spaceshipEntranceRamp; const childLayer = Layer.board; final component = TestLayeredBodyComponent()..layer = parentLayer; @@ -152,7 +154,7 @@ void main() { flameTester.test( 'nested children will keep their layer', (game) async { - const parentLayer = Layer.jetpack; + const parentLayer = Layer.spaceshipEntranceRamp; final component = TestLayeredBodyComponent()..layer = parentLayer; final childComponent = TestBodyComponent(); diff --git a/packages/pinball_components/test/src/components/ramp_opening_test.dart b/packages/pinball_components/test/src/components/ramp_opening_test.dart index cb42203a..46e56e3d 100644 --- a/packages/pinball_components/test/src/components/ramp_opening_test.dart +++ b/packages/pinball_components/test/src/components/ramp_opening_test.dart @@ -44,7 +44,7 @@ void main() { (game) async { final ramp = TestRampOpening( orientation: RampOrientation.down, - pathwayLayer: Layer.jetpack, + pathwayLayer: Layer.spaceshipEntranceRamp, ); await game.ready(); await game.ensureAdd(ramp); @@ -59,7 +59,7 @@ void main() { (game) async { final ramp = TestRampOpening( orientation: RampOrientation.down, - pathwayLayer: Layer.jetpack, + pathwayLayer: Layer.spaceshipEntranceRamp, ); await game.ensureAdd(ramp); @@ -68,7 +68,7 @@ void main() { ); group('first fixture', () { - const pathwayLayer = Layer.jetpack; + const pathwayLayer = Layer.spaceshipEntranceRamp; const openingLayer = Layer.opening; flameTester.test( @@ -124,7 +124,7 @@ void main() { final body = MockBody(); final area = TestRampOpening( orientation: RampOrientation.down, - pathwayLayer: Layer.jetpack, + pathwayLayer: Layer.spaceshipEntranceRamp, ); final callback = TestRampOpeningBallContactCallback(); @@ -145,7 +145,7 @@ void main() { final body = MockBody(); final area = TestRampOpening( orientation: RampOrientation.up, - pathwayLayer: Layer.jetpack, + pathwayLayer: Layer.spaceshipEntranceRamp, ); final callback = TestRampOpeningBallContactCallback(); @@ -165,7 +165,7 @@ void main() { final body = MockBody(); final area = TestRampOpening( orientation: RampOrientation.down, - pathwayLayer: Layer.jetpack, + pathwayLayer: Layer.spaceshipEntranceRamp, )..initialPosition = Vector2(0, 10); final callback = TestRampOpeningBallContactCallback(); @@ -189,7 +189,7 @@ void main() { final body = MockBody(); final area = TestRampOpening( orientation: RampOrientation.up, - pathwayLayer: Layer.jetpack, + pathwayLayer: Layer.spaceshipEntranceRamp, )..initialPosition = Vector2(0, 10); final callback = TestRampOpeningBallContactCallback(); @@ -213,7 +213,7 @@ void main() { final body = MockBody(); final area = TestRampOpening( orientation: RampOrientation.down, - pathwayLayer: Layer.jetpack, + pathwayLayer: Layer.spaceshipEntranceRamp, )..initialPosition = Vector2(0, 10); final callback = TestRampOpeningBallContactCallback(); diff --git a/packages/pinball_components/test/src/components/spaceship_rail_test.dart b/packages/pinball_components/test/src/components/spaceship_rail_test.dart new file mode 100644 index 00000000..6c5410b1 --- /dev/null +++ b/packages/pinball_components/test/src/components/spaceship_rail_test.dart @@ -0,0 +1,82 @@ +// ignore_for_file: cascade_invocations + +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_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('SpaceshipRail', () { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + flameTester.test( + 'loads correctly', + (game) async { + final spaceshipRail = SpaceshipRail(); + await game.addFromBlueprint(spaceshipRail); + await game.ready(); + + for (final element in spaceshipRail.components) { + expect(game.contains(element), isTrue); + } + }, + ); + }); + + // TODO(alestiago): Make ContactCallback private and use `beginContact` + // instead. + group('SpaceshipRailExitBallContactCallback', () { + late Forge2DGame game; + late SpaceshipRailExit railExit; + late Ball ball; + late Body body; + late Fixture fixture; + late Filter filterData; + + setUp(() { + game = MockGame(); + + railExit = MockSpaceshipRailExit(); + + 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); + }); + + setUp(() { + when(() => ball.priority).thenReturn(1); + when(() => railExit.outsideLayer).thenReturn(Layer.board); + when(() => railExit.outsidePriority).thenReturn(0); + }); + + test('changes the ball priority on contact', () { + SpaceshipRailExitBallContactCallback().begin( + railExit, + ball, + MockContact(), + ); + + verify(() => ball.sendTo(railExit.outsidePriority)).called(1); + }); + + test('changes the ball layer on contact', () { + SpaceshipRailExitBallContactCallback().begin( + railExit, + ball, + MockContact(), + ); + + verify(() => ball.layer = railExit.outsideLayer).called(1); + }); + }); +} diff --git a/packages/pinball_components/test/src/components/spaceship_ramp_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp_test.dart new file mode 100644 index 00000000..4d124596 --- /dev/null +++ b/packages/pinball_components/test/src/components/spaceship_ramp_test.dart @@ -0,0 +1,27 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('SpaceshipRamp', () { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + flameTester.test( + 'loads correctly', + (game) async { + final spaceshipEntranceRamp = SpaceshipRamp(); + await game.addFromBlueprint(spaceshipEntranceRamp); + await game.ready(); + + for (final element in spaceshipEntranceRamp.components) { + expect(game.contains(element), isTrue); + } + }, + ); + }); +} diff --git a/test/game/components/chrome_dino_test.dart b/test/game/components/chrome_dino_test.dart deleted file mode 100644 index af53ffea..00000000 --- a/test/game/components/chrome_dino_test.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flame_test/flame_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/game/game.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(Forge2DGame.new); - - group('ChromeDino', () { - flameTester.test( - 'loads correctly', - (game) async { - final chromeDino = ChromeDino(); - - await game.ready(); - await game.ensureAdd(chromeDino); - - expect(game.contains(chromeDino), isTrue); - }, - ); - }); -} diff --git a/test/game/components/controlled_ball_test.dart b/test/game/components/controlled_ball_test.dart index 8417aa25..f9494543 100644 --- a/test/game/components/controlled_ball_test.dart +++ b/test/game/components/controlled_ball_test.dart @@ -176,7 +176,7 @@ void main() { await game.ensureAdd(ball); final state = MockGameState(); - when(() => state.balls).thenReturn(1); + when(() => state.balls).thenReturn(0); final previousBalls = game.descendants().whereType().toList(); controller.onNewState(state); diff --git a/test/game/components/flipper_controller_test.dart b/test/game/components/controlled_flipper_test.dart similarity index 100% rename from test/game/components/flipper_controller_test.dart rename to test/game/components/controlled_flipper_test.dart diff --git a/test/game/components/spaceship_exit_rail_test.dart b/test/game/components/spaceship_exit_rail_test.dart deleted file mode 100644 index 99afc808..00000000 --- a/test/game/components/spaceship_exit_rail_test.dart +++ /dev/null @@ -1,60 +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_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 c6c2513c..c0dec5f5 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -67,8 +67,6 @@ class MockFilter extends Mock implements Filter {} class MockFixture extends Mock implements Fixture {} -class MockSpaceshipExitRailEnd extends Mock implements SpaceshipExitRailEnd {} - class MockComponentSet extends Mock implements ComponentSet {} class MockDashNestBumper extends Mock implements DashNestBumper {}