From e1bdb0e14b8addf29974883811cdc6bef8c144cd Mon Sep 17 00:00:00 2001 From: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> Date: Wed, 13 Apr 2022 09:14:51 -0500 Subject: [PATCH 1/2] feat: prevent re-entering launch ramp (#174) * fix: launch ramp render issue * feat: prevent going back into launch ramp * fix: changes from merge --- lib/game/components/controlled_ball.dart | 1 + lib/game/pinball_game.dart | 2 +- .../lib/src/components/ball.dart | 2 +- .../lib/src/components/boundaries.dart | 6 +- .../lib/src/components/dino_walls.dart | 2 +- .../lib/src/components/launch_ramp.dart | 55 ++++++++++++------- .../lib/src/components/layer.dart | 2 +- .../lib/src/components/plunger.dart | 6 +- 8 files changed, 48 insertions(+), 28 deletions(-) diff --git a/lib/game/components/controlled_ball.dart b/lib/game/components/controlled_ball.dart index 7a94f96b..d4f37bc7 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -19,6 +19,7 @@ class ControlledBall extends Ball with Controls { }) : super(baseColor: theme.characterTheme.ballColor) { controller = BallController(this); priority = Ball.launchRampPriority; + layer = Layer.launcher; } /// {@template bonus_ball} diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index cad2eac5..98083269 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -172,7 +172,7 @@ class DebugPinballGame extends PinballGame with TapDetector { anchor: Anchor.center, ) ..position = Vector2(0, -7.8) - ..priority = -2; + ..priority = -4; await add(spriteComponent); } diff --git a/packages/pinball_components/lib/src/components/ball.dart b/packages/pinball_components/lib/src/components/ball.dart index 71719a0e..b8c8e231 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball.dart @@ -35,7 +35,7 @@ class Ball extends BodyComponent static const int spaceshipRailPriority = 2; /// Render priority for the [Ball] while it's on the [LaunchRamp]. - static const int launchRampPriority = 0; + static const int launchRampPriority = -2; /// The size of the [Ball]. static final Vector2 size = Vector2.all(4.13); diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart index 16c0f7b7..e5aab5af 100644 --- a/packages/pinball_components/lib/src/components/boundaries.dart +++ b/packages/pinball_components/lib/src/components/boundaries.dart @@ -13,7 +13,7 @@ class Boundaries extends Forge2DBlueprint { final bottomBoundary = _BottomBoundary(); final outerBoundary = _OuterBoundary(); - addAll([bottomBoundary, outerBoundary]); + addAll([outerBoundary, bottomBoundary]); } } @@ -23,7 +23,7 @@ class Boundaries extends Forge2DBlueprint { /// {@endtemplate bottom_boundary} class _BottomBoundary extends BodyComponent with InitialPosition { /// {@macro bottom_boundary} - _BottomBoundary() : super(priority: 2); + _BottomBoundary() : super(priority: 1); List _createFixtureDefs() { final fixturesDefs = []; @@ -88,7 +88,7 @@ class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef { /// {@endtemplate outer_boundary} class _OuterBoundary extends BodyComponent with InitialPosition { /// {@macro outer_boundary} - _OuterBoundary() : super(priority: -1); + _OuterBoundary() : super(priority: Ball.launchRampPriority - 1); List _createFixtureDefs() { final fixturesDefs = []; diff --git a/packages/pinball_components/lib/src/components/dino_walls.dart b/packages/pinball_components/lib/src/components/dino_walls.dart index dc5b7a26..aec4c7aa 100644 --- a/packages/pinball_components/lib/src/components/dino_walls.dart +++ b/packages/pinball_components/lib/src/components/dino_walls.dart @@ -124,7 +124,7 @@ class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef { /// {@endtemplate} class _DinoBottomWall extends BodyComponent with InitialPosition { ///{@macro dino_top_wall} - _DinoBottomWall() : super(priority: 1); + _DinoBottomWall(); List _createFixtureDefs() { final fixturesDef = []; diff --git a/packages/pinball_components/lib/src/components/launch_ramp.dart b/packages/pinball_components/lib/src/components/launch_ramp.dart index 02ac742a..e92e1124 100644 --- a/packages/pinball_components/lib/src/components/launch_ramp.dart +++ b/packages/pinball_components/lib/src/components/launch_ramp.dart @@ -17,20 +17,21 @@ class LaunchRamp extends Forge2DBlueprint { RampOpeningBallContactCallback<_LaunchRampExit>(), ]); - final launchRampBase = _LaunchRampBase()..layer = Layer.launcher; + final launchRampBase = _LaunchRampBase(); - final launchRampForegroundRailing = _LaunchRampForegroundRailing() - ..layer = Layer.launcher; + final launchRampForegroundRailing = _LaunchRampForegroundRailing(); final launchRampExit = _LaunchRampExit(rotation: math.pi / 2) - ..initialPosition = Vector2(1.8, 34.2) - ..layer = Layer.opening - ..renderBody = false; + ..initialPosition = Vector2(0.6, 34); + + final launchRampCloseWall = _LaunchRampCloseWall() + ..initialPosition = Vector2(4, 66.5); addAll([ launchRampBase, launchRampForegroundRailing, launchRampExit, + launchRampCloseWall, ]); } } @@ -40,10 +41,7 @@ class LaunchRamp extends Forge2DBlueprint { /// {@endtemplate} class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { /// {@macro launch_ramp_base} - _LaunchRampBase() - : super( - priority: Ball.launchRampPriority - 1, - ) { + _LaunchRampBase() : super(priority: Ball.launchRampPriority - 1) { layer = Layer.launcher; } @@ -143,15 +141,9 @@ class _LaunchRampBaseSpriteComponent extends SpriteComponent with HasGameRef { /// Foreground railing for the [_LaunchRampBase] to render in front of the /// [Ball]. /// {@endtemplate} -class _LaunchRampForegroundRailing extends BodyComponent - with InitialPosition, Layered { +class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition { /// {@macro launch_ramp_foreground_railing} - _LaunchRampForegroundRailing() - : super( - priority: Ball.launchRampPriority + 1, - ) { - layer = Layer.launcher; - } + _LaunchRampForegroundRailing() : super(priority: Ball.launchRampPriority + 1); List _createFixtureDefs() { final fixturesDef = []; @@ -221,6 +213,26 @@ class _LaunchRampForegroundRailingSpriteComponent extends SpriteComponent } } +class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered { + _LaunchRampCloseWall() { + layer = Layer.board; + renderBody = false; + } + + @override + Body createBody() { + final shape = EdgeShape()..set(Vector2.zero(), Vector2(0, 4)); + + final fixtureDef = FixtureDef(shape); + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + /// {@template launch_ramp_exit} /// [RampOpening] with [Layer.launcher] to filter [Ball]s exiting the /// [LaunchRamp]. @@ -232,9 +244,14 @@ class _LaunchRampExit extends RampOpening { }) : _rotation = rotation, super( insideLayer: Layer.launcher, + outsideLayer: Layer.board, orientation: RampOrientation.down, insidePriority: Ball.launchRampPriority, - ); + outsidePriority: 0, + ) { + layer = Layer.launcher; + renderBody = false; + } final double _rotation; diff --git a/packages/pinball_components/lib/src/components/layer.dart b/packages/pinball_components/lib/src/components/layer.dart index 6f027d4c..9b20ecf2 100644 --- a/packages/pinball_components/lib/src/components/layer.dart +++ b/packages/pinball_components/lib/src/components/layer.dart @@ -88,7 +88,7 @@ extension LayerMaskBits on Layer { case Layer.spaceshipEntranceRamp: return 0x0002; case Layer.launcher: - return 0x0005; + return 0x0008; case Layer.spaceship: return 0x000A; case Layer.spaceshipExitRail: diff --git a/packages/pinball_components/lib/src/components/plunger.dart b/packages/pinball_components/lib/src/components/plunger.dart index 7e0ba5ba..19a29300 100644 --- a/packages/pinball_components/lib/src/components/plunger.dart +++ b/packages/pinball_components/lib/src/components/plunger.dart @@ -8,13 +8,15 @@ import 'package:pinball_components/pinball_components.dart'; /// /// [Plunger] ignores gravity so the player controls its downward [pull]. /// {@endtemplate} -class Plunger extends BodyComponent with InitialPosition { +class Plunger extends BodyComponent with InitialPosition, Layered { /// {@macro plunger} Plunger({ required this.compressionDistance, // TODO(ruimiguel): set to priority +1 over LaunchRamp once all priorities // are fixed. - }) : super(priority: 0); + }) : super(priority: 0) { + layer = Layer.launcher; + } /// Distance the plunger can lower. final double compressionDistance; From 034c3719bf7dc39c0b6d6fff28b789ac4187cc5f Mon Sep 17 00:00:00 2001 From: arturplaczek <33895544+arturplaczek@users.noreply.github.com> Date: Wed, 13 Apr 2022 18:02:41 +0200 Subject: [PATCH 2/2] chore: StartGameBloc for control flow before launch the game (#184) --- lib/start_game/bloc/start_game_bloc.dart | 58 +++++++++++++++++ lib/start_game/bloc/start_game_event.dart | 42 +++++++++++++ lib/start_game/bloc/start_game_state.dart | 44 +++++++++++++ lib/start_game/start_game.dart | 1 + .../start_game/bloc/start_game_bloc_test.dart | 62 +++++++++++++++++++ .../bloc/start_game_event_test.dart | 29 +++++++++ .../bloc/start_game_state_test.dart | 43 +++++++++++++ 7 files changed, 279 insertions(+) create mode 100644 lib/start_game/bloc/start_game_bloc.dart create mode 100644 lib/start_game/bloc/start_game_event.dart create mode 100644 lib/start_game/bloc/start_game_state.dart create mode 100644 lib/start_game/start_game.dart create mode 100644 test/start_game/bloc/start_game_bloc_test.dart create mode 100644 test/start_game/bloc/start_game_event_test.dart create mode 100644 test/start_game/bloc/start_game_state_test.dart diff --git a/lib/start_game/bloc/start_game_bloc.dart b/lib/start_game/bloc/start_game_bloc.dart new file mode 100644 index 00000000..ba44d88c --- /dev/null +++ b/lib/start_game/bloc/start_game_bloc.dart @@ -0,0 +1,58 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:pinball/game/game.dart'; + +part 'start_game_event.dart'; +part 'start_game_state.dart'; + +/// {@template start_game_bloc} +/// Bloc that manages the app flow before the game starts. +/// {@endtemplate} +class StartGameBloc extends Bloc { + /// {@macro start_game_bloc} + StartGameBloc({ + required PinballGame game, + }) : _game = game, + super(const StartGameState.initial()) { + on(_onPlayTapped); + on(_onCharacterSelected); + on(_onHowToPlayFinished); + } + + final PinballGame _game; + + void _onPlayTapped( + PlayTapped event, + Emitter emit, + ) { + _game.gameFlowController.start(); + + emit( + state.copyWith( + status: StartGameStatus.selectCharacter, + ), + ); + } + + void _onCharacterSelected( + CharacterSelected event, + Emitter emit, + ) { + emit( + state.copyWith( + status: StartGameStatus.howToPlay, + ), + ); + } + + void _onHowToPlayFinished( + HowToPlayFinished event, + Emitter emit, + ) { + emit( + state.copyWith( + status: StartGameStatus.play, + ), + ); + } +} diff --git a/lib/start_game/bloc/start_game_event.dart b/lib/start_game/bloc/start_game_event.dart new file mode 100644 index 00000000..ce164e97 --- /dev/null +++ b/lib/start_game/bloc/start_game_event.dart @@ -0,0 +1,42 @@ +part of 'start_game_bloc.dart'; + +/// {@template start_game_event} +/// Event added during the start game flow. +/// {@endtemplate} +abstract class StartGameEvent extends Equatable { + /// {@macro start_game_event} + const StartGameEvent(); +} + +/// {@template play_tapped} +/// Play tapped event. +/// {@endtemplate} +class PlayTapped extends StartGameEvent { + /// {@macro play_tapped} + const PlayTapped(); + + @override + List get props => []; +} + +/// {@template character_selected} +/// Character selected event. +/// {@endtemplate} +class CharacterSelected extends StartGameEvent { + /// {@macro character_selected} + const CharacterSelected(); + + @override + List get props => []; +} + +/// {@template how_to_play_finished} +/// How to play finished event. +/// {@endtemplate} +class HowToPlayFinished extends StartGameEvent { + /// {@macro how_to_play_finished} + const HowToPlayFinished(); + + @override + List get props => []; +} diff --git a/lib/start_game/bloc/start_game_state.dart b/lib/start_game/bloc/start_game_state.dart new file mode 100644 index 00000000..ad7c7cbe --- /dev/null +++ b/lib/start_game/bloc/start_game_state.dart @@ -0,0 +1,44 @@ +part of 'start_game_bloc.dart'; + +/// Defines status of start game flow. +enum StartGameStatus { + /// Initial status. + initial, + + /// Selection characters status. + selectCharacter, + + /// How to play status. + howToPlay, + + /// Play status. + play, +} + +/// {@template start_game_state} +/// Represents the state of flow before the game starts. +/// {@endtemplate} +class StartGameState extends Equatable { + /// {@macro start_game_state} + const StartGameState({ + required this.status, + }); + + /// Initial [StartGameState]. + const StartGameState.initial() : this(status: StartGameStatus.initial); + + /// Status of [StartGameState]. + final StartGameStatus status; + + /// Creates a copy of [StartGameState]. + StartGameState copyWith({ + StartGameStatus? status, + }) { + return StartGameState( + status: status ?? this.status, + ); + } + + @override + List get props => [status]; +} diff --git a/lib/start_game/start_game.dart b/lib/start_game/start_game.dart new file mode 100644 index 00000000..7171c66d --- /dev/null +++ b/lib/start_game/start_game.dart @@ -0,0 +1 @@ +export 'bloc/start_game_bloc.dart'; diff --git a/test/start_game/bloc/start_game_bloc_test.dart b/test/start_game/bloc/start_game_bloc_test.dart new file mode 100644 index 00000000..ec1b3ced --- /dev/null +++ b/test/start_game/bloc/start_game_bloc_test.dart @@ -0,0 +1,62 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball/start_game/bloc/start_game_bloc.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + late PinballGame pinballGame; + + setUp(() { + pinballGame = MockPinballGame(); + + when( + () => pinballGame.gameFlowController, + ).thenReturn( + MockGameFlowController(), + ); + }); + + group('StartGameBloc', () { + blocTest( + 'on PlayTapped changes status to selectCharacter', + build: () => StartGameBloc( + game: pinballGame, + ), + act: (bloc) => bloc.add(const PlayTapped()), + expect: () => [ + const StartGameState( + status: StartGameStatus.selectCharacter, + ) + ], + ); + + blocTest( + 'on CharacterSelected changes status to howToPlay', + build: () => StartGameBloc( + game: pinballGame, + ), + act: (bloc) => bloc.add(const CharacterSelected()), + expect: () => [ + const StartGameState( + status: StartGameStatus.howToPlay, + ) + ], + ); + + blocTest( + 'on HowToPlayFinished changes status to play', + build: () => StartGameBloc( + game: pinballGame, + ), + act: (bloc) => bloc.add(const HowToPlayFinished()), + expect: () => [ + const StartGameState( + status: StartGameStatus.play, + ) + ], + ); + }); +} diff --git a/test/start_game/bloc/start_game_event_test.dart b/test/start_game/bloc/start_game_event_test.dart new file mode 100644 index 00000000..cf481d9f --- /dev/null +++ b/test/start_game/bloc/start_game_event_test.dart @@ -0,0 +1,29 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/start_game/bloc/start_game_bloc.dart'; + +void main() { + group('StartGameEvent', () { + test('PlayTapped supports value equality', () { + expect( + PlayTapped(), + equals(PlayTapped()), + ); + }); + + test('CharacterSelected supports value equality', () { + expect( + CharacterSelected(), + equals(CharacterSelected()), + ); + }); + + test('HowToPlayFinished supports value equality', () { + expect( + HowToPlayFinished(), + equals(HowToPlayFinished()), + ); + }); + }); +} diff --git a/test/start_game/bloc/start_game_state_test.dart b/test/start_game/bloc/start_game_state_test.dart new file mode 100644 index 00000000..7ede696d --- /dev/null +++ b/test/start_game/bloc/start_game_state_test.dart @@ -0,0 +1,43 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/start_game/bloc/start_game_bloc.dart'; + +void main() { + group('StartGameState', () { + final testState = StartGameState( + status: StartGameStatus.selectCharacter, + ); + + test('initial state has correct values', () { + final state = StartGameState( + status: StartGameStatus.initial, + ); + + expect(state, StartGameState.initial()); + }); + + test('supports value equality', () { + final secondState = StartGameState( + status: StartGameStatus.selectCharacter, + ); + + expect(testState, secondState); + }); + + test('supports copyWith', () { + final secondState = testState.copyWith(); + + expect(testState, secondState); + }); + + test('has correct props', () { + expect( + testState.props, + equals([ + StartGameStatus.selectCharacter, + ]), + ); + }); + }); +}