diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 918112d9..1d112491 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -5,11 +5,11 @@ export 'bonus_word.dart'; export 'flipper.dart'; export 'jetpack_ramp.dart'; export 'joint_anchor.dart'; +export 'launcher_ramp.dart'; export 'layer.dart'; export 'pathway.dart'; export 'plunger.dart'; export 'round_bumper.dart'; export 'score_points.dart'; export 'sling_shot.dart'; -export 'sparky_ramp.dart'; export 'wall.dart'; diff --git a/lib/game/components/launcher_ramp.dart b/lib/game/components/launcher_ramp.dart new file mode 100644 index 00000000..5f90a228 --- /dev/null +++ b/lib/game/components/launcher_ramp.dart @@ -0,0 +1,122 @@ +import 'package:flame/components.dart'; +import 'package:flame/extensions.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball/game/game.dart'; + +/// {@template launcher_ramp} +/// Represent the launcher green and upper right yellow ramps for the game. +/// +/// Group of [Component]s composed by a [Pathway.arc] as the upper curve ramp, +/// a [Pathway.straight] for the launcher straight ramp, and two +/// [LauncherRampOpening] at the entrance and exit of the ramp, to detect when +/// a ball gets into/out of the ramp. +/// {@endtemplate} +class LauncherRamp extends Component with HasGameRef { + /// {@macro launcher_ramp} + LauncherRamp({ + required this.position, + }); + + final double _radius = 300; + final double _width = 80; + final double _angle = radians(200); + + /// The position of this [LauncherRamp] + final Vector2 position; + + @override + Future onLoad() async { + await add( + Pathway.straight( + color: const Color.fromARGB(255, 34, 255, 0), + position: position, + start: Vector2(0, 0), + end: Vector2(0, 600), + width: 80, + ), + ); + + await add( + Pathway.arc( + color: const Color.fromARGB(255, 251, 255, 0), + position: position + Vector2(-28.8, -6), + radius: _radius, + angle: _angle, + width: _width, + layer: Layer.launcher, + ), + ); + await add( + LauncherRampOpening( + position: position + Vector2(-46.5, -9), + orientation: RampOrientation.down, + rotation: radians(13), + ), + ); + await add( + LauncherRampOpening( + position: position + Vector2(4, 0), + orientation: RampOrientation.down, + ), + ); + + gameRef.addContactCallback(LauncherRampOpeningBallContactCallback()); + } +} + +/// {@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 Vector2 position, + double rotation = 0, + required RampOrientation orientation, + }) : _rotation = rotation, + _orientation = orientation, + super( + position: position, + layer: Layer.launcher, + ); + + /// Orientation of entrance/exit of [LauncherRamp] where + /// this [LauncherRampOpening] is placed. + final RampOrientation _orientation; + + /// Rotation of the [RampOpening] to place it right at the + /// entrance/exit of [LauncherRamp]. + final double _rotation; + + /// Size of the [RampOpening] placed at the entrance/exit of [LauncherRamp]. + final int _size = 7; + + @override + RampOrientation get orientation => _orientation; + + @override + Shape get shape => PolygonShape() + ..set([ + Vector2(-_size / 2, -.1)..rotate(_rotation), + Vector2(-_size / 2, .1)..rotate(_rotation), + Vector2(_size / 2, .1)..rotate(_rotation), + Vector2(_size / 2, -.1)..rotate(_rotation), + ]); +} + +/// {@template launcher_ramp_opening_ball_contact_callback} +/// Detects when a [Ball] enters or exits the [LauncherRamp] through a +/// [LauncherRampOpening]. +/// {@endtemplate} +class LauncherRampOpeningBallContactCallback + extends RampOpeningBallContactCallback { + /// {@macro launcher_ramp_opening_ball_contact_callback} + LauncherRampOpeningBallContactCallback() : super(); + + /// Collection of balls inside [LauncherRamp]. + final _ballsInsideLauncher = {}; + + @override + Set get ballsInside => _ballsInsideLauncher; +} diff --git a/lib/game/components/layer.dart b/lib/game/components/layer.dart index 558ad9dd..fc53bc14 100644 --- a/lib/game/components/layer.dart +++ b/lib/game/components/layer.dart @@ -32,8 +32,8 @@ enum Layer { /// Collide only with Jetpack group elements. jetpack, - /// Collide only with Sparky group elements. - sparky, + /// Collide only with Launcher group elements. + launcher, } /// Utility methods for [Layer]. @@ -45,7 +45,7 @@ extension LayerX on Layer { return 0xFFFF; case Layer.jetpack: return 0x0010; - case Layer.sparky: + case Layer.launcher: return 0x0100; } } diff --git a/lib/game/components/sparky_ramp.dart b/lib/game/components/sparky_ramp.dart deleted file mode 100644 index 54aaf33d..00000000 --- a/lib/game/components/sparky_ramp.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'package:flame/components.dart'; -import 'package:flame/extensions.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball/game/game.dart'; - -/// {@template sparky_ramp} -/// Represent the upper right yellow ramp for the game. -/// -/// Group of [Component]s composed by a [Pathway.arc] as the ramp, and two -/// [SparkyRampOpening] at the entrance and exit of the ramp, to detect when -/// a ball gets into/out of the ramp. -/// {@endtemplate} -class SparkyRamp extends Component with HasGameRef { - /// {@macro sparky_ramp} - SparkyRamp({ - required this.position, - }); - - final double _radius = 300; - final double _width = 80; - final double _angle = radians(200); - - /// The position of this [SparkyRamp] - final Vector2 position; - - @override - Future onLoad() async { - await add( - Pathway.arc( - color: const Color.fromARGB(255, 251, 255, 0), - position: position, - radius: _radius, - angle: _angle, - width: _width, - layer: Layer.sparky, - ), - ); - await add( - SparkyRampOpening( - position: position + Vector2(-18, -2), - orientation: RampOrientation.down, - rotation: radians(13), - ), - ); - await add( - SparkyRampOpening( - position: position + Vector2(33, 6), - orientation: RampOrientation.down, - ), - ); - - gameRef.addContactCallback(SparkyRampOpeningBallContactCallback()); - } -} - -/// {@template sparky_ramp_opening} -/// [RampOpening] with [Layer.sparky] to filter [Ball]s collisions -/// inside [SparkyRamp]. -/// {@endtemplate} -class SparkyRampOpening extends RampOpening { - /// {@macro sparky_ramp_opening} - SparkyRampOpening({ - required Vector2 position, - double rotation = 0, - required RampOrientation orientation, - }) : _rotation = rotation, - _orientation = orientation, - super( - position: position, - layer: Layer.sparky, - ); - - /// Orientation of entrance/exit of [SparkyRamp] where - /// this [SparkyRampOpening] is placed. - final RampOrientation _orientation; - - /// Rotation of the [RampOpening] to place it right at the - /// entrance/exit of [SparkyRamp]. - final double _rotation; - - /// Size of the [RampOpening] placed at the entrance/exit of [SparkyRamp]. - final int _size = 7; - - @override - RampOrientation get orientation => _orientation; - - @override - Shape get shape => PolygonShape() - ..set([ - Vector2(-_size / 2, -.1)..rotate(_rotation), - Vector2(-_size / 2, .1)..rotate(_rotation), - Vector2(_size / 2, .1)..rotate(_rotation), - Vector2(_size / 2, -.1)..rotate(_rotation), - ]); -} - -/// {@template sparky_ramp_opening_ball_contact_callback} -/// Detects when a [Ball] enters or exits the [SparkyRamp] through a -/// [SparkyRampOpening]. -/// {@endtemplate} -class SparkyRampOpeningBallContactCallback - extends RampOpeningBallContactCallback { - /// {@macro sparky_ramp_opening_ball_contact_callback} - SparkyRampOpeningBallContactCallback() : super(); - - /// Collection of balls inside [SparkyRamp]. - final _ballsInsideSparky = {}; - - @override - Set get ballsInside => _ballsInsideSparky; -} diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 3223bf15..6e695d96 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -1,7 +1,6 @@ // ignore_for_file: public_member_api_docs import 'dart:async'; -import 'dart:ui'; import 'package:flame/input.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; @@ -31,14 +30,6 @@ class PinballGame extends Forge2DGame Vector2(-150, -150), ); - late final sparkyRampPosition = screenToWorld( - Vector2( - camera.viewport.effectiveSize.x / 2, - camera.viewport.effectiveSize.y / 2, - ) + - Vector2(80, -100), - ); - @override void onAttach() { super.onAttach(); @@ -126,16 +117,6 @@ class PinballGame extends Forge2DGame } Future _addPaths() async { - await add( - Pathway.straight( - color: const Color.fromARGB(255, 34, 255, 0), - position: launcherRampPosition, - start: Vector2(0, 0), - end: Vector2(0, 600), - width: 80, - ), - ); - await add( JetpackRamp( position: jetpackRampPosition, @@ -143,8 +124,8 @@ class PinballGame extends Forge2DGame ); await add( - SparkyRamp( - position: sparkyRampPosition, + LauncherRamp( + position: launcherRampPosition, ), ); } diff --git a/test/game/components/layer_test.dart b/test/game/components/layer_test.dart index f8df221d..c75e70d1 100644 --- a/test/game/components/layer_test.dart +++ b/test/game/components/layer_test.dart @@ -50,8 +50,8 @@ void main() { group('LayerX', () { test('all types are different', () { expect(Layer.all.maskBits, isNot(equals(Layer.jetpack.maskBits))); - expect(Layer.jetpack.maskBits, isNot(equals(Layer.sparky.maskBits))); - expect(Layer.sparky.maskBits, isNot(equals(Layer.all.maskBits))); + expect(Layer.jetpack.maskBits, isNot(equals(Layer.launcher.maskBits))); + expect(Layer.launcher.maskBits, isNot(equals(Layer.all.maskBits))); }); test('all type has default maskBits', () { @@ -62,8 +62,8 @@ void main() { expect(Layer.jetpack.maskBits, equals(0x0010)); }); - test('sparky type has 0x0100 maskBits', () { - expect(Layer.sparky.maskBits, equals(0x0100)); + test('launcher type has 0x0100 maskBits', () { + expect(Layer.launcher.maskBits, equals(0x0100)); }); }); diff --git a/test/game/components/sparky_ramp_test.dart b/test/game/components/sparky_ramp_test.dart index 32ce0845..5aa56027 100644 --- a/test/game/components/sparky_ramp_test.dart +++ b/test/game/components/sparky_ramp_test.dart @@ -9,17 +9,17 @@ import 'package:pinball/game/game.dart'; import '../../helpers/helpers.dart'; -class MockSparkyRampArea extends Mock implements SparkyRampOpening {} +class MockLauncherRampArea extends Mock implements LauncherRampOpening {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(PinballGameTest.create); - group('SparkyRamp', () { + group('LauncherRamp', () { flameTester.test( 'loads correctly', (game) async { - final ramp = SparkyRamp( + final ramp = LauncherRamp( position: Vector2.zero(), ); await game.ready(); @@ -34,7 +34,7 @@ void main() { 'positions correctly', (game) async { final position = Vector2.all(10); - final ramp = SparkyRamp( + final ramp = LauncherRamp( position: position, ); await game.ensureAdd(ramp); @@ -46,46 +46,42 @@ void main() { group('children', () { flameTester.test( - 'has only one Pathway.arc', + 'has two Pathway', (game) async { - final ramp = SparkyRamp( + final ramp = LauncherRamp( position: Vector2.zero(), ); await game.ready(); await game.ensureAdd(ramp); - expect( - () => ramp.children.singleWhere( - (component) => component is Pathway, - ), - returnsNormally, - ); + final pathways = ramp.children.whereType().toList(); + expect(pathways.length, 2); }, ); flameTester.test( 'has a two sensors for the ramp', (game) async { - final ramp = SparkyRamp( + final ramp = LauncherRamp( position: Vector2.zero(), ); await game.ready(); await game.ensureAdd(ramp); final rampAreas = - ramp.children.whereType().toList(); + ramp.children.whereType().toList(); expect(rampAreas.length, 2); }, ); }); }); - group('SparkyRampOpening', () { + group('LauncherRampOpening', () { flameTester.test( 'orientation is down', (game) async { final position = Vector2.all(10); - final ramp = SparkyRampOpening( + final ramp = LauncherRampOpening( position: position, orientation: RampOrientation.down, ); @@ -97,10 +93,10 @@ void main() { ); }); - group('SparkyRampOpeningBallContactCallback', () { + group('LauncherRampOpeningBallContactCallback', () { test('has no ball inside on creation', () { expect( - SparkyRampOpeningBallContactCallback().ballsInside, + LauncherRampOpeningBallContactCallback().ballsInside, equals({}), ); }); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index e7de7f0c..12e82b91 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -88,11 +88,11 @@ void main() { ); flameTester.test( - 'has only one SparkyRamp', + 'has only one LauncherRamp', (game) async { await game.ready(); - final rampAreas = game.children.whereType().toList(); + final rampAreas = game.children.whereType().toList(); expect(rampAreas.length, 1); }, );