diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 4e165148..ea299c7b 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -11,6 +11,7 @@ export 'launcher_ramp.dart'; export 'layer.dart'; export 'pathway.dart'; export 'plunger.dart'; +export 'ramp_opening.dart'; export 'round_bumper.dart'; export 'score_points.dart'; export 'sling_shot.dart'; diff --git a/lib/game/components/layer.dart b/lib/game/components/layer.dart index d471403c..bbeedb7d 100644 --- a/lib/game/components/layer.dart +++ b/lib/game/components/layer.dart @@ -1,7 +1,4 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball/game/game.dart'; /// Modifies maskBits of [BodyComponent] to control what other bodies it can /// have physical interactions with. @@ -70,97 +67,3 @@ extension LayerX on Layer { } } } - -/// Indicates the orientation of a ramp entrance/exit. -/// -/// Used to know if a ramp is facing up or down on the board. -enum RampOrientation { - /// Facing up on the board. - up, - - /// Facing down on the board. - down, -} - -/// {@template ramp_opening} -/// [BodyComponent] located at the entrance and exit of a ramp. -/// -/// [RampOpeningBallContactCallback] detects when a [Ball] passes -/// through this opening. By default openings are [Layer.board] that -/// means opening are at ground level, not over board. -/// {@endtemplate} -abstract class RampOpening extends BodyComponent with InitialPosition { - /// {@macro ramp_opening} - RampOpening({ - required Layer pathwayLayer, - Layer? openingLayer, - }) : _pathwayLayer = pathwayLayer, - _openingLayer = openingLayer ?? Layer.board; - - final Layer _openingLayer; - final Layer _pathwayLayer; - - /// Mask of category bits for collision with [RampOpening]. - Layer get openingLayer => _openingLayer; - - /// Mask of category bits for collision inside [Pathway]. - Layer get pathwayLayer => _pathwayLayer; - - /// The [Shape] of the [RampOpening]. - Shape get shape; - - /// Orientation of the [RampOpening] entrance/exit - // TODO(ruimiguel): Try to remove the need of [RampOrientation] for collision - // calculations. - RampOrientation get orientation; - - @override - Body createBody() { - final fixtureDef = FixtureDef(shape) - ..isSensor = true - ..filter.categoryBits = _openingLayer.maskBits; - - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..type = BodyType.static; - - return world.createBody(bodyDef)..createFixture(fixtureDef); - } -} - -/// {@template ramp_opening_ball_contact_callback} -/// Detects when a [Ball] enters or exits a [Pathway] ramp through a -/// [RampOpening]. -/// -/// Modifies [Ball]'s maskBits while it is inside the ramp. When [Ball] exits, -/// sets maskBits to collide with all elements. -/// {@endtemplate} -class RampOpeningBallContactCallback - extends ContactCallback { - /// Collection of balls inside ramp pathway. - final _ballsInside = {}; - - @override - void begin(Ball ball, Opening opening, Contact _) { - late final Layer layer; - if (!_ballsInside.contains(ball)) { - layer = opening.pathwayLayer; - _ballsInside.add(ball); - } else { - layer = Layer.board; - _ballsInside.remove(ball); - } - - ball.layer = layer; - } - - @override - void end(Ball ball, Opening opening, Contact _) { - final isBallOutsideOpening = opening.orientation == RampOrientation.up - ? ball.body.position.y > opening.body.position.y - : ball.body.position.y < opening.body.position.y; - - if (isBallOutsideOpening) ball.layer = Layer.board; - } -} diff --git a/lib/game/components/ramp_opening.dart b/lib/game/components/ramp_opening.dart new file mode 100644 index 00000000..64adf1eb --- /dev/null +++ b/lib/game/components/ramp_opening.dart @@ -0,0 +1,98 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball/game/game.dart'; + +/// Indicates the orientation of a ramp entrance/exit. +/// +/// Used to know if a ramp is facing up or down on the board. +enum RampOrientation { + /// Facing up on the board. + up, + + /// Facing down on the board. + down, +} + +/// {@template ramp_opening} +/// [BodyComponent] located at the entrance and exit of a ramp. +/// +/// [RampOpeningBallContactCallback] detects when a [Ball] passes +/// through this opening. By default openings are [Layer.board] that +/// means opening are at ground level, not over board. +/// {@endtemplate} +abstract class RampOpening extends BodyComponent with InitialPosition { + /// {@macro ramp_opening} + RampOpening({ + required Layer pathwayLayer, + Layer? openingLayer, + }) : _pathwayLayer = pathwayLayer, + _openingLayer = openingLayer ?? Layer.board; + + final Layer _openingLayer; + final Layer _pathwayLayer; + + /// Mask of category bits for collision with [RampOpening]. + Layer get openingLayer => _openingLayer; + + /// Mask of category bits for collision inside [Pathway]. + Layer get pathwayLayer => _pathwayLayer; + + /// The [Shape] of the [RampOpening]. + Shape get shape; + + /// Orientation of the [RampOpening] entrance/exit + // TODO(ruimiguel): Try to remove the need of [RampOrientation] for collision + // calculations. + RampOrientation get orientation; + + @override + Body createBody() { + final fixtureDef = FixtureDef(shape) + ..isSensor = true + ..filter.categoryBits = _openingLayer.maskBits; + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition + ..type = BodyType.static; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + +/// {@template ramp_opening_ball_contact_callback} +/// Detects when a [Ball] enters or exits a [Pathway] ramp through a +/// [RampOpening]. +/// +/// Modifies [Ball]'s maskBits while it is inside the ramp. When [Ball] exits, +/// sets maskBits to collide with all elements. +/// {@endtemplate} +class RampOpeningBallContactCallback + extends ContactCallback { + /// Collection of balls inside ramp pathway. + final _ballsInside = {}; + + @override + void begin(Ball ball, Opening opening, Contact _) { + late final Layer layer; + if (!_ballsInside.contains(ball)) { + layer = opening.pathwayLayer; + _ballsInside.add(ball); + } else { + layer = Layer.board; + _ballsInside.remove(ball); + } + + ball.layer = layer; + } + + @override + void end(Ball ball, Opening opening, Contact _) { + final isBallOutsideOpening = opening.orientation == RampOrientation.up + ? ball.body.position.y > opening.body.position.y + : ball.body.position.y < opening.body.position.y; + + if (isBallOutsideOpening) ball.layer = Layer.board; + } +}