diff --git a/.github/workflows/pinball_flame.yaml b/.github/workflows/pinball_flame.yaml new file mode 100644 index 00000000..2263bb5a --- /dev/null +++ b/.github/workflows/pinball_flame.yaml @@ -0,0 +1,20 @@ +name: pinball_flame + +on: + push: + paths: + - "packages/pinball_flame/**" + - ".github/workflows/pinball_flame.yaml" + + pull_request: + paths: + - "packages/pinball_flame/**" + - ".github/workflows/pinball_flame.yaml" + +jobs: + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1 + with: + working_directory: packages/pinball_flame + coverage_excludes: "lib/gen/*.dart" + test_optimization: false diff --git a/lib/flame/flame.dart b/lib/flame/flame.dart deleted file mode 100644 index 9264c0f4..00000000 --- a/lib/flame/flame.dart +++ /dev/null @@ -1 +0,0 @@ -export 'component_controller.dart'; diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index ce1a78b4..4ba63092 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -11,14 +11,10 @@ class GameBloc extends Bloc { GameBloc() : super(const GameState.initial()) { on(_onBallLost); on(_onScored); - on(_onBonusLetterActivated); - on(_onDashNestActivated); + on(_onBonusActivated); on(_onSparkyTurboChargeActivated); } - static const bonusWord = 'GOOGLE'; - static const bonusWordScore = 10000; - void _onBallLost(BallLost event, Emitter emit) { emit(state.copyWith(balls: state.balls - 1)); } @@ -29,54 +25,12 @@ class GameBloc extends Bloc { } } - void _onBonusLetterActivated(BonusLetterActivated event, Emitter emit) { - final newBonusLetters = [ - ...state.activatedBonusLetters, - event.letterIndex, - ]; - - final achievedBonus = newBonusLetters.length == bonusWord.length; - if (achievedBonus) { - emit( - state.copyWith( - activatedBonusLetters: [], - bonusHistory: [ - ...state.bonusHistory, - GameBonus.word, - ], - ), - ); - add(const Scored(points: bonusWordScore)); - } else { - emit( - state.copyWith(activatedBonusLetters: newBonusLetters), - ); - } - } - - void _onDashNestActivated(DashNestActivated event, Emitter emit) { - final newNests = { - ...state.activatedDashNests, - event.nestId, - }; - - final achievedBonus = newNests.length == 3; - if (achievedBonus) { - emit( - state.copyWith( - balls: state.balls + 1, - activatedDashNests: {}, - bonusHistory: [ - ...state.bonusHistory, - GameBonus.dashNest, - ], - ), - ); - } else { - emit( - state.copyWith(activatedDashNests: newNests), - ); - } + void _onBonusActivated(BonusActivated event, Emitter emit) { + emit( + state.copyWith( + bonusHistory: [...state.bonusHistory, event.bonus], + ), + ); } Future _onSparkyTurboChargeActivated( diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index ee5315ad..bbb89028 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -33,26 +33,13 @@ class Scored extends GameEvent { List get props => [points]; } -class BonusLetterActivated extends GameEvent { - const BonusLetterActivated(this.letterIndex) - : assert( - letterIndex < GameBloc.bonusWord.length, - 'Index must be smaller than the length of the word', - ); +class BonusActivated extends GameEvent { + const BonusActivated(this.bonus); - final int letterIndex; + final GameBonus bonus; @override - List get props => [letterIndex]; -} - -class DashNestActivated extends GameEvent { - const DashNestActivated(this.nestId); - - final String nestId; - - @override - List get props => [nestId]; + List get props => [bonus]; } class SparkyTurboChargeActivated extends GameEvent { diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 0d9485e9..c57eedb4 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -4,9 +4,8 @@ part of 'game_bloc.dart'; /// Defines bonuses that a player can gain during a PinballGame. enum GameBonus { - /// Bonus achieved when the user activate all of the bonus - /// letters on the board, forming the bonus word. - word, + /// Bonus achieved when the ball activates all Google letters. + googleWord, /// Bonus achieved when the user activates all dash nest bumpers. dashNest, @@ -23,17 +22,13 @@ class GameState extends Equatable { const GameState({ required this.score, required this.balls, - required this.activatedBonusLetters, required this.bonusHistory, - required this.activatedDashNests, }) : assert(score >= 0, "Score can't be negative"), assert(balls >= 0, "Number of balls can't be negative"); const GameState.initial() : score = 0, balls = 3, - activatedBonusLetters = const [], - activatedDashNests = const {}, bonusHistory = const []; /// The current score of the game. @@ -44,12 +39,6 @@ class GameState extends Equatable { /// When the number of balls is 0, the game is over. final int balls; - /// Active bonus letters. - final List activatedBonusLetters; - - /// Active dash nests. - final Set activatedDashNests; - /// Holds the history of all the [GameBonus]es earned by the player during a /// PinballGame. final List bonusHistory; @@ -57,15 +46,9 @@ class GameState extends Equatable { /// Determines when the game is over. bool get isGameOver => balls == 0; - /// Shortcut method to check if the given [i] - /// is activated. - bool isLetterActivated(int i) => activatedBonusLetters.contains(i); - GameState copyWith({ int? score, int? balls, - List? activatedBonusLetters, - Set? activatedDashNests, List? bonusHistory, }) { assert( @@ -76,9 +59,6 @@ class GameState extends Equatable { return GameState( score: score ?? this.score, balls: balls ?? this.balls, - activatedBonusLetters: - activatedBonusLetters ?? this.activatedBonusLetters, - activatedDashNests: activatedDashNests ?? this.activatedDashNests, bonusHistory: bonusHistory ?? this.bonusHistory, ); } @@ -87,8 +67,6 @@ class GameState extends Equatable { List get props => [ score, balls, - activatedBonusLetters, - activatedDashNests, bonusHistory, ]; } diff --git a/lib/game/components/alien_zone.dart b/lib/game/components/alien_zone.dart index 1f9befba..30d7bbd5 100644 --- a/lib/game/components/alien_zone.dart +++ b/lib/game/components/alien_zone.dart @@ -3,9 +3,9 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template alien_zone} /// Area positioned below [Spaceship] where the [Ball] diff --git a/lib/game/components/bonus_word.dart b/lib/game/components/bonus_word.dart deleted file mode 100644 index f3b9743c..00000000 --- a/lib/game/components/bonus_word.dart +++ /dev/null @@ -1,208 +0,0 @@ -// ignore_for_file: avoid_renaming_method_parameters - -import 'dart:async'; - -import 'package:flame/components.dart'; -import 'package:flame/effects.dart'; -import 'package:flame_bloc/flame_bloc.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'; - -/// {@template bonus_word} -/// Loads all [BonusLetter]s to compose a [BonusWord]. -/// {@endtemplate} -class BonusWord extends Component - with BlocComponent, HasGameRef { - /// {@macro bonus_word} - BonusWord({required Vector2 position}) : _position = position; - - final Vector2 _position; - - @override - bool listenWhen(GameState? previousState, GameState newState) { - return (previousState?.bonusHistory.length ?? 0) < - newState.bonusHistory.length && - newState.bonusHistory.last == GameBonus.word; - } - - @override - void onNewState(GameState state) { - if (state.bonusHistory.last == GameBonus.word) { - gameRef.audio.googleBonus(); - - final letters = children.whereType().toList(); - - for (var i = 0; i < letters.length; i++) { - final letter = letters[i]; - letter - ..isEnabled = false - ..add( - SequenceEffect( - [ - ColorEffect( - i.isOdd - ? BonusLetter._activeColor - : BonusLetter._disableColor, - const Offset(0, 1), - EffectController(duration: 0.25), - ), - ColorEffect( - i.isOdd - ? BonusLetter._disableColor - : BonusLetter._activeColor, - const Offset(0, 1), - EffectController(duration: 0.25), - ), - ], - repeatCount: 4, - )..onFinishCallback = () { - letter - ..isEnabled = true - ..add( - ColorEffect( - BonusLetter._disableColor, - const Offset(0, 1), - EffectController(duration: 0.25), - ), - ); - }, - ); - } - } - } - - @override - Future onLoad() async { - await super.onLoad(); - - final offsets = [ - Vector2(-12.92, 1.82), - Vector2(-8.33, -0.65), - Vector2(-2.88, -1.75), - ]; - offsets.addAll( - offsets.reversed - .map( - (offset) => Vector2(-offset.x, offset.y), - ) - .toList(), - ); - assert(offsets.length == GameBloc.bonusWord.length, 'Invalid positions'); - - final letters = []; - for (var i = 0; i < GameBloc.bonusWord.length; i++) { - letters.add( - BonusLetter( - letter: GameBloc.bonusWord[i], - index: i, - )..initialPosition = _position + offsets[i], - ); - } - - await addAll(letters); - } -} - -/// {@template bonus_letter} -/// [BodyType.static] sensor component, part of a word bonus, -/// which will activate its letter after contact with a [Ball]. -/// {@endtemplate} -class BonusLetter extends BodyComponent - with BlocComponent, InitialPosition { - /// {@macro bonus_letter} - BonusLetter({ - required String letter, - required int index, - }) : _letter = letter, - _index = index { - paint = Paint()..color = _disableColor; - } - - /// The size of the [BonusLetter]. - static final size = Vector2.all(3.7); - - static const _activeColor = Colors.green; - static const _disableColor = Colors.red; - - final String _letter; - final int _index; - - /// Indicates if a [BonusLetter] can be activated on [Ball] contact. - /// - /// It is disabled whilst animating and enabled again once the animation - /// completes. The animation is triggered when [GameBonus.word] is - /// awarded. - bool isEnabled = true; - - @override - Future onLoad() async { - await super.onLoad(); - - await add( - TextComponent( - position: Vector2(-1, -1), - text: _letter, - textRenderer: TextPaint( - style: const TextStyle(fontSize: 2, color: Colors.white), - ), - ), - ); - } - - @override - Body createBody() { - final shape = CircleShape()..radius = size.x / 2; - - final fixtureDef = FixtureDef(shape)..isSensor = true; - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this - ..type = BodyType.static; - - return world.createBody(bodyDef)..createFixture(fixtureDef); - } - - @override - bool listenWhen(GameState? previousState, GameState newState) { - final wasActive = previousState?.isLetterActivated(_index) ?? false; - final isActive = newState.isLetterActivated(_index); - - return wasActive != isActive; - } - - @override - void onNewState(GameState state) { - final isActive = state.isLetterActivated(_index); - - add( - ColorEffect( - isActive ? _activeColor : _disableColor, - const Offset(0, 1), - EffectController(duration: 0.25), - ), - ); - } - - /// Activates this [BonusLetter], if it's not already activated. - void activate() { - final isActive = state?.isLetterActivated(_index) ?? false; - if (!isActive) { - gameRef.read().add(BonusLetterActivated(_index)); - } - } -} - -/// Triggers [BonusLetter.activate] method when a [BonusLetter] and a [Ball] -/// come in contact. -class BonusLetterBallContactCallback - extends ContactCallback { - @override - void begin(Ball ball, BonusLetter bonusLetter, Contact contact) { - if (bonusLetter.isEnabled) { - bonusLetter.activate(); - } - } -} diff --git a/lib/game/components/camera_controller.dart b/lib/game/components/camera_controller.dart index aa963e9a..a411942e 100644 --- a/lib/game/components/camera_controller.dart +++ b/lib/game/components/camera_controller.dart @@ -1,7 +1,7 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// Adds helpers methods to Flame's [Camera] extension CameraX on Camera { diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 058bfe20..e05f9f00 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,6 +1,5 @@ export 'alien_zone.dart'; export 'board.dart'; -export 'bonus_word.dart'; export 'camera_controller.dart'; export 'controlled_ball.dart'; export 'controlled_flipper.dart'; @@ -8,6 +7,7 @@ export 'controlled_plunger.dart'; export 'controlled_sparky_computer.dart'; export 'flutter_forest.dart'; export 'game_flow_controller.dart'; +export 'google_word.dart'; export 'launcher.dart'; export 'score_effect_controller.dart'; export 'score_points.dart'; diff --git a/lib/game/components/controlled_ball.dart b/lib/game/components/controlled_ball.dart index 67d75daf..659dd994 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -1,9 +1,9 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/forge2d_game.dart'; import 'package:flutter/material.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart'; /// {@template controlled_ball} diff --git a/lib/game/components/controlled_flipper.dart b/lib/game/components/controlled_flipper.dart index f1991fe1..3c82e719 100644 --- a/lib/game/components/controlled_flipper.dart +++ b/lib/game/components/controlled_flipper.dart @@ -1,9 +1,9 @@ import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/services.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template controlled_flipper} /// A [Flipper] with a [FlipperController] attached. diff --git a/lib/game/components/controlled_plunger.dart b/lib/game/components/controlled_plunger.dart index cec71876..d6c622f7 100644 --- a/lib/game/components/controlled_plunger.dart +++ b/lib/game/components/controlled_plunger.dart @@ -1,9 +1,9 @@ import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/services.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template controlled_plunger} /// A [Plunger] with a [PlungerController] attached. diff --git a/lib/game/components/controlled_sparky_computer.dart b/lib/game/components/controlled_sparky_computer.dart index 10168e50..6a8e9e59 100644 --- a/lib/game/components/controlled_sparky_computer.dart +++ b/lib/game/components/controlled_sparky_computer.dart @@ -3,9 +3,9 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template controlled_sparky_computer} /// [SparkyComputer] with a [SparkyComputerController] attached. diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart index 88099ee4..3c5f5a1f 100644 --- a/lib/game/components/flutter_forest.dart +++ b/lib/game/components/flutter_forest.dart @@ -1,12 +1,10 @@ // ignore_for_file: avoid_renaming_method_parameters import 'package:flame/components.dart'; -import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template flutter_forest} /// Area positioned at the top right of the [Board] where the [Ball] @@ -15,9 +13,8 @@ import 'package:pinball_components/pinball_components.dart'; /// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest] /// is awarded, and the [BigDashNestBumper] releases a new [Ball]. /// {@endtemplate} -// TODO(alestiago): Make a [Blueprint] once [Blueprint] inherits from -// [Component]. -class FlutterForest extends Component with Controls<_FlutterForestController> { +class FlutterForest extends Component + with Controls<_FlutterForestController>, HasGameRef { /// {@macro flutter_forest} FlutterForest() { controller = _FlutterForestController(this); @@ -26,17 +23,16 @@ class FlutterForest extends Component with Controls<_FlutterForestController> { @override Future onLoad() async { await super.onLoad(); + gameRef.addContactCallback(_DashNestBumperBallContactCallback()); + final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, -58.3); - final bigNest = _ControlledBigDashNestBumper( - id: 'big_nest_bumper', - )..initialPosition = Vector2(18.55, -59.35); - final smallLeftNest = _ControlledSmallDashNestBumper.a( - id: 'small_nest_bumper_a', - )..initialPosition = Vector2(8.95, -51.95); - final smallRightNest = _ControlledSmallDashNestBumper.b( - id: 'small_nest_bumper_b', - )..initialPosition = Vector2(23.3, -46.75); + final bigNest = _BigDashNestBumper() + ..initialPosition = Vector2(18.55, -59.35); + final smallLeftNest = _SmallDashNestBumper.a() + ..initialPosition = Vector2(8.95, -51.95); + final smallRightNest = _SmallDashNestBumper.b() + ..initialPosition = Vector2(23.3, -46.75); final dashAnimatronic = DashAnimatronic()..position = Vector2(20, -66); await addAll([ @@ -50,31 +46,31 @@ class FlutterForest extends Component with Controls<_FlutterForestController> { } class _FlutterForestController extends ComponentController - with BlocComponent, HasGameRef { + with HasGameRef { _FlutterForestController(FlutterForest flutterForest) : super(flutterForest); - @override - Future onLoad() async { - await super.onLoad(); - gameRef.addContactCallback(_ControlledDashNestBumperBallContactCallback()); - } + final _activatedBumpers = {}; - @override - bool listenWhen(GameState? previousState, GameState newState) { - return (previousState?.bonusHistory.length ?? 0) < - newState.bonusHistory.length && - newState.bonusHistory.last == GameBonus.dashNest; - } + void activateBumper(DashNestBumper dashNestBumper) { + if (!_activatedBumpers.add(dashNestBumper)) return; - @override - void onNewState(GameState state) { - super.onNewState(state); + dashNestBumper.activate(); - component.firstChild()?.playing = true; - _addBonusBall(); + final activatedBonus = _activatedBumpers.length == 3; + if (activatedBonus) { + _addBonusBall(); + + gameRef.read().add(const BonusActivated(GameBonus.dashNest)); + _activatedBumpers + ..forEach((bumper) => bumper.deactivate()) + ..clear(); + + component.firstChild()?.playing = true; + } } Future _addBonusBall() async { + // TODO(alestiago): Remove hardcoded duration. await Future.delayed(const Duration(milliseconds: 700)); await gameRef.add( ControlledBall.bonus(theme: gameRef.theme) @@ -83,83 +79,29 @@ class _FlutterForestController extends ComponentController } } -class _ControlledBigDashNestBumper extends BigDashNestBumper - with Controls, ScorePoints { - _ControlledBigDashNestBumper({required String id}) : super() { - controller = DashNestBumperController(this, id: id); - } - +// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D +// ContactCallback process is enhanced. +class _BigDashNestBumper extends BigDashNestBumper with ScorePoints { @override int get points => 20; } -class _ControlledSmallDashNestBumper extends SmallDashNestBumper - with Controls, ScorePoints { - _ControlledSmallDashNestBumper.a({required String id}) : super.a() { - controller = DashNestBumperController(this, id: id); - } +class _SmallDashNestBumper extends SmallDashNestBumper with ScorePoints { + _SmallDashNestBumper.a() : super.a(); - _ControlledSmallDashNestBumper.b({required String id}) : super.b() { - controller = DashNestBumperController(this, id: id); - } + _SmallDashNestBumper.b() : super.b(); @override - int get points => 10; + int get points => 20; } -/// {@template dash_nest_bumper_controller} -/// Controls a [DashNestBumper]. -/// {@endtemplate} -@visibleForTesting -class DashNestBumperController extends ComponentController - with BlocComponent, HasGameRef { - /// {@macro dash_nest_bumper_controller} - DashNestBumperController( - DashNestBumper dashNestBumper, { - required this.id, - }) : super(dashNestBumper); - - /// Unique identifier for the controlled [DashNestBumper]. - /// - /// Used to identify [DashNestBumper]s in [GameState.activatedDashNests]. - final String id; - - @override - bool listenWhen(GameState? previousState, GameState newState) { - final wasActive = previousState?.activatedDashNests.contains(id) ?? false; - final isActive = newState.activatedDashNests.contains(id); - - return wasActive != isActive; - } - +class _DashNestBumperBallContactCallback + extends ContactCallback { @override - void onNewState(GameState state) { - super.onNewState(state); - - if (state.activatedDashNests.contains(id)) { - component.activate(); - } else { - component.deactivate(); + void begin(DashNestBumper dashNestBumper, _, __) { + final parent = dashNestBumper.parent; + if (parent is FlutterForest) { + parent.controller.activateBumper(dashNestBumper); } } - - /// Registers when a [DashNestBumper] is hit by a [Ball]. - /// - /// Triggered by [_ControlledDashNestBumperBallContactCallback]. - void hit() { - gameRef.read().add(DashNestActivated(id)); - } -} - -/// Listens when a [Ball] bounces bounces against a [DashNestBumper]. -class _ControlledDashNestBumperBallContactCallback - extends ContactCallback, Ball> { - @override - void begin( - Controls controlledDashNestBumper, - Ball _, - Contact __, - ) { - controlledDashNestBumper.controller.hit(); - } } diff --git a/lib/game/components/game_flow_controller.dart b/lib/game/components/game_flow_controller.dart index 01ee0c32..957689a9 100644 --- a/lib/game/components/game_flow_controller.dart +++ b/lib/game/components/game_flow_controller.dart @@ -1,8 +1,8 @@ import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template game_flow_controller} /// A [Component] that controls the game over and game restart logic diff --git a/lib/game/components/google_word.dart b/lib/game/components/google_word.dart new file mode 100644 index 00000000..34609c64 --- /dev/null +++ b/lib/game/components/google_word.dart @@ -0,0 +1,83 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +/// {@template google_word} +/// Loads all [GoogleLetter]s to compose a [GoogleWord]. +/// {@endtemplate} +class GoogleWord extends Component + with HasGameRef, Controls<_GoogleWordController> { + /// {@macro google_word} + GoogleWord({ + required Vector2 position, + }) : _position = position { + controller = _GoogleWordController(this); + } + + final Vector2 _position; + + @override + Future onLoad() async { + await super.onLoad(); + gameRef.addContactCallback(_GoogleLetterBallContactCallback()); + + final offsets = [ + Vector2(-12.92, 1.82), + Vector2(-8.33, -0.65), + Vector2(-2.88, -1.75), + Vector2(2.88, -1.75), + Vector2(8.33, -0.65), + Vector2(12.92, 1.82), + ]; + + final letters = []; + for (var index = 0; index < offsets.length; index++) { + letters.add( + GoogleLetter(index)..initialPosition = _position + offsets[index], + ); + } + + await addAll(letters); + } +} + +class _GoogleWordController extends ComponentController + with HasGameRef { + _GoogleWordController(GoogleWord googleWord) : super(googleWord); + + final _activatedLetters = {}; + + void activate(GoogleLetter googleLetter) { + if (!_activatedLetters.add(googleLetter)) return; + + googleLetter.activate(); + + final activatedBonus = _activatedLetters.length == 6; + if (activatedBonus) { + gameRef.audio.googleBonus(); + gameRef.read().add(const BonusActivated(GameBonus.googleWord)); + component.children.whereType().forEach( + (letter) => letter.deactivate(), + ); + _activatedLetters.clear(); + } + } +} + +/// Activates a [GoogleLetter] when it contacts with a [Ball]. +class _GoogleLetterBallContactCallback + extends ContactCallback { + @override + void begin(GoogleLetter googleLetter, _, __) { + final parent = googleLetter.parent; + if (parent is GoogleWord) { + parent.controller.activate(googleLetter); + } + } +} diff --git a/lib/game/components/launcher.dart b/lib/game/components/launcher.dart index 79211d80..f3238152 100644 --- a/lib/game/components/launcher.dart +++ b/lib/game/components/launcher.dart @@ -1,6 +1,7 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/components/components.dart'; import 'package:pinball_components/pinball_components.dart' hide Assets; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template launcher} /// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and diff --git a/lib/game/components/score_effect_controller.dart b/lib/game/components/score_effect_controller.dart index f9fe9349..f4a185e1 100644 --- a/lib/game/components/score_effect_controller.dart +++ b/lib/game/components/score_effect_controller.dart @@ -2,9 +2,9 @@ import 'dart:math'; import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template score_effect_controller} /// A [ComponentController] responsible for adding [ScoreText]s diff --git a/lib/game/components/score_points.dart b/lib/game/components/score_points.dart index ce13c718..f0d6ec3a 100644 --- a/lib/game/components/score_points.dart +++ b/lib/game/components/score_points.dart @@ -34,10 +34,7 @@ class BallScorePointsCallback extends ContactCallback { ScorePoints scorePoints, Contact __, ) { - _gameRef.read().add( - Scored(points: scorePoints.points), - ); - + _gameRef.read().add(Scored(points: scorePoints.points)); _gameRef.audio.score(); } } diff --git a/lib/game/components/sparky_fire_zone.dart b/lib/game/components/sparky_fire_zone.dart index bf8ba883..0a5abe88 100644 --- a/lib/game/components/sparky_fire_zone.dart +++ b/lib/game/components/sparky_fire_zone.dart @@ -3,9 +3,9 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template sparky_fire_zone} /// Area positioned at the top left of the [Board] where the [Ball] diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 4cc8bee5..e7c7a343 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -58,6 +58,12 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.sparky.bumper.c.inactive.keyName), images.load(components.Assets.images.backboard.backboardScores.keyName), images.load(components.Assets.images.backboard.backboardGameOver.keyName), + images.load(components.Assets.images.googleWord.letter1.keyName), + images.load(components.Assets.images.googleWord.letter2.keyName), + images.load(components.Assets.images.googleWord.letter3.keyName), + images.load(components.Assets.images.googleWord.letter4.keyName), + images.load(components.Assets.images.googleWord.letter5.keyName), + images.load(components.Assets.images.googleWord.letter6.keyName), images.load(components.Assets.images.backboard.display.keyName), images.load(Assets.images.components.background.path), ]; diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 07d637c0..b1ff2c6c 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -5,11 +5,11 @@ import 'package:flame/components.dart'; import 'package:flame/input.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball/flame/flame.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/gen/assets.gen.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart' hide Assets; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' hide Assets; class PinballGame extends Forge2DGame @@ -72,7 +72,6 @@ class PinballGame extends Forge2DGame void _addContactCallbacks() { addContactCallback(BallScorePointsCallback(this)); addContactCallback(BottomWallBallContactCallback()); - addContactCallback(BonusLetterBallContactCallback()); } Future _addGameBoundaries() async { @@ -82,7 +81,7 @@ class PinballGame extends Forge2DGame Future _addBonusWord() async { await add( - BonusWord( + GoogleWord( position: Vector2( BoardDimensions.bounds.center.dx - 4.1, BoardDimensions.bounds.center.dy + 1.8, diff --git a/packages/pinball_components/lib/src/components/alien_bumper.dart b/packages/pinball_components/lib/src/components/alien_bumper.dart index 9b102d2f..9165cc15 100644 --- a/packages/pinball_components/lib/src/components/alien_bumper.dart +++ b/packages/pinball_components/lib/src/components/alien_bumper.dart @@ -72,13 +72,14 @@ class AlienBumper extends BodyComponent with InitialPosition { majorRadius: _majorRadius, minorRadius: _minorRadius, )..rotate(1.29); - final fixtureDef = FixtureDef(shape) - ..friction = 0 - ..restitution = 4; - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; + final fixtureDef = FixtureDef( + shape, + restitution: 4, + ); + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart b/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart index 05f89217..98ae6ae0 100644 --- a/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart +++ b/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:flame/components.dart'; import 'package:flutter/services.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// Signature for the callback called when the used has /// submettied their initials on the [BackboardGameOver] diff --git a/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart b/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart index 8f404d53..61d2074d 100644 --- a/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart +++ b/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart @@ -5,6 +5,7 @@ import 'package:flame/components.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template backboard_letter_prompt} /// A [PositionComponent] that renders a letter prompt used diff --git a/packages/pinball_components/lib/src/components/ball.dart b/packages/pinball_components/lib/src/components/ball.dart index 726e474f..36059cfd 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball.dart @@ -14,13 +14,18 @@ class Ball extends BodyComponent /// {@macro ball} Ball({ required this.baseColor, - }) { + }) : super( + children: [ + _BallSpriteComponent()..tint(baseColor.withOpacity(0.5)), + ], + ) { // TODO(ruimiguel): while developing Ball can be launched by clicking mouse, // and default layer is Layer.all. But on final game Ball will be always be // be launched from Plunger and LauncherRamp will modify it to Layer.board. // We need to see what happens if Ball appears from other place like nest // bumper, it will need to explicit change layer to Layer.board then. layer = Layer.board; + renderBody = false; } /// Render priority for the [Ball] while it's on the board. @@ -47,28 +52,18 @@ class Ball extends BodyComponent double _boostTimer = 0; static const _boostDuration = 2.0; - final _BallSpriteComponent _spriteComponent = _BallSpriteComponent(); - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - - await add( - _spriteComponent..tint(baseColor.withOpacity(0.5)), - ); - - renderBody = false; - } - @override Body createBody() { final shape = CircleShape()..radius = size.x / 2; - final fixtureDef = FixtureDef(shape)..density = 1; - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this - ..type = BodyType.dynamic; + final fixtureDef = FixtureDef( + shape, + density: 1, + ); + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + type: BodyType.dynamic, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } @@ -128,7 +123,10 @@ class Ball extends BodyComponent ((standardizedYPosition / boardHeight) * (1 - maxShrinkValue)); body.fixtures.first.shape.radius = (size.x / 2) * scaleFactor; - _spriteComponent.scale = Vector2.all(scaleFactor); + + // TODO(alestiago): Revisit and see if there's a better way to do this. + final spriteComponent = firstChild<_BallSpriteComponent>(); + spriteComponent?.scale = Vector2.all(scaleFactor); } void _setPositionalGravity() { diff --git a/packages/pinball_components/lib/src/components/baseboard.dart b/packages/pinball_components/lib/src/components/baseboard.dart index df602f65..29d7285b 100644 --- a/packages/pinball_components/lib/src/components/baseboard.dart +++ b/packages/pinball_components/lib/src/components/baseboard.dart @@ -11,7 +11,12 @@ class Baseboard extends BodyComponent with InitialPosition { /// {@macro baseboard} Baseboard({ required BoardSide side, - }) : _side = side; + }) : _side = side, + super( + children: [_BaseboardSpriteComponent(side: side)], + ) { + renderBody = false; + } /// Whether the [Baseboard] is on the left or right side of the board. final BoardSide _side; @@ -79,20 +84,13 @@ class Baseboard extends BodyComponent with InitialPosition { return fixturesDef; } - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - await add(_BaseboardSpriteComponent(side: _side)); - } - @override Body createBody() { const angle = 37.1 * (math.pi / 180); - - final bodyDef = BodyDef() - ..position = initialPosition - ..angle = _side.isLeft ? angle : -angle; + final bodyDef = BodyDef( + position: initialPosition, + angle: -angle * _side.direction, + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart index 38ce5f4c..983b4839 100644 --- a/packages/pinball_components/lib/src/components/boundaries.dart +++ b/packages/pinball_components/lib/src/components/boundaries.dart @@ -3,6 +3,7 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template boundaries} /// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary]. @@ -23,7 +24,13 @@ class Boundaries extends Forge2DBlueprint { /// {@endtemplate bottom_boundary} class _BottomBoundary extends BodyComponent with InitialPosition { /// {@macro bottom_boundary} - _BottomBoundary() : super(priority: 1); + _BottomBoundary() + : super( + priority: 1, + children: [_BottomBoundarySpriteComponent()], + ) { + renderBody = false; + } List _createFixtureDefs() { final fixturesDefs = []; @@ -59,13 +66,6 @@ class _BottomBoundary extends BodyComponent with InitialPosition { return body; } - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - await add(_BottomBoundarySpriteComponent()); - } } class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef { @@ -88,7 +88,13 @@ class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef { /// {@endtemplate outer_boundary} class _OuterBoundary extends BodyComponent with InitialPosition { /// {@macro outer_boundary} - _OuterBoundary() : super(priority: Ball.launchRampPriority - 1); + _OuterBoundary() + : super( + priority: Ball.launchRampPriority - 1, + children: [_OuterBoundarySpriteComponent()], + ) { + renderBody = false; + } List _createFixtureDefs() { final fixturesDefs = []; @@ -130,13 +136,6 @@ class _OuterBoundary extends BodyComponent with InitialPosition { return body; } - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - await add(_OuterBoundarySpriteComponent()); - } } class _OuterBoundarySpriteComponent extends SpriteComponent with HasGameRef { diff --git a/packages/pinball_components/lib/src/components/chrome_dino.dart b/packages/pinball_components/lib/src/components/chrome_dino.dart index 06b302c7..e450b661 100644 --- a/packages/pinball_components/lib/src/components/chrome_dino.dart +++ b/packages/pinball_components/lib/src/components/chrome_dino.dart @@ -54,12 +54,14 @@ class ChromeDino extends BodyComponent with InitialPosition { // TODO(alestiago): Subject to change when sprites are added. final box = PolygonShape()..setAsBoxXY(size.x / 2, size.y / 2); - final fixtureDef = FixtureDef(box) - ..shape = box - ..density = 999 - ..friction = 0.3 - ..restitution = 0.1 - ..isSensor = true; + final fixtureDef = FixtureDef( + box, + density: 999, + friction: 0.3, + restitution: 0.1, + isSensor: true, + ); + fixtureDefs.add(fixtureDef); // FIXME(alestiago): Investigate why adding these fixtures is considered as @@ -93,10 +95,11 @@ class ChromeDino extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef() - ..gravityScale = Vector2.zero() - ..position = initialPosition - ..type = BodyType.dynamic; + final bodyDef = BodyDef( + position: initialPosition, + type: BodyType.dynamic, + gravityScale: Vector2.zero(), + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -111,10 +114,7 @@ class ChromeDino extends BodyComponent with InitialPosition { class _ChromeDinoAnchor extends JointAnchor { /// {@macro flipper_anchor} _ChromeDinoAnchor() { - initialPosition = Vector2( - ChromeDino.size.x / 2, - 0, - ); + initialPosition = Vector2(ChromeDino.size.x / 2, 0); } } diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 7b4e1ddd..ffada30b 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -19,8 +19,8 @@ export 'joint_anchor.dart'; export 'kicker.dart'; export 'launch_ramp.dart'; export 'layer.dart'; +export 'layer_sensor.dart'; export 'plunger.dart'; -export 'ramp_opening.dart'; export 'rocket.dart'; export 'score_text.dart'; export 'shapes/shapes.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 9cd242d0..a16fa376 100644 --- a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart @@ -79,10 +79,10 @@ class BigDashNestBumper extends DashNestBumper { minorRadius: 3.75, )..rotate(math.pi / 1.9); final fixtureDef = FixtureDef(shape); - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } @@ -130,13 +130,14 @@ class SmallDashNestBumper extends DashNestBumper { majorRadius: 3, minorRadius: 2.25, )..rotate(math.pi / 2); - final fixtureDef = FixtureDef(shape) - ..friction = 0 - ..restitution = 4; - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; + final fixtureDef = FixtureDef( + shape, + restitution: 4, + ); + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/packages/pinball_components/lib/src/components/dino_walls.dart b/packages/pinball_components/lib/src/components/dino_walls.dart index 53c67861..9bc0ba09 100644 --- a/packages/pinball_components/lib/src/components/dino_walls.dart +++ b/packages/pinball_components/lib/src/components/dino_walls.dart @@ -6,6 +6,7 @@ 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; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template dinowalls} /// A [Blueprint] which creates walls for the [ChromeDino]. @@ -28,7 +29,13 @@ class DinoWalls extends Forge2DBlueprint { /// {@endtemplate} class _DinoTopWall extends BodyComponent with InitialPosition { ///{@macro dino_top_wall} - _DinoTopWall() : super(priority: 1); + _DinoTopWall() + : super( + priority: 1, + children: [_DinoTopWallSpriteComponent()], + ) { + renderBody = false; + } List _createFixtureDefs() { final fixturesDef = []; @@ -81,10 +88,11 @@ class _DinoTopWall extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..type = BodyType.static; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); + final body = world.createBody(bodyDef); _createFixtureDefs().forEach( (fixture) => body.createFixture( @@ -96,14 +104,6 @@ class _DinoTopWall extends BodyComponent with InitialPosition { return body; } - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - - await add(_DinoTopWallSpriteComponent()); - } } class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef { @@ -124,10 +124,16 @@ class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef { /// {@endtemplate} class _DinoBottomWall extends BodyComponent with InitialPosition { ///{@macro dino_top_wall} - _DinoBottomWall(); + _DinoBottomWall() + : super( + children: [_DinoBottomWallSpriteComponent()], + ) { + renderBody = false; + } List _createFixtureDefs() { final fixturesDef = []; + const restitution = 1.0; final topStraightControlPoints = [ Vector2(32.4, -8.3), @@ -138,7 +144,10 @@ class _DinoBottomWall extends BodyComponent with InitialPosition { topStraightControlPoints.first, topStraightControlPoints.last, ); - final topStraightFixtureDef = FixtureDef(topStraightShape); + final topStraightFixtureDef = FixtureDef( + topStraightShape, + restitution: restitution, + ); fixturesDef.add(topStraightFixtureDef); final topLeftCurveControlPoints = [ @@ -149,7 +158,11 @@ class _DinoBottomWall extends BodyComponent with InitialPosition { final topLeftCurveShape = BezierCurveShape( controlPoints: topLeftCurveControlPoints, ); - fixturesDef.add(FixtureDef(topLeftCurveShape)); + final topLeftCurveFixtureDef = FixtureDef( + topLeftCurveShape, + restitution: restitution, + ); + fixturesDef.add(topLeftCurveFixtureDef); final bottomLeftStraightControlPoints = [ topLeftCurveControlPoints.last, @@ -160,7 +173,10 @@ class _DinoBottomWall extends BodyComponent with InitialPosition { bottomLeftStraightControlPoints.first, bottomLeftStraightControlPoints.last, ); - final bottomLeftStraightFixtureDef = FixtureDef(bottomLeftStraightShape); + final bottomLeftStraightFixtureDef = FixtureDef( + bottomLeftStraightShape, + restitution: restitution, + ); fixturesDef.add(bottomLeftStraightFixtureDef); final bottomStraightControlPoints = [ @@ -172,7 +188,10 @@ class _DinoBottomWall extends BodyComponent with InitialPosition { bottomStraightControlPoints.first, bottomStraightControlPoints.last, ); - final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); + final bottomStraightFixtureDef = FixtureDef( + bottomStraightShape, + restitution: restitution, + ); fixturesDef.add(bottomStraightFixtureDef); return fixturesDef; @@ -180,30 +199,16 @@ class _DinoBottomWall extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..type = BodyType.static; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); final body = world.createBody(bodyDef); - _createFixtureDefs().forEach( - (fixture) => body.createFixture( - fixture - ..restitution = 0.1 - ..friction = 0, - ), - ); + _createFixtureDefs().forEach(body.createFixture); return body; } - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - - await add(_DinoBottomWallSpriteComponent()); - } } class _DinoBottomWallSpriteComponent extends SpriteComponent with HasGameRef { diff --git a/packages/pinball_components/lib/src/components/flipper.dart b/packages/pinball_components/lib/src/components/flipper.dart index ebe468b3..1d0a3c5d 100644 --- a/packages/pinball_components/lib/src/components/flipper.dart +++ b/packages/pinball_components/lib/src/components/flipper.dart @@ -13,7 +13,11 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { /// {@macro flipper} Flipper({ required this.side, - }); + }) : super( + children: [_FlipperSpriteComponent(side: side)], + ) { + renderBody = false; + } /// The size of the [Flipper]. static final size = Vector2(13.5, 4.3); @@ -99,9 +103,11 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { Vector2(smallCircleShape.position.x, -smallCircleShape.radius), ]; final trapezium = PolygonShape()..set(trapeziumVertices); - final trapeziumFixtureDef = FixtureDef(trapezium) - ..density = 50.0 // TODO(alestiago): Use a proper density. - ..friction = .1; // TODO(alestiago): Use a proper friction. + final trapeziumFixtureDef = FixtureDef( + trapezium, + density: 50, // TODO(alestiago): Use a proper density. + friction: .1, // TODO(alestiago): Use a proper friction. + ); fixturesDef.add(trapeziumFixtureDef); return fixturesDef; @@ -110,18 +116,18 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { @override Future onLoad() async { await super.onLoad(); - renderBody = false; await _anchorToJoint(); - await add(_FlipperSpriteComponent(side: side)); } @override Body createBody() { - final bodyDef = BodyDef() - ..position = initialPosition - ..gravityScale = Vector2.zero() - ..type = BodyType.dynamic; + final bodyDef = BodyDef( + position: initialPosition, + gravityScale: Vector2.zero(), + type: BodyType.dynamic, + ); + final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); diff --git a/packages/pinball_components/lib/src/components/flutter_sign_post.dart b/packages/pinball_components/lib/src/components/flutter_sign_post.dart index 070fa316..d7999183 100644 --- a/packages/pinball_components/lib/src/components/flutter_sign_post.dart +++ b/packages/pinball_components/lib/src/components/flutter_sign_post.dart @@ -6,19 +6,21 @@ import 'package:pinball_components/pinball_components.dart'; /// A sign, found in the Flutter Forest. /// {@endtemplate} class FlutterSignPost extends BodyComponent with InitialPosition { - @override - Future onLoad() async { - await super.onLoad(); + /// {@macro flutter_sign_post} + FlutterSignPost() + : super( + children: [_FlutterSignPostSpriteComponent()], + ) { renderBody = false; - - await add(_FlutterSignPostSpriteComponent()); } @override Body createBody() { final shape = CircleShape()..radius = 0.25; final fixtureDef = FixtureDef(shape); - final bodyDef = BodyDef()..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/packages/pinball_components/lib/src/components/google_letter.dart b/packages/pinball_components/lib/src/components/google_letter.dart index 9e9e2dec..1ed31435 100644 --- a/packages/pinball_components/lib/src/components/google_letter.dart +++ b/packages/pinball_components/lib/src/components/google_letter.dart @@ -33,12 +33,14 @@ class GoogleLetter extends BodyComponent with InitialPosition { @override Body createBody() { final shape = CircleShape()..radius = 1.85; - final fixtureDef = FixtureDef(shape)..isSensor = true; - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this - ..type = BodyType.static; + final fixtureDef = FixtureDef( + shape, + isSensor: true, + ); + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/packages/pinball_components/lib/src/components/joint_anchor.dart b/packages/pinball_components/lib/src/components/joint_anchor.dart index 7ca75ba0..63875d40 100644 --- a/packages/pinball_components/lib/src/components/joint_anchor.dart +++ b/packages/pinball_components/lib/src/components/joint_anchor.dart @@ -22,7 +22,9 @@ class JointAnchor extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef()..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + ); return world.createBody(bodyDef); } } diff --git a/packages/pinball_components/lib/src/components/kicker.dart b/packages/pinball_components/lib/src/components/kicker.dart index f70de757..26e6c8f9 100644 --- a/packages/pinball_components/lib/src/components/kicker.dart +++ b/packages/pinball_components/lib/src/components/kicker.dart @@ -16,7 +16,12 @@ class Kicker extends BodyComponent with InitialPosition { /// {@macro kicker} Kicker({ required BoardSide side, - }) : _side = side; + }) : _side = side, + super( + children: [_KickerSpriteComponent(side: side)], + ) { + renderBody = false; + } /// The size of the [Kicker] body. static final Vector2 size = Vector2(4.4, 15); @@ -35,7 +40,7 @@ class Kicker extends BodyComponent with InitialPosition { final upperCircle = CircleShape()..radius = 1.6; upperCircle.position.setValues(0, upperCircle.radius / 2); - final upperCircleFixtureDef = FixtureDef(upperCircle)..friction = 0; + final upperCircleFixtureDef = FixtureDef(upperCircle); fixturesDefs.add(upperCircleFixtureDef); final lowerCircle = CircleShape()..radius = 1.6; @@ -43,7 +48,7 @@ class Kicker extends BodyComponent with InitialPosition { size.x * -direction, size.y + 0.8, ); - final lowerCircleFixtureDef = FixtureDef(lowerCircle)..friction = 0; + final lowerCircleFixtureDef = FixtureDef(lowerCircle); fixturesDefs.add(lowerCircleFixtureDef); final wallFacingEdge = EdgeShape() @@ -55,7 +60,7 @@ class Kicker extends BodyComponent with InitialPosition { ), Vector2(2.5 * direction, size.y - 2), ); - final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge)..friction = 0; + final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge); fixturesDefs.add(wallFacingLineFixtureDef); final bottomEdge = EdgeShape() @@ -67,7 +72,7 @@ class Kicker extends BodyComponent with InitialPosition { lowerCircle.radius * math.sin(quarterPi), ), ); - final bottomLineFixtureDef = FixtureDef(bottomEdge)..friction = 0; + final bottomLineFixtureDef = FixtureDef(bottomEdge); fixturesDefs.add(bottomLineFixtureDef); final bouncyEdge = EdgeShape() @@ -84,10 +89,11 @@ class Kicker extends BodyComponent with InitialPosition { ), ); - final bouncyFixtureDef = FixtureDef(bouncyEdge) + final bouncyFixtureDef = FixtureDef( + bouncyEdge, // TODO(alestiago): Play with restitution value once game is bundled. - ..restitution = 10.0 - ..friction = 0; + restitution: 10, + ); fixturesDefs.add(bouncyFixtureDef); // TODO(alestiago): Evaluate if there is value on centering the fixtures. @@ -111,19 +117,14 @@ class Kicker extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef()..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); return body; } - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - await add(_KickerSpriteComponent(side: _side)); - } } class _KickerSpriteComponent extends SpriteComponent with HasGameRef { diff --git a/packages/pinball_components/lib/src/components/launch_ramp.dart b/packages/pinball_components/lib/src/components/launch_ramp.dart index d135f3b9..c3d7624d 100644 --- a/packages/pinball_components/lib/src/components/launch_ramp.dart +++ b/packages/pinball_components/lib/src/components/launch_ramp.dart @@ -5,6 +5,7 @@ import 'dart:math' as math; import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template launch_ramp} /// A [Blueprint] which creates the [_LaunchRampBase] and @@ -14,7 +15,7 @@ class LaunchRamp extends Forge2DBlueprint { @override void build(_) { addAllContactCallback([ - RampOpeningBallContactCallback<_LaunchRampExit>(), + LayerSensorBallContactCallback<_LaunchRampExit>(), ]); final launchRampBase = _LaunchRampBase(); @@ -103,9 +104,10 @@ class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -234,10 +236,10 @@ class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered { } /// {@template launch_ramp_exit} -/// [RampOpening] with [Layer.launcher] to filter [Ball]s exiting the +/// [LayerSensor] with [Layer.launcher] to filter [Ball]s exiting the /// [LaunchRamp]. /// {@endtemplate} -class _LaunchRampExit extends RampOpening { +class _LaunchRampExit extends LayerSensor { /// {@macro launch_ramp_exit} _LaunchRampExit({ required double rotation, @@ -245,7 +247,7 @@ class _LaunchRampExit extends RampOpening { super( insideLayer: Layer.launcher, outsideLayer: Layer.board, - orientation: RampOrientation.down, + orientation: LayerEntranceOrientation.down, insidePriority: Ball.launchRampPriority, outsidePriority: 0, ) { diff --git a/packages/pinball_components/lib/src/components/layer_sensor.dart b/packages/pinball_components/lib/src/components/layer_sensor.dart new file mode 100644 index 00000000..05f9a98d --- /dev/null +++ b/packages/pinball_components/lib/src/components/layer_sensor.dart @@ -0,0 +1,110 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template layer_entrance_orientation} +/// Determines if a layer entrance is oriented [up] or [down] on the board. +/// {@endtemplate} +enum LayerEntranceOrientation { + /// Facing up on the Board. + up, + + /// Facing down on the Board. + down, +} + +/// {@template layer_sensor} +/// [BodyComponent] located at the entrance and exit of a [Layer]. +/// +/// [LayerSensorBallContactCallback] detects when a [Ball] passes +/// through this sensor. +/// +/// By default the base [layer] is set to [Layer.board] and the +/// [outsidePriority] is set to the lowest possible [Layer]. +/// {@endtemplate} +abstract class LayerSensor extends BodyComponent with InitialPosition, Layered { + /// {@macro layer_sensor} + LayerSensor({ + required Layer insideLayer, + Layer? outsideLayer, + required int insidePriority, + int? outsidePriority, + required this.orientation, + }) : _insideLayer = insideLayer, + _outsideLayer = outsideLayer ?? Layer.board, + _insidePriority = insidePriority, + _outsidePriority = outsidePriority ?? Ball.boardPriority { + layer = Layer.opening; + } + final Layer _insideLayer; + final Layer _outsideLayer; + final int _insidePriority; + final int _outsidePriority; + + /// Mask bits value for collisions on [Layer]. + Layer get insideLayer => _insideLayer; + + /// Mask bits value for collisions outside of [Layer]. + Layer get outsideLayer => _outsideLayer; + + /// Render priority for the [Ball] on [Layer]. + int get insidePriority => _insidePriority; + + /// Render priority for the [Ball] outside of [Layer]. + int get outsidePriority => _outsidePriority; + + /// The [Shape] of the [LayerSensor]. + Shape get shape; + + /// {@macro layer_entrance_orientation} + // TODO(ruimiguel): Try to remove the need of [LayerEntranceOrientation] for + // collision calculations. + final LayerEntranceOrientation orientation; + + @override + Body createBody() { + final fixtureDef = FixtureDef( + shape, + isSensor: true, + ); + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + +/// {@template layer_sensor_ball_contact_callback} +/// Detects when a [Ball] enters or exits a [Layer] through a [LayerSensor]. +/// +/// Modifies [Ball]'s [Layer] and render priority depending on whether the +/// [Ball] is on or outside of a [Layer]. +/// {@endtemplate} +class LayerSensorBallContactCallback + extends ContactCallback { + @override + void begin(Ball ball, LayerEntrance layerEntrance, Contact _) { + if (ball.layer != layerEntrance.insideLayer) { + final isBallEnteringOpening = + (layerEntrance.orientation == LayerEntranceOrientation.down && + ball.body.linearVelocity.y < 0) || + (layerEntrance.orientation == LayerEntranceOrientation.up && + ball.body.linearVelocity.y > 0); + + if (isBallEnteringOpening) { + ball + ..layer = layerEntrance.insideLayer + ..priority = layerEntrance.insidePriority + ..reorderChildren(); + } + } else { + ball + ..layer = layerEntrance.outsideLayer + ..priority = layerEntrance.outsidePriority + ..reorderChildren(); + } + } +} diff --git a/packages/pinball_components/lib/src/components/plunger.dart b/packages/pinball_components/lib/src/components/plunger.dart index 6b218e67..f01c007c 100644 --- a/packages/pinball_components/lib/src/components/plunger.dart +++ b/packages/pinball_components/lib/src/components/plunger.dart @@ -33,11 +33,12 @@ class Plunger extends BodyComponent with InitialPosition, Layered { final fixtureDef = FixtureDef(shape)..density = 80; - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this - ..type = BodyType.dynamic - ..gravityScale = Vector2.zero(); + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + type: BodyType.dynamic, + gravityScale: Vector2.zero(), + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/packages/pinball_components/lib/src/components/ramp_opening.dart b/packages/pinball_components/lib/src/components/ramp_opening.dart deleted file mode 100644 index 92f1ff69..00000000 --- a/packages/pinball_components/lib/src/components/ramp_opening.dart +++ /dev/null @@ -1,127 +0,0 @@ -// ignore_for_file: avoid_renaming_method_parameters - -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball_components/pinball_components.dart'; - -/// {@template ramp_orientation} -/// Determines if a ramp is facing [up] or [down] on the Board. -/// {@endtemplate} -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 the base [layer] is set to [Layer.board] and the -/// [outsidePriority] is set to the lowest possible [Layer]. -/// {@endtemplate} -// TODO(ruialonso): Consider renaming the class. -abstract class RampOpening extends BodyComponent with InitialPosition, Layered { - /// {@macro ramp_opening} - RampOpening({ - required Layer insideLayer, - Layer? outsideLayer, - required int insidePriority, - int? outsidePriority, - required this.orientation, - }) : _insideLayer = insideLayer, - _outsideLayer = outsideLayer ?? Layer.board, - _insidePriority = insidePriority, - _outsidePriority = outsidePriority ?? Ball.boardPriority { - layer = Layer.opening; - } - final Layer _insideLayer; - final Layer _outsideLayer; - final int _insidePriority; - final int _outsidePriority; - - /// Mask of category bits for collision inside ramp. - Layer get insideLayer => _insideLayer; - - /// Mask of category bits for collision outside ramp. - Layer get outsideLayer => _outsideLayer; - - /// Priority for the [Ball] inside ramp. - int get insidePriority => _insidePriority; - - /// Priority for the [Ball] outside ramp. - int get outsidePriority => _outsidePriority; - - /// The [Shape] of the [RampOpening]. - Shape get shape; - - /// {@macro ramp_orientation} - // TODO(ruimiguel): Try to remove the need of [RampOrientation] for collision - // calculations. - final RampOrientation orientation; - - @override - Body createBody() { - final fixtureDef = FixtureDef(shape)..isSensor = true; - - 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 ramp through a [RampOpening]. -/// -/// Modifies [Ball]'s [Layer] accordingly depending on whether the [Ball] is -/// outside or inside a ramp. -/// {@endtemplate} -class RampOpeningBallContactCallback - extends ContactCallback { - /// [Ball]s currently inside the ramp. - final _ballsInside = {}; - - @override - void begin(Ball ball, Opening opening, Contact _) { - Layer layer; - - if (!_ballsInside.contains(ball)) { - layer = opening.insideLayer; - _ballsInside.add(ball); - ball - ..sendTo(opening.insidePriority) - ..layer = layer; - } else { - _ballsInside.remove(ball); - } - } - - @override - void end(Ball ball, Opening opening, Contact _) { - if (!_ballsInside.contains(ball)) { - ball.layer = opening.outsideLayer; - } else { - // TODO(ruimiguel): change this code. Check what happens with ball that - // slightly touch Opening and goes out again. With InitialPosition change - // now doesn't work position.y comparison - final isBallOutsideOpening = - (opening.orientation == RampOrientation.down && - ball.body.linearVelocity.y > 0) || - (opening.orientation == RampOrientation.up && - ball.body.linearVelocity.y < 0); - - if (isBallOutsideOpening) { - ball - ..sendTo(opening.outsidePriority) - ..layer = opening.outsideLayer; - _ballsInside.remove(ball); - } - } - } -} diff --git a/packages/pinball_components/lib/src/components/slingshot.dart b/packages/pinball_components/lib/src/components/slingshot.dart index d6eabd83..b460565b 100644 --- a/packages/pinball_components/lib/src/components/slingshot.dart +++ b/packages/pinball_components/lib/src/components/slingshot.dart @@ -3,6 +3,7 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template slingshots} /// A [Blueprint] which creates the pair of [Slingshot]s on the right side of @@ -41,27 +42,29 @@ class Slingshot extends BodyComponent with InitialPosition { required String spritePath, }) : _length = length, _angle = angle, - _spritePath = spritePath, - super(priority: 1); + super( + priority: 1, + children: [_SlinghsotSpriteComponent(spritePath, angle: angle)], + ) { + renderBody = false; + } final double _length; final double _angle; - final String _spritePath; - List _createFixtureDefs() { final fixturesDef = []; const circleRadius = 1.55; final topCircleShape = CircleShape()..radius = circleRadius; topCircleShape.position.setValues(0, -_length / 2); - final topCircleFixtureDef = FixtureDef(topCircleShape)..friction = 0; + final topCircleFixtureDef = FixtureDef(topCircleShape); fixturesDef.add(topCircleFixtureDef); final bottomCircleShape = CircleShape()..radius = circleRadius; bottomCircleShape.position.setValues(0, _length / 2); - final bottomCircleFixtureDef = FixtureDef(bottomCircleShape)..friction = 0; + final bottomCircleFixtureDef = FixtureDef(bottomCircleShape); fixturesDef.add(bottomCircleFixtureDef); final leftEdgeShape = EdgeShape() @@ -69,9 +72,11 @@ class Slingshot extends BodyComponent with InitialPosition { Vector2(circleRadius, _length / 2), Vector2(circleRadius, -_length / 2), ); - final leftEdgeShapeFixtureDef = FixtureDef(leftEdgeShape) - ..friction = 0 - ..restitution = 5; + final leftEdgeShapeFixtureDef = FixtureDef( + leftEdgeShape, + restitution: 5, + ); + fixturesDef.add(leftEdgeShapeFixtureDef); final rightEdgeShape = EdgeShape() @@ -79,9 +84,10 @@ class Slingshot extends BodyComponent with InitialPosition { Vector2(-circleRadius, _length / 2), Vector2(-circleRadius, -_length / 2), ); - final rightEdgeShapeFixtureDef = FixtureDef(rightEdgeShape) - ..friction = 0 - ..restitution = 5; + final rightEdgeShapeFixtureDef = FixtureDef( + rightEdgeShape, + restitution: 5, + ); fixturesDef.add(rightEdgeShapeFixtureDef); return fixturesDef; @@ -89,34 +95,36 @@ class Slingshot extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..angle = _angle; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + angle: _angle, + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); return body; } +} + +class _SlinghsotSpriteComponent extends SpriteComponent with HasGameRef { + _SlinghsotSpriteComponent( + String path, { + required double angle, + }) : _path = path, + super( + angle: -angle, + anchor: Anchor.center, + ); + + final String _path; @override Future onLoad() async { await super.onLoad(); - await _loadSprite(); - renderBody = false; - } - - Future _loadSprite() async { - final sprite = await gameRef.loadSprite(_spritePath); - - await add( - SpriteComponent( - sprite: sprite, - size: sprite.originalSize / 10, - anchor: Anchor.center, - angle: -_angle, - ), - ); + final sprite = await gameRef.loadSprite(_path); + this.sprite = sprite; + size = sprite.originalSize / 10; } } diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index b48bed4d..4beb0367 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -7,6 +7,7 @@ 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; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship} /// A [Blueprint] which creates the spaceship feature. @@ -24,19 +25,19 @@ class Spaceship extends Forge2DBlueprint { @override void build(_) { addAllContactCallback([ - SpaceshipHoleBallContactCallback(), - SpaceshipEntranceBallContactCallback(), + LayerSensorBallContactCallback<_SpaceshipEntrance>(), + LayerSensorBallContactCallback<_SpaceshipHole>(), ]); addAll([ SpaceshipSaucer()..initialPosition = position, - SpaceshipEntrance()..initialPosition = position, + _SpaceshipEntrance()..initialPosition = position, AndroidHead()..initialPosition = position, - SpaceshipHole( + _SpaceshipHole( outsideLayer: Layer.spaceshipExitRail, outsidePriority: Ball.spaceshipRailPriority, )..initialPosition = position - Vector2(5.2, -4.8), - SpaceshipHole()..initialPosition = position - Vector2(-7.2, -0.8), + _SpaceshipHole()..initialPosition = position - Vector2(-7.2, -0.8), SpaceshipWall()..initialPosition = position, ]); } @@ -71,17 +72,17 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final circleShape = CircleShape()..radius = 3; - - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..type = BodyType.static; + final shape = CircleShape()..radius = 3; + final fixtureDef = FixtureDef( + shape, + isSensor: true, + ); + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); - return world.createBody(bodyDef) - ..createFixture( - FixtureDef(circleShape)..isSensor = true, - ); + return world.createBody(bodyDef)..createFixture(fixtureDef); } } @@ -91,26 +92,23 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { /// {@endtemplate} class AndroidHead extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_bridge} - AndroidHead() : super(priority: Ball.spaceshipPriority + 1) { - layer = Layer.spaceship; - } - - @override - Future onLoad() async { - await super.onLoad(); + AndroidHead() + : super( + priority: Ball.spaceshipPriority + 1, + children: [_AndroidHeadSpriteAnimation()], + ) { renderBody = false; - - await add(_AndroidHeadSpriteAnimation()); + layer = Layer.spaceship; } @override Body createBody() { final circleShape = CircleShape()..radius = 2; - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..type = BodyType.static; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); return world.createBody(bodyDef) ..createFixture( @@ -142,17 +140,11 @@ class _AndroidHeadSpriteAnimation extends SpriteAnimationComponent } } -/// {@template spaceship_entrance} -/// A sensor [BodyComponent] used to detect when the ball enters the -/// the spaceship area in order to modify its filter data so the ball -/// can correctly collide only with the Spaceship -/// {@endtemplate} -class SpaceshipEntrance extends RampOpening { - /// {@macro spaceship_entrance} - SpaceshipEntrance() +class _SpaceshipEntrance extends LayerSensor { + _SpaceshipEntrance() : super( insideLayer: Layer.spaceship, - orientation: RampOrientation.up, + orientation: LayerEntranceOrientation.up, insidePriority: Ball.spaceshipPriority, ) { layer = Layer.spaceship; @@ -176,17 +168,12 @@ class SpaceshipEntrance extends RampOpening { } } -/// {@template spaceship_hole} -/// A sensor [BodyComponent] responsible for sending the [Ball] -/// out from the [Spaceship]. -/// {@endtemplate} -class SpaceshipHole extends RampOpening { - /// {@macro spaceship_hole} - SpaceshipHole({Layer? outsideLayer, int? outsidePriority = 1}) +class _SpaceshipHole extends LayerSensor { + _SpaceshipHole({Layer? outsideLayer, int? outsidePriority = 1}) : super( insideLayer: Layer.spaceship, outsideLayer: outsideLayer, - orientation: RampOrientation.up, + orientation: LayerEntranceOrientation.down, insidePriority: 4, outsidePriority: outsidePriority, ) { @@ -246,47 +233,15 @@ class SpaceshipWall extends BodyComponent with InitialPosition, Layered { Body createBody() { renderBody = false; - final wallShape = _SpaceshipWallShape(); - - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..angle = -1.7 - ..type = BodyType.static; - - return world.createBody(bodyDef) - ..createFixture( - FixtureDef(wallShape)..restitution = 1, - ); - } -} + final shape = _SpaceshipWallShape(); + final fixtureDef = FixtureDef(shape); -/// [ContactCallback] that handles the contact between the [Ball] -/// and the [SpaceshipEntrance]. -/// -/// It modifies the [Ball] priority and filter data so it can appear on top of -/// the spaceship and also only collide with the spaceship. -class SpaceshipEntranceBallContactCallback - extends ContactCallback { - @override - void begin(SpaceshipEntrance entrance, Ball ball, _) { - ball - ..sendTo(entrance.insidePriority) - ..layer = Layer.spaceship; - } -} + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + angle: -1.7, + ); -/// [ContactCallback] that handles the contact between the [Ball] -/// and a [SpaceshipHole]. -/// -/// It sets the [Ball] priority and filter data so it will outside of the -/// [Spaceship]. -class SpaceshipHoleBallContactCallback - extends ContactCallback { - @override - void begin(SpaceshipHole hole, Ball ball, _) { - ball - ..sendTo(hole.outsidePriority) - ..layer = hole.outsideLayer; + return world.createBody(bodyDef)..createFixture(fixtureDef); } } diff --git a/packages/pinball_components/lib/src/components/spaceship_rail.dart b/packages/pinball_components/lib/src/components/spaceship_rail.dart index d9d6bc09..cc308c77 100644 --- a/packages/pinball_components/lib/src/components/spaceship_rail.dart +++ b/packages/pinball_components/lib/src/components/spaceship_rail.dart @@ -6,6 +6,7 @@ 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; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship_rail} /// A [Blueprint] for the spaceship drop tube. @@ -17,11 +18,11 @@ class SpaceshipRail extends Forge2DBlueprint { @override void build(_) { addAllContactCallback([ - SpaceshipRailExitBallContactCallback(), + LayerSensorBallContactCallback<_SpaceshipRailExit>(), ]); final railRamp = _SpaceshipRailRamp(); - final railEnd = SpaceshipRailExit(); + final railEnd = _SpaceshipRailExit(); final topBase = _SpaceshipRailBase(radius: 0.55) ..initialPosition = Vector2(-26.15, -18.65); final bottomBase = _SpaceshipRailBase(radius: 0.8) @@ -122,9 +123,10 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -189,26 +191,20 @@ class _SpaceshipRailBase extends BodyComponent with InitialPosition, Layered { @override Body createBody() { final shape = CircleShape()..radius = radius; - final fixtureDef = FixtureDef(shape); - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; + 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() +class _SpaceshipRailExit extends LayerSensor { + _SpaceshipRailExit() : super( - orientation: RampOrientation.down, + orientation: LayerEntranceOrientation.down, insideLayer: Layer.spaceshipExitRail, insidePriority: Ball.spaceshipRailPriority, ) { @@ -226,18 +222,3 @@ class SpaceshipRailExit extends RampOpening { ); } } - -/// [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 index f7477fe9..b0adb8cb 100644 --- a/packages/pinball_components/lib/src/components/spaceship_ramp.dart +++ b/packages/pinball_components/lib/src/components/spaceship_ramp.dart @@ -6,6 +6,7 @@ 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; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship_ramp} /// A [Blueprint] which creates the ramp leading into the [Spaceship]. @@ -17,7 +18,7 @@ class SpaceshipRamp extends Forge2DBlueprint { @override void build(_) { addAllContactCallback([ - RampOpeningBallContactCallback<_SpaceshipRampOpening>(), + LayerSensorBallContactCallback<_SpaceshipRampOpening>(), ]); final rightOpening = _SpaceshipRampOpening( @@ -75,7 +76,6 @@ class _SpaceshipRampBackground extends BodyComponent Vector2(-14.2, -71.25), ], ); - final outerLeftCurveFixtureDef = FixtureDef(outerLeftCurveShape); fixturesDef.add(outerLeftCurveFixtureDef); @@ -86,7 +86,6 @@ class _SpaceshipRampBackground extends BodyComponent Vector2(6.1, -44.9), ], ); - final outerRightCurveFixtureDef = FixtureDef(outerRightCurveShape); fixturesDef.add(outerRightCurveFixtureDef); @@ -103,9 +102,10 @@ class _SpaceshipRampBackground extends BodyComponent @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -170,8 +170,12 @@ class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent class _SpaceshipRampForegroundRailing extends BodyComponent with InitialPosition, Layered { _SpaceshipRampForegroundRailing() - : super(priority: Ball.spaceshipRampPriority + 1) { + : super( + priority: Ball.spaceshipRampPriority + 1, + children: [_SpaceshipRampForegroundRailingSpriteComponent()], + ) { layer = Layer.spaceshipEntranceRamp; + renderBody = false; } List _createFixtureDefs() { @@ -212,23 +216,16 @@ class _SpaceshipRampForegroundRailing extends BodyComponent @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); return body; } - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - - await add(_SpaceshipRampForegroundRailingSpriteComponent()); - } } class _SpaceshipRampForegroundRailingSpriteComponent extends SpriteComponent @@ -266,20 +263,20 @@ class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered { ], ); final fixtureDef = FixtureDef(baseShape); - - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } } /// {@template spaceship_ramp_opening} -/// [RampOpening] with [Layer.spaceshipEntranceRamp] to filter [Ball] collisions +/// [LayerSensor] with [Layer.spaceshipEntranceRamp] to filter [Ball] collisions /// inside [_SpaceshipRampBackground]. /// {@endtemplate} -class _SpaceshipRampOpening extends RampOpening { +class _SpaceshipRampOpening extends LayerSensor { /// {@macro spaceship_ramp_opening} _SpaceshipRampOpening({ Layer? outsideLayer, @@ -289,7 +286,7 @@ class _SpaceshipRampOpening extends RampOpening { super( insideLayer: Layer.spaceshipEntranceRamp, outsideLayer: outsideLayer, - orientation: RampOrientation.down, + orientation: LayerEntranceOrientation.down, insidePriority: Ball.spaceshipRampPriority, outsidePriority: outsidePriority, ) { diff --git a/packages/pinball_components/lib/src/components/sparky_bumper.dart b/packages/pinball_components/lib/src/components/sparky_bumper.dart index dcd20352..3022ed38 100644 --- a/packages/pinball_components/lib/src/components/sparky_bumper.dart +++ b/packages/pinball_components/lib/src/components/sparky_bumper.dart @@ -90,10 +90,10 @@ class SparkyBumper extends BodyComponent with InitialPosition { majorRadius: _majorRadius, minorRadius: _minorRadius, )..rotate(math.pi / 2.1); - final fixtureDef = FixtureDef(shape) - ..friction = 0 - ..restitution = 4; - + final fixtureDef = FixtureDef( + shape, + restitution: 4, + ); final bodyDef = BodyDef() ..position = initialPosition ..userData = this; diff --git a/packages/pinball_components/lib/src/components/sparky_computer.dart b/packages/pinball_components/lib/src/components/sparky_computer.dart index f2edc343..04ab315f 100644 --- a/packages/pinball_components/lib/src/components/sparky_computer.dart +++ b/packages/pinball_components/lib/src/components/sparky_computer.dart @@ -3,6 +3,7 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template sparky_computer} /// A [Blueprint] which creates the [_ComputerBase] and @@ -56,9 +57,10 @@ class _ComputerBase extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); diff --git a/packages/pinball_components/lib/src/flame/flame.dart b/packages/pinball_components/lib/src/flame/flame.dart deleted file mode 100644 index 9b766995..00000000 --- a/packages/pinball_components/lib/src/flame/flame.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'blueprint.dart'; -export 'keyboard_input_controller.dart'; -export 'priority.dart'; diff --git a/packages/pinball_components/lib/src/flame/priority.dart b/packages/pinball_components/lib/src/flame/priority.dart deleted file mode 100644 index f4dccabf..00000000 --- a/packages/pinball_components/lib/src/flame/priority.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'dart:math' as math; -import 'package:flame/components.dart'; - -/// Helper methods to change the [priority] of a [Component]. -extension ComponentPriorityX on Component { - static const _lowestPriority = 0; - - /// Changes the priority to a specific one. - void sendTo(int destinationPriority) { - if (priority != destinationPriority) { - priority = math.max(destinationPriority, _lowestPriority); - reorderChildren(); - } - } - - /// Changes the priority to the lowest possible. - void sendToBack() { - if (priority != _lowestPriority) { - priority = _lowestPriority; - reorderChildren(); - } - } - - /// Decreases the priority to be lower than another [Component]. - void showBehindOf(Component other) { - if (priority >= other.priority) { - priority = math.max(other.priority - 1, _lowestPriority); - reorderChildren(); - } - } - - /// Increases the priority to be higher than another [Component]. - void showInFrontOf(Component other) { - if (priority <= other.priority) { - priority = other.priority + 1; - reorderChildren(); - } - } -} diff --git a/packages/pinball_components/lib/src/pinball_components.dart b/packages/pinball_components/lib/src/pinball_components.dart index 50dee227..e50f9875 100644 --- a/packages/pinball_components/lib/src/pinball_components.dart +++ b/packages/pinball_components/lib/src/pinball_components.dart @@ -1,3 +1,2 @@ export 'components/components.dart'; export 'extensions/extensions.dart'; -export 'flame/flame.dart'; diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index d27084f1..be06949c 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -14,6 +14,8 @@ dependencies: geometry: path: ../geometry intl: ^0.17.0 + pinball_flame: + path: ../pinball_flame dev_dependencies: diff --git a/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart b/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart index 0bc2755d..ab984045 100644 --- a/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart @@ -1,5 +1,6 @@ import 'package:flame/extensions.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; diff --git a/packages/pinball_components/sandbox/lib/stories/launch_ramp/launch_ramp_game.dart b/packages/pinball_components/sandbox/lib/stories/launch_ramp/launch_ramp_game.dart index b3d23b7a..67fa9431 100644 --- a/packages/pinball_components/sandbox/lib/stories/launch_ramp/launch_ramp_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/launch_ramp/launch_ramp_game.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flame/input.dart'; import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class LaunchRampGame extends BasicBallGame { diff --git a/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart b/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart index dd1df4de..7d941099 100644 --- a/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart @@ -1,5 +1,6 @@ import 'package:flame/extensions.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; diff --git a/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart b/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart index 95afcd7f..6c00f476 100644 --- a/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/spaceship/basic_spaceship_game.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flame/input.dart'; import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:sandbox/common/common.dart'; class BasicSpaceshipGame extends BasicGame with TapDetector { diff --git a/packages/pinball_components/sandbox/lib/stories/spaceship_rail/spaceship_rail_game.dart b/packages/pinball_components/sandbox/lib/stories/spaceship_rail/spaceship_rail_game.dart index f4da4786..c738d56a 100644 --- a/packages/pinball_components/sandbox/lib/stories/spaceship_rail/spaceship_rail_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/spaceship_rail/spaceship_rail_game.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flame/input.dart'; import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class SpaceshipRailGame extends BasicBallGame { diff --git a/packages/pinball_components/sandbox/lib/stories/spaceship_ramp/spaceship_ramp_game.dart b/packages/pinball_components/sandbox/lib/stories/spaceship_ramp/spaceship_ramp_game.dart index 2e6f6d56..aa2ed5cd 100644 --- a/packages/pinball_components/sandbox/lib/stories/spaceship_ramp/spaceship_ramp_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/spaceship_ramp/spaceship_ramp_game.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flame/input.dart'; import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class SpaceshipRampGame extends BasicBallGame { diff --git a/packages/pinball_components/sandbox/pubspec.lock b/packages/pinball_components/sandbox/pubspec.lock index 61af3a8a..7f78e365 100644 --- a/packages/pinball_components/sandbox/pubspec.lock +++ b/packages/pinball_components/sandbox/pubspec.lock @@ -240,6 +240,13 @@ packages: relative: true source: path version: "1.0.0+1" + pinball_flame: + dependency: "direct main" + description: + path: "../../pinball_flame" + relative: true + source: path + version: "1.0.0+1" platform: dependency: transitive description: diff --git a/packages/pinball_components/sandbox/pubspec.yaml b/packages/pinball_components/sandbox/pubspec.yaml index 6687e358..03a46ee0 100644 --- a/packages/pinball_components/sandbox/pubspec.yaml +++ b/packages/pinball_components/sandbox/pubspec.yaml @@ -14,6 +14,8 @@ dependencies: sdk: flutter pinball_components: path: ../ + pinball_flame: + path: ../../pinball_flame dev_dependencies: flutter_test: diff --git a/packages/pinball_components/test/helpers/mocks.dart b/packages/pinball_components/test/helpers/mocks.dart index 21d5d01a..520555df 100644 --- a/packages/pinball_components/test/helpers/mocks.dart +++ b/packages/pinball_components/test/helpers/mocks.dart @@ -13,12 +13,6 @@ class MockBall extends Mock implements Ball {} class MockGame extends Mock implements Forge2DGame {} -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 index 9fad34be..e62d63e3 100644 --- a/packages/pinball_components/test/src/components/boundaries_test.dart +++ b/packages/pinball_components/test/src/components/boundaries_test.dart @@ -4,6 +4,7 @@ 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 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; @@ -17,6 +18,7 @@ void main() { await game.addFromBlueprint(Boundaries()); game.camera.followVector2(Vector2.zero()); game.camera.zoom = 3.9; + await game.ready(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/dino_walls_test.dart b/packages/pinball_components/test/src/components/dino_walls_test.dart index de3fa2b9..b3a58264 100644 --- a/packages/pinball_components/test/src/components/dino_walls_test.dart +++ b/packages/pinball_components/test/src/components/dino_walls_test.dart @@ -4,6 +4,7 @@ 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 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; @@ -18,6 +19,7 @@ void main() { await game.addFromBlueprint(DinoWalls()); game.camera.followVector2(Vector2.zero()); game.camera.zoom = 6.5; + await game.ready(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/launch_ramp_test.dart b/packages/pinball_components/test/src/components/launch_ramp_test.dart index e56bfae9..1f5d6f26 100644 --- a/packages/pinball_components/test/src/components/launch_ramp_test.dart +++ b/packages/pinball_components/test/src/components/launch_ramp_test.dart @@ -4,6 +4,7 @@ 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 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; diff --git a/packages/pinball_components/test/src/components/layer_sensor_test.dart b/packages/pinball_components/test/src/components/layer_sensor_test.dart new file mode 100644 index 00000000..c87f02b3 --- /dev/null +++ b/packages/pinball_components/test/src/components/layer_sensor_test.dart @@ -0,0 +1,181 @@ +// 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'; + +class TestLayerSensor extends LayerSensor { + TestLayerSensor({ + required LayerEntranceOrientation orientation, + required int insidePriority, + required Layer insideLayer, + }) : super( + insideLayer: insideLayer, + insidePriority: insidePriority, + orientation: orientation, + ); + + @override + Shape get shape => PolygonShape()..setAsBoxXY(1, 1); +} + +class TestLayerSensorBallContactCallback + extends LayerSensorBallContactCallback { + TestLayerSensorBallContactCallback() : super(); +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + const insidePriority = 1; + + group('LayerSensor', () { + flameTester.test( + 'loads correctly', + (game) async { + final layerSensor = TestLayerSensor( + orientation: LayerEntranceOrientation.down, + insidePriority: insidePriority, + insideLayer: Layer.spaceshipEntranceRamp, + ); + await game.ensureAdd(layerSensor); + + expect(game.contains(layerSensor), isTrue); + }, + ); + + group('body', () { + flameTester.test( + 'is static', + (game) async { + final layerSensor = TestLayerSensor( + orientation: LayerEntranceOrientation.down, + insidePriority: insidePriority, + insideLayer: Layer.spaceshipEntranceRamp, + ); + await game.ensureAdd(layerSensor); + + expect(layerSensor.body.bodyType, equals(BodyType.static)); + }, + ); + + group('first fixture', () { + const pathwayLayer = Layer.spaceshipEntranceRamp; + const openingLayer = Layer.opening; + + flameTester.test( + 'exists', + (game) async { + final layerSensor = TestLayerSensor( + orientation: LayerEntranceOrientation.down, + insidePriority: insidePriority, + insideLayer: pathwayLayer, + )..layer = openingLayer; + await game.ensureAdd(layerSensor); + + expect(layerSensor.body.fixtures[0], isA()); + }, + ); + + flameTester.test( + 'shape is a polygon', + (game) async { + final layerSensor = TestLayerSensor( + orientation: LayerEntranceOrientation.down, + insidePriority: insidePriority, + insideLayer: pathwayLayer, + )..layer = openingLayer; + await game.ensureAdd(layerSensor); + + final fixture = layerSensor.body.fixtures[0]; + expect(fixture.shape.shapeType, equals(ShapeType.polygon)); + }, + ); + + flameTester.test( + 'is sensor', + (game) async { + final layerSensor = TestLayerSensor( + orientation: LayerEntranceOrientation.down, + insidePriority: insidePriority, + insideLayer: pathwayLayer, + )..layer = openingLayer; + await game.ensureAdd(layerSensor); + + final fixture = layerSensor.body.fixtures[0]; + expect(fixture.isSensor, isTrue); + }, + ); + }); + }); + }); + + group('LayerSensorBallContactCallback', () { + late Ball ball; + late Body body; + + setUp(() { + ball = MockBall(); + body = MockBody(); + + when(() => ball.body).thenReturn(body); + when(() => ball.priority).thenReturn(1); + when(() => ball.layer).thenReturn(Layer.board); + }); + + flameTester.test( + 'changes ball layer and priority ' + 'when a ball enters and exits a downward oriented LayerSensor', + (game) async { + final sensor = TestLayerSensor( + orientation: LayerEntranceOrientation.down, + insidePriority: insidePriority, + insideLayer: Layer.spaceshipEntranceRamp, + )..initialPosition = Vector2(0, 10); + final callback = TestLayerSensorBallContactCallback(); + + when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); + + callback.begin(ball, sensor, MockContact()); + verify(() => ball.layer = sensor.insideLayer).called(1); + verify(() => ball.priority = sensor.insidePriority).called(1); + verify(ball.reorderChildren).called(1); + + when(() => ball.layer).thenReturn(sensor.insideLayer); + + callback.begin(ball, sensor, MockContact()); + verify(() => ball.layer = Layer.board); + verify(() => ball.priority = Ball.boardPriority).called(1); + verify(ball.reorderChildren).called(1); + }); + + flameTester.test( + 'changes ball layer and priority ' + 'when a ball enters and exits an upward oriented LayerSensor', + (game) async { + final sensor = TestLayerSensor( + orientation: LayerEntranceOrientation.up, + insidePriority: insidePriority, + insideLayer: Layer.spaceshipEntranceRamp, + )..initialPosition = Vector2(0, 10); + final callback = TestLayerSensorBallContactCallback(); + + when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); + + callback.begin(ball, sensor, MockContact()); + verify(() => ball.layer = sensor.insideLayer).called(1); + verify(() => ball.priority = sensor.insidePriority).called(1); + verify(ball.reorderChildren).called(1); + + when(() => ball.layer).thenReturn(sensor.insideLayer); + + callback.begin(ball, sensor, MockContact()); + verify(() => ball.layer = Layer.board); + verify(() => ball.priority = Ball.boardPriority).called(1); + verify(ball.reorderChildren).called(1); + }); + }); +} diff --git a/packages/pinball_components/test/src/components/ramp_opening_test.dart b/packages/pinball_components/test/src/components/ramp_opening_test.dart deleted file mode 100644 index 37b8edb0..00000000 --- a/packages/pinball_components/test/src/components/ramp_opening_test.dart +++ /dev/null @@ -1,249 +0,0 @@ -// 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'; - -class TestRampOpening extends RampOpening { - TestRampOpening({ - required RampOrientation orientation, - required int insidePriority, - required Layer pathwayLayer, - }) : super( - insideLayer: pathwayLayer, - insidePriority: insidePriority, - orientation: orientation, - ); - - @override - Shape get shape => PolygonShape() - ..set([ - Vector2(0, 0), - Vector2(0, 1), - Vector2(1, 1), - Vector2(1, 0), - ]); -} - -class TestRampOpeningBallContactCallback - extends RampOpeningBallContactCallback { - TestRampOpeningBallContactCallback() : super(); -} - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); - const insidePriority = 1; - - group('RampOpening', () { - flameTester.test( - 'loads correctly', - (game) async { - final ramp = TestRampOpening( - orientation: RampOrientation.down, - insidePriority: insidePriority, - pathwayLayer: Layer.spaceshipEntranceRamp, - ); - await game.ready(); - await game.ensureAdd(ramp); - - expect(game.contains(ramp), isTrue); - }, - ); - - group('body', () { - flameTester.test( - 'is static', - (game) async { - final ramp = TestRampOpening( - orientation: RampOrientation.down, - insidePriority: insidePriority, - pathwayLayer: Layer.spaceshipEntranceRamp, - ); - await game.ensureAdd(ramp); - - expect(ramp.body.bodyType, equals(BodyType.static)); - }, - ); - - group('first fixture', () { - const pathwayLayer = Layer.spaceshipEntranceRamp; - const openingLayer = Layer.opening; - - flameTester.test( - 'exists', - (game) async { - final ramp = TestRampOpening( - orientation: RampOrientation.down, - insidePriority: insidePriority, - pathwayLayer: pathwayLayer, - )..layer = openingLayer; - await game.ensureAdd(ramp); - - expect(ramp.body.fixtures[0], isA()); - }, - ); - - flameTester.test( - 'shape is a polygon', - (game) async { - final ramp = TestRampOpening( - orientation: RampOrientation.down, - insidePriority: insidePriority, - pathwayLayer: pathwayLayer, - )..layer = openingLayer; - await game.ensureAdd(ramp); - - final fixture = ramp.body.fixtures[0]; - expect(fixture.shape.shapeType, equals(ShapeType.polygon)); - }, - ); - - flameTester.test( - 'is sensor', - (game) async { - final ramp = TestRampOpening( - orientation: RampOrientation.down, - insidePriority: insidePriority, - pathwayLayer: pathwayLayer, - )..layer = openingLayer; - await game.ensureAdd(ramp); - - final fixture = ramp.body.fixtures[0]; - expect(fixture.isSensor, isTrue); - }, - ); - }); - }); - }); - - group('RampOpeningBallContactCallback', () { - flameTester.test( - 'changes ball layer ' - 'when a ball enters upwards into a downward ramp opening', - (game) async { - final ball = MockBall(); - final body = MockBody(); - final area = TestRampOpening( - orientation: RampOrientation.down, - insidePriority: insidePriority, - pathwayLayer: Layer.spaceshipEntranceRamp, - ); - final callback = TestRampOpeningBallContactCallback(); - - when(() => ball.body).thenReturn(body); - when(() => ball.priority).thenReturn(1); - when(() => body.position).thenReturn(Vector2.zero()); - when(() => ball.layer).thenReturn(Layer.board); - - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.insideLayer).called(1); - }); - - flameTester.test( - 'changes ball layer ' - 'when a ball enters downwards into a upward ramp opening', - (game) async { - final ball = MockBall(); - final body = MockBody(); - final area = TestRampOpening( - orientation: RampOrientation.up, - insidePriority: insidePriority, - pathwayLayer: Layer.spaceshipEntranceRamp, - ); - final callback = TestRampOpeningBallContactCallback(); - - when(() => ball.body).thenReturn(body); - when(() => ball.priority).thenReturn(1); - when(() => body.position).thenReturn(Vector2.zero()); - when(() => ball.layer).thenReturn(Layer.board); - - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.insideLayer).called(1); - }); - - flameTester.test( - 'changes ball layer ' - 'when a ball exits from a downward oriented ramp', (game) async { - final ball = MockBall(); - final body = MockBody(); - final area = TestRampOpening( - orientation: RampOrientation.down, - insidePriority: insidePriority, - pathwayLayer: Layer.spaceshipEntranceRamp, - )..initialPosition = Vector2(0, 10); - final callback = TestRampOpeningBallContactCallback(); - - when(() => ball.body).thenReturn(body); - when(() => ball.priority).thenReturn(1); - when(() => body.position).thenReturn(Vector2.zero()); - when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); - when(() => ball.layer).thenReturn(Layer.board); - - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.insideLayer).called(1); - - callback.end(ball, area, MockContact()); - verify(() => ball.layer = Layer.board); - }); - - flameTester.test( - 'changes ball layer ' - 'when a ball exits from a upward oriented ramp', (game) async { - final ball = MockBall(); - final body = MockBody(); - final area = TestRampOpening( - orientation: RampOrientation.up, - insidePriority: insidePriority, - pathwayLayer: Layer.spaceshipEntranceRamp, - )..initialPosition = Vector2(0, 10); - final callback = TestRampOpeningBallContactCallback(); - - when(() => ball.body).thenReturn(body); - when(() => ball.priority).thenReturn(1); - when(() => body.position).thenReturn(Vector2.zero()); - when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); - when(() => ball.layer).thenReturn(Layer.board); - - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.insideLayer).called(1); - - callback.end(ball, area, MockContact()); - verify(() => ball.layer = Layer.board); - }); - - flameTester.test( - 'change ball layer from pathwayLayer to Layer.board ' - 'when a ball enters and exits from ramp', (game) async { - final ball = MockBall(); - final body = MockBody(); - final area = TestRampOpening( - orientation: RampOrientation.down, - insidePriority: insidePriority, - pathwayLayer: Layer.spaceshipEntranceRamp, - )..initialPosition = Vector2(0, 10); - final callback = TestRampOpeningBallContactCallback(); - - when(() => ball.body).thenReturn(body); - when(() => ball.priority).thenReturn(1); - when(() => body.position).thenReturn(Vector2.zero()); - when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); - when(() => ball.layer).thenReturn(Layer.board); - - callback.begin(ball, area, MockContact()); - verify(() => ball.layer = area.insideLayer).called(1); - - callback.end(ball, area, MockContact()); - verifyNever(() => ball.layer = Layer.board); - - callback.begin(ball, area, MockContact()); - verifyNever(() => ball.layer = area.insideLayer); - - callback.end(ball, area, MockContact()); - verify(() => ball.layer = Layer.board); - }); - }); -} diff --git a/packages/pinball_components/test/src/components/slingshot_test.dart b/packages/pinball_components/test/src/components/slingshot_test.dart index f32f01d2..a5535750 100644 --- a/packages/pinball_components/test/src/components/slingshot_test.dart +++ b/packages/pinball_components/test/src/components/slingshot_test.dart @@ -4,6 +4,7 @@ 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 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; @@ -19,6 +20,7 @@ void main() { setUp: (game, tester) async { await game.addFromBlueprint(Slingshots()); game.camera.followVector2(Vector2.zero()); + await game.ready(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/spaceship_rail_test.dart b/packages/pinball_components/test/src/components/spaceship_rail_test.dart index c35798c3..c73c8dee 100644 --- a/packages/pinball_components/test/src/components/spaceship_rail_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_rail_test.dart @@ -3,8 +3,8 @@ 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 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; @@ -41,57 +41,4 @@ void main() { }, ); }); - - // 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 index b6afcde2..8b623461 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp_test.dart @@ -4,6 +4,7 @@ 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 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; @@ -16,6 +17,7 @@ void main() { setUp: (game, tester) async { await game.addFromBlueprint(SpaceshipRamp()); game.camera.followVector2(Vector2(-13, -50)); + await game.ready(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/spaceship_test.dart b/packages/pinball_components/test/src/components/spaceship_test.dart index 0f627be9..c9a90746 100644 --- a/packages/pinball_components/test/src/components/spaceship_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_test.dart @@ -5,6 +5,7 @@ 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 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; @@ -14,8 +15,6 @@ void main() { late Fixture fixture; late Body body; late Ball ball; - late SpaceshipEntrance entrance; - late SpaceshipHole hole; late Forge2DGame game; setUp(() { @@ -32,9 +31,6 @@ void main() { ball = MockBall(); when(() => ball.gameRef).thenReturn(game); when(() => ball.body).thenReturn(body); - - entrance = MockSpaceshipEntrance(); - hole = MockSpaceshipHole(); }); group('Spaceship', () { @@ -46,6 +42,7 @@ void main() { final position = Vector2(30, -30); await game.addFromBlueprint(Spaceship(position: position)); game.camera.followVector2(position); + await game.ready(); }, verify: (game, tester) async { await expectLater( @@ -55,36 +52,5 @@ void main() { }, ); }); - - group('SpaceshipEntranceBallContactCallback', () { - test('changes the ball priority on contact', () { - when(() => ball.priority).thenReturn(2); - when(() => entrance.insidePriority).thenReturn(3); - - SpaceshipEntranceBallContactCallback().begin( - entrance, - ball, - MockContact(), - ); - - verify(() => ball.sendTo(entrance.insidePriority)).called(1); - }); - }); - - group('SpaceshipHoleBallContactCallback', () { - test('changes the ball priority on contact', () { - when(() => ball.priority).thenReturn(2); - when(() => hole.outsideLayer).thenReturn(Layer.board); - when(() => hole.outsidePriority).thenReturn(1); - - SpaceshipHoleBallContactCallback().begin( - hole, - ball, - MockContact(), - ); - - verify(() => ball.sendTo(hole.outsidePriority)).called(1); - }); - }); }); } diff --git a/packages/pinball_components/test/src/components/sparky_computer_test.dart b/packages/pinball_components/test/src/components/sparky_computer_test.dart index c7573338..3d67106d 100644 --- a/packages/pinball_components/test/src/components/sparky_computer_test.dart +++ b/packages/pinball_components/test/src/components/sparky_computer_test.dart @@ -4,6 +4,7 @@ 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 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; diff --git a/packages/pinball_components/test/src/flame/priority_test.dart b/packages/pinball_components/test/src/flame/priority_test.dart deleted file mode 100644 index 231c7744..00000000 --- a/packages/pinball_components/test/src/flame/priority_test.dart +++ /dev/null @@ -1,221 +0,0 @@ -// 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/src/flame/priority.dart'; - -import '../../helpers/helpers.dart'; - -class TestBodyComponent extends BodyComponent { - @override - Body createBody() { - final fixtureDef = FixtureDef(CircleShape()); - return world.createBody(BodyDef())..createFixture(fixtureDef); - } -} - -void main() { - final flameTester = FlameTester(Forge2DGame.new); - - group('ComponentPriorityX', () { - group('sendTo', () { - flameTester.test( - 'changes the priority correctly to other level', - (game) async { - const newPriority = 5; - final component = TestBodyComponent()..priority = 4; - - component.sendTo(newPriority); - - expect(component.priority, equals(newPriority)); - }, - ); - - flameTester.test( - 'calls reorderChildren if the new priority is different', - (game) async { - const newPriority = 5; - final component = MockComponent(); - when(() => component.priority).thenReturn(4); - - component.sendTo(newPriority); - - verify(component.reorderChildren).called(1); - }, - ); - - flameTester.test( - "doesn't call reorderChildren if the priority is the same", - (game) async { - const newPriority = 5; - final component = MockComponent(); - when(() => component.priority).thenReturn(newPriority); - - component.sendTo(newPriority); - - verifyNever(component.reorderChildren); - }, - ); - }); - - group('sendToBack', () { - flameTester.test( - 'changes the priority correctly to board level', - (game) async { - final component = TestBodyComponent()..priority = 4; - - component.sendToBack(); - - expect(component.priority, equals(0)); - }, - ); - - flameTester.test( - 'calls reorderChildren if the priority is greater than lowest level', - (game) async { - final component = MockComponent(); - when(() => component.priority).thenReturn(4); - - component.sendToBack(); - - verify(component.reorderChildren).called(1); - }, - ); - - flameTester.test( - "doesn't call reorderChildren if the priority is the lowest level", - (game) async { - final component = MockComponent(); - when(() => component.priority).thenReturn(0); - - component.sendToBack(); - - verifyNever(component.reorderChildren); - }, - ); - }); - - group('showBehindOf', () { - flameTester.test( - 'changes the priority if it is greater than other component', - (game) async { - const startPriority = 2; - final component = TestBodyComponent()..priority = startPriority; - final otherComponent = TestBodyComponent() - ..priority = startPriority - 1; - - component.showBehindOf(otherComponent); - - expect(component.priority, equals(otherComponent.priority - 1)); - }, - ); - - flameTester.test( - "doesn't change the priority if it is lower than other component", - (game) async { - const startPriority = 2; - final component = TestBodyComponent()..priority = startPriority; - final otherComponent = TestBodyComponent() - ..priority = startPriority + 1; - - component.showBehindOf(otherComponent); - - expect(component.priority, equals(startPriority)); - }, - ); - - flameTester.test( - 'calls reorderChildren if the priority is greater than other component', - (game) async { - const startPriority = 2; - final component = MockComponent(); - final otherComponent = MockComponent(); - when(() => component.priority).thenReturn(startPriority); - when(() => otherComponent.priority).thenReturn(startPriority - 1); - - component.showBehindOf(otherComponent); - - verify(component.reorderChildren).called(1); - }, - ); - - flameTester.test( - "doesn't call reorderChildren if the priority is lower than other " - 'component', - (game) async { - const startPriority = 2; - final component = MockComponent(); - final otherComponent = MockComponent(); - when(() => component.priority).thenReturn(startPriority); - when(() => otherComponent.priority).thenReturn(startPriority + 1); - - component.showBehindOf(otherComponent); - - verifyNever(component.reorderChildren); - }, - ); - }); - - group('showInFrontOf', () { - flameTester.test( - 'changes the priority if it is lower than other component', - (game) async { - const startPriority = 2; - final component = TestBodyComponent()..priority = startPriority; - final otherComponent = TestBodyComponent() - ..priority = startPriority + 1; - - component.showInFrontOf(otherComponent); - - expect(component.priority, equals(otherComponent.priority + 1)); - }, - ); - - flameTester.test( - "doesn't change the priority if it is greater than other component", - (game) async { - const startPriority = 2; - final component = TestBodyComponent()..priority = startPriority; - final otherComponent = TestBodyComponent() - ..priority = startPriority - 1; - - component.showInFrontOf(otherComponent); - - expect(component.priority, equals(startPriority)); - }, - ); - - flameTester.test( - 'calls reorderChildren if the priority is lower than other component', - (game) async { - const startPriority = 2; - final component = MockComponent(); - final otherComponent = MockComponent(); - when(() => component.priority).thenReturn(startPriority); - when(() => otherComponent.priority).thenReturn(startPriority + 1); - - component.showInFrontOf(otherComponent); - - verify(component.reorderChildren).called(1); - }, - ); - - flameTester.test( - "doesn't call reorderChildren if the priority is greater than other " - 'component', - (game) async { - const startPriority = 2; - final component = MockComponent(); - final otherComponent = MockComponent(); - when(() => component.priority).thenReturn(startPriority); - when(() => otherComponent.priority).thenReturn(startPriority - 1); - - component.showInFrontOf(otherComponent); - - verifyNever(component.reorderChildren); - }, - ); - }); - }); -} diff --git a/packages/pinball_flame/.gitignore b/packages/pinball_flame/.gitignore new file mode 100644 index 00000000..d6130351 --- /dev/null +++ b/packages/pinball_flame/.gitignore @@ -0,0 +1,39 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# VSCode related +.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/packages/pinball_flame/README.md b/packages/pinball_flame/README.md new file mode 100644 index 00000000..ecf0f4d4 --- /dev/null +++ b/packages/pinball_flame/README.md @@ -0,0 +1,11 @@ +# pinball_flame + +[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] +[![License: MIT][license_badge]][license_link] + +Set of out-of-the-way solutions for common Pinball game problems. + +[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg +[license_link]: https://opensource.org/licenses/MIT +[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg +[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis \ No newline at end of file diff --git a/packages/pinball_flame/analysis_options.yaml b/packages/pinball_flame/analysis_options.yaml new file mode 100644 index 00000000..3742fc3d --- /dev/null +++ b/packages/pinball_flame/analysis_options.yaml @@ -0,0 +1 @@ +include: package:very_good_analysis/analysis_options.2.4.0.yaml \ No newline at end of file diff --git a/packages/pinball_flame/lib/pinball_flame.dart b/packages/pinball_flame/lib/pinball_flame.dart new file mode 100644 index 00000000..2d2e760b --- /dev/null +++ b/packages/pinball_flame/lib/pinball_flame.dart @@ -0,0 +1,5 @@ +library pinball_flame; + +export 'src/blueprint.dart'; +export 'src/component_controller.dart'; +export 'src/keyboard_input_controller.dart'; diff --git a/packages/pinball_components/lib/src/flame/blueprint.dart b/packages/pinball_flame/lib/src/blueprint.dart similarity index 100% rename from packages/pinball_components/lib/src/flame/blueprint.dart rename to packages/pinball_flame/lib/src/blueprint.dart diff --git a/lib/flame/component_controller.dart b/packages/pinball_flame/lib/src/component_controller.dart similarity index 91% rename from lib/flame/component_controller.dart rename to packages/pinball_flame/lib/src/component_controller.dart index b9568348..6afc1c40 100644 --- a/lib/flame/component_controller.dart +++ b/packages/pinball_flame/lib/src/component_controller.dart @@ -1,12 +1,9 @@ import 'package:flame/components.dart'; -import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/foundation.dart'; /// {@template component_controller} /// A [ComponentController] is a [Component] in charge of handling the logic /// associated with another [Component]. -/// -/// [ComponentController]s usually implement [BlocComponent]. /// {@endtemplate} abstract class ComponentController extends Component { /// {@macro component_controller} diff --git a/packages/pinball_components/lib/src/flame/keyboard_input_controller.dart b/packages/pinball_flame/lib/src/keyboard_input_controller.dart similarity index 100% rename from packages/pinball_components/lib/src/flame/keyboard_input_controller.dart rename to packages/pinball_flame/lib/src/keyboard_input_controller.dart diff --git a/packages/pinball_flame/pubspec.yaml b/packages/pinball_flame/pubspec.yaml new file mode 100644 index 00000000..ad8ec131 --- /dev/null +++ b/packages/pinball_flame/pubspec.yaml @@ -0,0 +1,20 @@ +name: pinball_flame +description: Set of out-of-the-way solutions for common Pinball game problems. +version: 1.0.0+1 +publish_to: none + +environment: + sdk: ">=2.16.0 <3.0.0" + +dependencies: + flame: ^1.1.1 + flame_forge2d: ^0.11.0 + flutter: + sdk: flutter + +dev_dependencies: + flame_test: ^1.3.0 + flutter_test: + sdk: flutter + mocktail: ^0.3.0 + very_good_analysis: ^2.4.0 diff --git a/packages/pinball_flame/test/helpers/helpers.dart b/packages/pinball_flame/test/helpers/helpers.dart new file mode 100644 index 00000000..efe914f6 --- /dev/null +++ b/packages/pinball_flame/test/helpers/helpers.dart @@ -0,0 +1 @@ +export 'mocks.dart'; diff --git a/packages/pinball_flame/test/helpers/mocks.dart b/packages/pinball_flame/test/helpers/mocks.dart new file mode 100644 index 00000000..bf96390d --- /dev/null +++ b/packages/pinball_flame/test/helpers/mocks.dart @@ -0,0 +1,10 @@ +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:mocktail/mocktail.dart'; + +class MockForge2DGame extends Mock implements Forge2DGame {} + +class MockContactCallback extends Mock + implements ContactCallback {} + +class MockComponent extends Mock implements Component {} diff --git a/packages/pinball_components/test/src/flame/blueprint_test.dart b/packages/pinball_flame/test/src/blueprint_test.dart similarity index 82% rename from packages/pinball_components/test/src/flame/blueprint_test.dart rename to packages/pinball_flame/test/src/blueprint_test.dart index a9629422..f1c58dc9 100644 --- a/packages/pinball_components/test/src/flame/blueprint_test.dart +++ b/packages/pinball_flame/test/src/blueprint_test.dart @@ -1,9 +1,12 @@ import 'package:flame/components.dart'; +import 'package:flame_forge2d/contact_callbacks.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; -import '../../helpers/helpers.dart'; +import '../helpers/helpers.dart'; + +class TestContactCallback extends ContactCallback {} class MyBlueprint extends Blueprint { @override @@ -51,19 +54,19 @@ void main() { }); test('components can be added to it', () { - final blueprint = MyBlueprint()..build(MockGame()); + final blueprint = MyBlueprint()..build(MockForge2DGame()); expect(blueprint.components.length, equals(3)); }); test('blueprints can be added to it', () { - final blueprint = MyComposedBlueprint()..build(MockGame()); + final blueprint = MyComposedBlueprint()..build(MockForge2DGame()); expect(blueprint.blueprints.length, equals(3)); }); test('adds the components to a game on attach', () { - final mockGame = MockGame(); + final mockGame = MockForge2DGame(); when(() => mockGame.addAll(any())).thenAnswer((_) async {}); MyBlueprint().attach(mockGame); @@ -71,7 +74,7 @@ void main() { }); test('adds components from a child Blueprint the to a game on attach', () { - final mockGame = MockGame(); + final mockGame = MockForge2DGame(); when(() => mockGame.addAll(any())).thenAnswer((_) async {}); MyComposedBlueprint().attach(mockGame); @@ -81,7 +84,7 @@ void main() { test( 'throws assertion error when adding to an already attached blueprint', () async { - final mockGame = MockGame(); + final mockGame = MockForge2DGame(); when(() => mockGame.addAll(any())).thenAnswer((_) async {}); final blueprint = MyBlueprint(); await blueprint.attach(mockGame); @@ -94,17 +97,17 @@ void main() { group('Forge2DBlueprint', () { setUpAll(() { - registerFallbackValue(SpaceshipHoleBallContactCallback()); + registerFallbackValue(TestContactCallback()); }); test('callbacks can be added to it', () { - final blueprint = MyForge2dBlueprint()..build(MockGame()); + final blueprint = MyForge2dBlueprint()..build(MockForge2DGame()); expect(blueprint.callbacks.length, equals(3)); }); test('adds the callbacks to a game on attach', () async { - final mockGame = MockGame(); + final mockGame = MockForge2DGame(); when(() => mockGame.addAll(any())).thenAnswer((_) async {}); when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {}); await MyForge2dBlueprint().attach(mockGame); @@ -115,7 +118,7 @@ void main() { test( 'throws assertion error when adding to an already attached blueprint', () async { - final mockGame = MockGame(); + final mockGame = MockForge2DGame(); when(() => mockGame.addAll(any())).thenAnswer((_) async {}); when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {}); final blueprint = MyForge2dBlueprint(); diff --git a/test/flame/component_controller_test.dart b/packages/pinball_flame/test/src/component_controller_test.dart similarity index 97% rename from test/flame/component_controller_test.dart rename to packages/pinball_flame/test/src/component_controller_test.dart index e1973274..0e08be92 100644 --- a/test/flame/component_controller_test.dart +++ b/packages/pinball_flame/test/src/component_controller_test.dart @@ -4,7 +4,7 @@ import 'package:flame/game.dart'; import 'package:flame/src/components/component.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/flame/flame.dart'; +import 'package:pinball_flame/pinball_flame.dart'; class TestComponentController extends ComponentController { TestComponentController(Component component) : super(component); diff --git a/packages/pinball_components/test/src/flame/keyboard_input_controller_test.dart b/packages/pinball_flame/test/src/keyboard_input_controller_test.dart similarity index 96% rename from packages/pinball_components/test/src/flame/keyboard_input_controller_test.dart rename to packages/pinball_flame/test/src/keyboard_input_controller_test.dart index 991f1143..99a0006b 100644 --- a/packages/pinball_components/test/src/flame/keyboard_input_controller_test.dart +++ b/packages/pinball_flame/test/src/keyboard_input_controller_test.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; abstract class _KeyCallStub { bool onCall(); diff --git a/pubspec.lock b/pubspec.lock index 08188ef7..1a502f37 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -483,6 +483,13 @@ packages: relative: true source: path version: "1.0.0+1" + pinball_flame: + dependency: "direct main" + description: + path: "packages/pinball_flame" + relative: true + source: path + version: "1.0.0+1" pinball_theme: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6141415c..f7676247 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,8 @@ dependencies: path: packages/pinball_audio pinball_components: path: packages/pinball_components + pinball_flame: + path: packages/pinball_flame pinball_theme: path: packages/pinball_theme diff --git a/test/game/bloc/game_bloc_test.dart b/test/game/bloc/game_bloc_test.dart index fb543814..37e14f73 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -21,8 +21,6 @@ void main() { const GameState( score: 0, balls: 2, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ), ], @@ -41,15 +39,11 @@ void main() { const GameState( score: 2, balls: 3, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 5, balls: 3, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ), ], @@ -69,158 +63,46 @@ void main() { const GameState( score: 0, balls: 2, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 0, balls: 1, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 0, balls: 0, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ), ], ); }); - group('BonusLetterActivated', () { - blocTest( - 'adds the letter to the state', - build: GameBloc.new, - act: (bloc) => bloc - ..add(const BonusLetterActivated(0)) - ..add(const BonusLetterActivated(1)) - ..add(const BonusLetterActivated(2)), - expect: () => const [ - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [0], - activatedDashNests: {}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [0, 1], - activatedDashNests: {}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [0, 1, 2], - activatedDashNests: {}, - bonusHistory: [], - ), - ], - ); - - blocTest( - 'adds the bonus when the bonusWord is completed', - build: GameBloc.new, - act: (bloc) => bloc - ..add(const BonusLetterActivated(0)) - ..add(const BonusLetterActivated(1)) - ..add(const BonusLetterActivated(2)) - ..add(const BonusLetterActivated(3)) - ..add(const BonusLetterActivated(4)) - ..add(const BonusLetterActivated(5)), - expect: () => const [ - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [0], - activatedDashNests: {}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [0, 1], - activatedDashNests: {}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [0, 1, 2], - activatedDashNests: {}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [0, 1, 2, 3], - activatedDashNests: {}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [0, 1, 2, 3, 4], - activatedDashNests: {}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [], - activatedDashNests: {}, - bonusHistory: [GameBonus.word], - ), - GameState( - score: GameBloc.bonusWordScore, - balls: 3, - activatedBonusLetters: [], - activatedDashNests: {}, - bonusHistory: [GameBonus.word], - ), - ], - ); - }); - - group('DashNestActivated', () { - blocTest( - 'adds the bonus when all nests are activated', - build: GameBloc.new, - act: (bloc) => bloc - ..add(const DashNestActivated('0')) - ..add(const DashNestActivated('1')) - ..add(const DashNestActivated('2')), - expect: () => const [ - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [], - activatedDashNests: {'0'}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 3, - activatedBonusLetters: [], - activatedDashNests: {'0', '1'}, - bonusHistory: [], - ), - GameState( - score: 0, - balls: 4, - activatedBonusLetters: [], - activatedDashNests: {}, - bonusHistory: [GameBonus.dashNest], - ), - ], - ); - }); + group( + 'BonusActivated', + () { + blocTest( + 'adds bonus to history', + build: GameBloc.new, + act: (bloc) => bloc + ..add(const BonusActivated(GameBonus.googleWord)) + ..add(const BonusActivated(GameBonus.dashNest)), + expect: () => const [ + GameState( + score: 0, + balls: 3, + bonusHistory: [GameBonus.googleWord], + ), + GameState( + score: 0, + balls: 3, + bonusHistory: [GameBonus.googleWord, GameBonus.dashNest], + ), + ], + ); + }, + ); group('SparkyTurboChargeActivated', () { blocTest( @@ -231,8 +113,6 @@ void main() { GameState( score: 0, balls: 3, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [GameBonus.sparkyTurboCharge], ), ], diff --git a/test/game/bloc/game_event_test.dart b/test/game/bloc/game_event_test.dart index 68530aae..d7d587bd 100644 --- a/test/game/bloc/game_event_test.dart +++ b/test/game/bloc/game_event_test.dart @@ -41,61 +41,34 @@ void main() { }); }); - group('BonusLetterActivated', () { + group('BonusActivated', () { test('can be instantiated', () { - expect(const BonusLetterActivated(0), isNotNull); + expect(const BonusActivated(GameBonus.dashNest), isNotNull); }); test('supports value equality', () { expect( - BonusLetterActivated(0), - equals(BonusLetterActivated(0)), + BonusActivated(GameBonus.googleWord), + equals(const BonusActivated(GameBonus.googleWord)), ); expect( - BonusLetterActivated(0), - isNot(equals(BonusLetterActivated(1))), + const BonusActivated(GameBonus.googleWord), + isNot(equals(const BonusActivated(GameBonus.dashNest))), ); }); - - test( - 'throws assertion error if index is bigger than the word length', - () { - expect( - () => BonusLetterActivated(8), - throwsAssertionError, - ); - }, - ); }); + }); - group('DashNestActivated', () { - test('can be instantiated', () { - expect(const DashNestActivated('0'), isNotNull); - }); - - test('supports value equality', () { - expect( - DashNestActivated('0'), - equals(DashNestActivated('0')), - ); - expect( - DashNestActivated('0'), - isNot(equals(DashNestActivated('1'))), - ); - }); + group('SparkyTurboChargeActivated', () { + test('can be instantiated', () { + expect(const SparkyTurboChargeActivated(), isNotNull); }); - group('SparkyTurboChargeActivated', () { - test('can be instantiated', () { - expect(const SparkyTurboChargeActivated(), isNotNull); - }); - - test('supports value equality', () { - expect( - SparkyTurboChargeActivated(), - equals(SparkyTurboChargeActivated()), - ); - }); + test('supports value equality', () { + expect( + SparkyTurboChargeActivated(), + equals(SparkyTurboChargeActivated()), + ); }); }); } diff --git a/test/game/bloc/game_state_test.dart b/test/game/bloc/game_state_test.dart index ed80d192..8170346f 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -10,16 +10,12 @@ void main() { GameState( score: 0, balls: 0, - activatedBonusLetters: const [], - activatedDashNests: const {}, bonusHistory: const [], ), equals( const GameState( score: 0, balls: 0, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ), ), @@ -32,8 +28,6 @@ void main() { const GameState( score: 0, balls: 0, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ), isNotNull, @@ -49,8 +43,6 @@ void main() { () => GameState( balls: -1, score: 0, - activatedBonusLetters: const [], - activatedDashNests: const {}, bonusHistory: const [], ), throwsAssertionError, @@ -66,8 +58,6 @@ void main() { () => GameState( balls: 0, score: -1, - activatedBonusLetters: const [], - activatedDashNests: const {}, bonusHistory: const [], ), throwsAssertionError, @@ -82,8 +72,6 @@ void main() { const gameState = GameState( balls: 0, score: 0, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ); expect(gameState.isGameOver, isTrue); @@ -95,44 +83,12 @@ void main() { const gameState = GameState( balls: 1, score: 0, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ); expect(gameState.isGameOver, isFalse); }); }); - group('isLetterActivated', () { - test( - 'is true when the letter is activated', - () { - const gameState = GameState( - balls: 3, - score: 0, - activatedBonusLetters: [1], - activatedDashNests: {}, - bonusHistory: [], - ); - expect(gameState.isLetterActivated(1), isTrue); - }, - ); - - test( - 'is false when the letter is not activated', - () { - const gameState = GameState( - balls: 3, - score: 0, - activatedBonusLetters: [1], - activatedDashNests: {}, - bonusHistory: [], - ); - expect(gameState.isLetterActivated(0), isFalse); - }, - ); - }); - group('copyWith', () { test( 'throws AssertionError ' @@ -141,8 +97,6 @@ void main() { const gameState = GameState( balls: 0, score: 2, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ); expect( @@ -159,8 +113,6 @@ void main() { const gameState = GameState( balls: 0, score: 2, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ); expect( @@ -177,16 +129,12 @@ void main() { const gameState = GameState( score: 2, balls: 0, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ); final otherGameState = GameState( score: gameState.score + 1, balls: gameState.balls + 1, - activatedBonusLetters: const [0], - activatedDashNests: const {'1'}, - bonusHistory: const [GameBonus.word], + bonusHistory: const [GameBonus.googleWord], ); expect(gameState, isNot(equals(otherGameState))); @@ -194,8 +142,6 @@ void main() { gameState.copyWith( score: otherGameState.score, balls: otherGameState.balls, - activatedBonusLetters: otherGameState.activatedBonusLetters, - activatedDashNests: otherGameState.activatedDashNests, bonusHistory: otherGameState.bonusHistory, ), equals(otherGameState), diff --git a/test/game/components/alien_zone_test.dart b/test/game/components/alien_zone_test.dart index 68a2c2f1..863bef31 100644 --- a/test/game/components/alien_zone_test.dart +++ b/test/game/components/alien_zone_test.dart @@ -13,7 +13,7 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); group('AlienZone', () { flameTester.test( @@ -52,7 +52,7 @@ void main() { }); final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, ); diff --git a/test/game/components/board_test.dart b/test/game/components/board_test.dart index 9f2a5260..0a1928ab 100644 --- a/test/game/components/board_test.dart +++ b/test/game/components/board_test.dart @@ -9,7 +9,7 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); group('Board', () { flameTester.test( diff --git a/test/game/components/bonus_word_test.dart b/test/game/components/bonus_word_test.dart deleted file mode 100644 index f01fced9..00000000 --- a/test/game/components/bonus_word_test.dart +++ /dev/null @@ -1,376 +0,0 @@ -// ignore_for_file: cascade_invocations - -import 'package:bloc_test/bloc_test.dart'; -import 'package:flame/effects.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flame_test/flame_test.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:pinball/game/game.dart'; -import 'package:pinball_audio/pinball_audio.dart'; - -import '../../helpers/helpers.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); - - group('BonusWord', () { - flameTester.test( - 'loads the letters correctly', - (game) async { - final bonusWord = BonusWord( - position: Vector2.zero(), - ); - await game.ensureAdd(bonusWord); - - final letters = bonusWord.descendants().whereType(); - expect(letters.length, equals(GameBloc.bonusWord.length)); - }, - ); - - group('listenWhen', () { - final previousState = MockGameState(); - final currentState = MockGameState(); - - test( - 'returns true when there is a new word bonus awarded', - () { - when(() => previousState.bonusHistory).thenReturn([]); - when(() => currentState.bonusHistory).thenReturn([GameBonus.word]); - - expect( - BonusWord(position: Vector2.zero()).listenWhen( - previousState, - currentState, - ), - isTrue, - ); - }, - ); - - test( - 'returns false when there is no new word bonus awarded', - () { - when(() => previousState.bonusHistory).thenReturn([GameBonus.word]); - when(() => currentState.bonusHistory).thenReturn([GameBonus.word]); - - expect( - BonusWord(position: Vector2.zero()).listenWhen( - previousState, - currentState, - ), - isFalse, - ); - }, - ); - }); - - group('onNewState', () { - final state = MockGameState(); - flameTester.test( - 'adds sequence effect to the letters when the player receives a bonus', - (game) async { - when(() => state.bonusHistory).thenReturn([GameBonus.word]); - - final bonusWord = BonusWord(position: Vector2.zero()); - await game.ensureAdd(bonusWord); - await game.ready(); - - bonusWord.onNewState(state); - game.update(0); // Run one frame so the effects are added - - final letters = bonusWord.children.whereType(); - expect(letters.length, equals(GameBloc.bonusWord.length)); - - for (final letter in letters) { - expect( - letter.children.whereType().length, - equals(1), - ); - } - }, - ); - - flameTester.test( - 'plays the google bonus sound', - (game) async { - when(() => state.bonusHistory).thenReturn([GameBonus.word]); - - final bonusWord = BonusWord(position: Vector2.zero()); - await game.ensureAdd(bonusWord); - await game.ready(); - - bonusWord.onNewState(state); - - verify(bonusWord.gameRef.audio.googleBonus).called(1); - }, - ); - - flameTester.test( - 'adds a color effect to reset the color when the sequence is finished', - (game) async { - when(() => state.bonusHistory).thenReturn([GameBonus.word]); - - final bonusWord = BonusWord(position: Vector2.zero()); - await game.ensureAdd(bonusWord); - await game.ready(); - - bonusWord.onNewState(state); - // Run the amount of time necessary for the animation to finish - game.update(3); - game.update(0); // Run one additional frame so the effects are added - - final letters = bonusWord.children.whereType(); - expect(letters.length, equals(GameBloc.bonusWord.length)); - - for (final letter in letters) { - expect( - letter.children.whereType().length, - equals(1), - ); - } - }, - ); - }); - }); - - group('BonusLetter', () { - final flameTester = FlameTester(EmptyPinballGameTest.new); - - flameTester.test( - 'loads correctly', - (game) async { - final bonusLetter = BonusLetter( - letter: 'G', - index: 0, - ); - await game.ensureAdd(bonusLetter); - await game.ready(); - - expect(game.contains(bonusLetter), isTrue); - }, - ); - - group('body', () { - flameTester.test( - 'is static', - (game) async { - final bonusLetter = BonusLetter( - letter: 'G', - index: 0, - ); - await game.ensureAdd(bonusLetter); - - expect(bonusLetter.body.bodyType, equals(BodyType.static)); - }, - ); - }); - - group('fixture', () { - flameTester.test( - 'exists', - (game) async { - final bonusLetter = BonusLetter( - letter: 'G', - index: 0, - ); - await game.ensureAdd(bonusLetter); - - expect(bonusLetter.body.fixtures[0], isA()); - }, - ); - - flameTester.test( - 'is sensor', - (game) async { - final bonusLetter = BonusLetter( - letter: 'G', - index: 0, - ); - await game.ensureAdd(bonusLetter); - - final fixture = bonusLetter.body.fixtures[0]; - expect(fixture.isSensor, isTrue); - }, - ); - - flameTester.test( - 'shape is circular', - (game) async { - final bonusLetter = BonusLetter( - letter: 'G', - index: 0, - ); - await game.ensureAdd(bonusLetter); - - final fixture = bonusLetter.body.fixtures[0]; - expect(fixture.shape.shapeType, equals(ShapeType.circle)); - expect(fixture.shape.radius, equals(1.85)); - }, - ); - }); - - group('bonus letter activation', () { - late GameBloc gameBloc; - late PinballAudio pinballAudio; - - final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, - blocBuilder: () => gameBloc, - repositories: () => [ - RepositoryProvider.value(value: pinballAudio), - ], - ); - - setUp(() { - gameBloc = MockGameBloc(); - whenListen( - gameBloc, - const Stream.empty(), - initialState: const GameState.initial(), - ); - - pinballAudio = MockPinballAudio(); - when(pinballAudio.googleBonus).thenAnswer((_) {}); - }); - - flameBlocTester.testGameWidget( - 'adds BonusLetterActivated to GameBloc when not activated', - setUp: (game, tester) async { - final bonusWord = BonusWord( - position: Vector2.zero(), - ); - await game.ensureAdd(bonusWord); - - final bonusLetters = - game.descendants().whereType().toList(); - for (var index = 0; index < bonusLetters.length; index++) { - final bonusLetter = bonusLetters[index]; - bonusLetter.activate(); - await game.ready(); - - verify(() => gameBloc.add(BonusLetterActivated(index))).called(1); - } - }, - ); - - flameBlocTester.testGameWidget( - "doesn't add BonusLetterActivated to GameBloc when already activated", - setUp: (game, tester) async { - const state = GameState( - score: 0, - balls: 2, - activatedBonusLetters: [0], - activatedDashNests: {}, - bonusHistory: [], - ); - whenListen( - gameBloc, - Stream.value(state), - initialState: state, - ); - - final bonusLetter = BonusLetter(letter: '', index: 0); - await game.add(bonusLetter); - await game.ready(); - - bonusLetter.activate(); - await game.ready(); - }, - verify: (game, tester) async { - verifyNever(() => gameBloc.add(const BonusLetterActivated(0))); - }, - ); - - flameBlocTester.testGameWidget( - 'adds a ColorEffect', - setUp: (game, tester) async { - const state = GameState( - score: 0, - balls: 2, - activatedBonusLetters: [0], - activatedDashNests: {}, - bonusHistory: [], - ); - - final bonusLetter = BonusLetter(letter: '', index: 0); - await game.add(bonusLetter); - await game.ready(); - - bonusLetter.activate(); - - bonusLetter.onNewState(state); - await tester.pump(); - }, - verify: (game, tester) async { - // TODO(aleastiago): Look into making `testGameWidget` pass the - // subject. - final bonusLetter = game.descendants().whereType().last; - expect( - bonusLetter.children.whereType().length, - equals(1), - ); - }, - ); - - flameBlocTester.testGameWidget( - 'listens when there is a change on the letter status', - setUp: (game, tester) async { - final bonusWord = BonusWord( - position: Vector2.zero(), - ); - await game.ensureAdd(bonusWord); - - final bonusLetters = - game.descendants().whereType().toList(); - for (var index = 0; index < bonusLetters.length; index++) { - final bonusLetter = bonusLetters[index]; - bonusLetter.activate(); - await game.ready(); - - final state = GameState( - score: 0, - balls: 2, - activatedBonusLetters: [index], - activatedDashNests: const {}, - bonusHistory: const [], - ); - - expect( - bonusLetter.listenWhen(const GameState.initial(), state), - isTrue, - ); - } - }, - ); - }); - - group('BonusLetterBallContactCallback', () { - test('calls ball.activate', () { - final ball = MockBall(); - final bonusLetter = MockBonusLetter(); - final contactCallback = BonusLetterBallContactCallback(); - - when(() => bonusLetter.isEnabled).thenReturn(true); - - contactCallback.begin(ball, bonusLetter, MockContact()); - - verify(bonusLetter.activate).called(1); - }); - - test("doesn't call ball.activate when letter is disabled", () { - final ball = MockBall(); - final bonusLetter = MockBonusLetter(); - final contactCallback = BonusLetterBallContactCallback(); - - when(() => bonusLetter.isEnabled).thenReturn(false); - - contactCallback.begin(ball, bonusLetter, MockContact()); - - verifyNever(bonusLetter.activate); - }); - }); - }); -} diff --git a/test/game/components/controlled_ball_test.dart b/test/game/components/controlled_ball_test.dart index 41a1cdca..96c67dd4 100644 --- a/test/game/components/controlled_ball_test.dart +++ b/test/game/components/controlled_ball_test.dart @@ -41,7 +41,7 @@ void main() { }); final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, ); diff --git a/test/game/components/controlled_flipper_test.dart b/test/game/components/controlled_flipper_test.dart index a005de30..01982129 100644 --- a/test/game/components/controlled_flipper_test.dart +++ b/test/game/components/controlled_flipper_test.dart @@ -11,18 +11,16 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); - final gameOverBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, + final flameBlocTester = FlameBlocTester( + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () { final bloc = MockGameBloc(); const state = GameState( score: 0, balls: 0, bonusHistory: [], - activatedBonusLetters: [], - activatedDashNests: {}, ); whenListen(bloc, Stream.value(state), initialState: state); return bloc; @@ -66,7 +64,7 @@ void main() { }); testRawKeyDownEvents(leftKeys, (event) { - gameOverBlocTester.testGameWidget( + flameBlocTester.testGameWidget( 'does nothing when is game over', setUp: (game, tester) async { await game.ensureAdd(flipper); @@ -151,7 +149,7 @@ void main() { }); testRawKeyDownEvents(rightKeys, (event) { - gameOverBlocTester.testGameWidget( + flameBlocTester.testGameWidget( 'does nothing when is game over', setUp: (game, tester) async { await game.ensureAdd(flipper); diff --git a/test/game/components/controlled_plunger_test.dart b/test/game/components/controlled_plunger_test.dart index 8a722c9b..eee2bcb0 100644 --- a/test/game/components/controlled_plunger_test.dart +++ b/test/game/components/controlled_plunger_test.dart @@ -12,18 +12,16 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); - final gameOverBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, + final flameBlocTester = FlameBlocTester( + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () { final bloc = MockGameBloc(); const state = GameState( score: 0, balls: 0, bonusHistory: [], - activatedBonusLetters: [], - activatedDashNests: {}, ); whenListen(bloc, Stream.value(state), initialState: state); return bloc; @@ -92,7 +90,7 @@ void main() { }); testRawKeyDownEvents(downKeys, (event) { - gameOverBlocTester.testGameWidget( + flameBlocTester.testGameWidget( 'does nothing when is game over', setUp: (game, tester) async { await game.ensureAdd(plunger); diff --git a/test/game/components/controlled_sparky_computer_test.dart b/test/game/components/controlled_sparky_computer_test.dart index a3e13486..944afca3 100644 --- a/test/game/components/controlled_sparky_computer_test.dart +++ b/test/game/components/controlled_sparky_computer_test.dart @@ -10,7 +10,7 @@ import '../../helpers/helpers.dart'; void main() { group('SparkyComputerController', () { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); late ControlledSparkyComputer controlledSparkyComputer; diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index e9e58985..2089b7b7 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -12,7 +12,7 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); group('FlutterForest', () { flameTester.test( @@ -79,106 +79,21 @@ void main() { ); }); - group('controller', () { - group('listenWhen', () { - final gameBloc = MockGameBloc(); - final flameBlocTester = FlameBlocTester( - gameBuilder: TestGame.new, - blocBuilder: () => gameBloc, - ); - - flameBlocTester.testGameWidget( - 'listens when a Bonus.dashNest and a bonusBall is added', - verify: (game, tester) async { - final flutterForest = FlutterForest(); - - const state = GameState( - score: 0, - balls: 3, - activatedBonusLetters: [], - activatedDashNests: {}, - bonusHistory: [GameBonus.dashNest], - ); - - expect( - flutterForest.controller - .listenWhen(const GameState.initial(), state), - isTrue, - ); - }, - ); - }); - }); - - flameTester.test( - 'onNewState adds a new ball after a duration', - (game) async { - final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); - - final previousBalls = game.descendants().whereType().length; - flutterForest.controller.onNewState(MockGameState()); - - await Future.delayed(const Duration(milliseconds: 700)); - await game.ready(); - - expect( - game.descendants().whereType().length, - greaterThan(previousBalls), - ); - }, - ); - - flameTester.test( - 'onNewState starts Dash animatronic', - (game) async { - final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); - - flutterForest.controller.onNewState(MockGameState()); - final dashAnimatronic = - game.descendants().whereType().single; - - expect(dashAnimatronic.playing, isTrue); - }, - ); - group('bumpers', () { late Ball ball; late GameBloc gameBloc; setUp(() { ball = Ball(baseColor: const Color(0xFF00FFFF)); - gameBloc = MockGameBloc(); - whenListen( - gameBloc, - const Stream.empty(), - initialState: const GameState.initial(), - ); }); final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, - blocBuilder: () => gameBloc, - ); - - flameBlocTester.testGameWidget( - 'add DashNestActivated event', - setUp: (game, tester) async { - final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); - await game.ensureAdd(ball); - - final bumpers = - flutterForest.descendants().whereType(); - - for (final bumper in bumpers) { - beginContact(game, bumper, ball); - final controller = bumper.firstChild()!; - verify( - () => gameBloc.add(DashNestActivated(controller.id)), - ).called(1); - } + gameBuilder: EmptyPinballTestGame.new, + blocBuilder: () { + gameBloc = MockGameBloc(); + const state = GameState.initial(); + whenListen(gameBloc, Stream.value(state), initialState: state); + return gameBloc; }, ); @@ -186,8 +101,10 @@ void main() { 'add Scored event', setUp: (game, tester) async { final flutterForest = FlutterForest(); - await game.ensureAdd(flutterForest); - await game.ensureAdd(ball); + await game.ensureAddAll([ + flutterForest, + ball, + ]); game.addContactCallback(BallScorePointsCallback(game)); final bumpers = flutterForest.descendants().whereType(); @@ -202,122 +119,58 @@ void main() { } }, ); - }); - }); - - group('DashNestBumperController', () { - late DashNestBumper dashNestBumper; - setUp(() { - dashNestBumper = MockDashNestBumper(); - }); - - group( - 'listensWhen', - () { - late GameState previousState; - late GameState newState; - - setUp( - () { - previousState = MockGameState(); - newState = MockGameState(); - }, - ); - - test('listens when the id is added to activatedDashNests', () { - const id = ''; - final controller = DashNestBumperController( - dashNestBumper, - id: id, - ); - - when(() => previousState.activatedDashNests).thenReturn({}); - when(() => newState.activatedDashNests).thenReturn({id}); - - expect(controller.listenWhen(previousState, newState), isTrue); - }); - - test('listens when the id is removed from activatedDashNests', () { - const id = ''; - final controller = DashNestBumperController( - dashNestBumper, - id: id, - ); - - when(() => previousState.activatedDashNests).thenReturn({id}); - when(() => newState.activatedDashNests).thenReturn({}); - - expect(controller.listenWhen(previousState, newState), isTrue); - }); - - test("doesn't listen when the id is never in activatedDashNests", () { - final controller = DashNestBumperController( - dashNestBumper, - id: '', - ); - - when(() => previousState.activatedDashNests).thenReturn({}); - when(() => newState.activatedDashNests).thenReturn({}); - - expect(controller.listenWhen(previousState, newState), isFalse); - }); - - test("doesn't listen when the id still in activatedDashNests", () { - const id = ''; - final controller = DashNestBumperController( - dashNestBumper, - id: id, - ); - - when(() => previousState.activatedDashNests).thenReturn({id}); - when(() => newState.activatedDashNests).thenReturn({id}); - - expect(controller.listenWhen(previousState, newState), isFalse); - }); - }, - ); - - group( - 'onNewState', - () { - late GameState state; - - setUp(() { - state = MockGameState(); - }); - - test( - 'activates the bumper when id in activatedDashNests', - () { - const id = ''; - final controller = DashNestBumperController( - dashNestBumper, - id: id, - ); - - when(() => state.activatedDashNests).thenReturn({id}); - controller.onNewState(state); + flameBlocTester.testGameWidget( + 'adds GameBonus.dashNest to the game when 3 bumpers are activated', + setUp: (game, _) async { + final ball = Ball(baseColor: const Color(0xFFFF0000)); + final flutterForest = FlutterForest(); + await game.ensureAddAll([flutterForest, ball]); - verify(() => dashNestBumper.activate()).called(1); - }, - ); + final bumpers = flutterForest.children.whereType(); + expect(bumpers.length, equals(3)); + for (final bumper in bumpers) { + beginContact(game, bumper, ball); + await game.ready(); + + if (bumper == bumpers.last) { + verify( + () => gameBloc.add(const BonusActivated(GameBonus.dashNest)), + ).called(1); + } else { + verifyNever( + () => gameBloc.add(const BonusActivated(GameBonus.dashNest)), + ); + } + } + }, + ); - test( - 'deactivates the bumper when id not in activatedDashNests', - () { - final controller = DashNestBumperController( - dashNestBumper, - id: '', - ); + flameBlocTester.testGameWidget( + 'deactivates bumpers when 3 are active', + setUp: (game, _) async { + final ball = Ball(baseColor: const Color(0xFFFF0000)); + final flutterForest = FlutterForest(); + await game.ensureAddAll([flutterForest, ball]); - when(() => state.activatedDashNests).thenReturn({}); - controller.onNewState(state); + final bumpers = [ + MockDashNestBumper(), + MockDashNestBumper(), + MockDashNestBumper(), + ]; - verify(() => dashNestBumper.deactivate()).called(1); - }, - ); - }, - ); + for (final bumper in bumpers) { + flutterForest.controller.activateBumper(bumper); + await game.ready(); + + if (bumper == bumpers.last) { + for (final bumper in bumpers) { + verify(bumper.deactivate).called(1); + } + } + } + }, + ); + }); }); } diff --git a/test/game/components/game_flow_controller_test.dart b/test/game/components/game_flow_controller_test.dart index 42ce82d7..b9a6181a 100644 --- a/test/game/components/game_flow_controller_test.dart +++ b/test/game/components/game_flow_controller_test.dart @@ -15,9 +15,7 @@ void main() { final state = GameState( score: 10, balls: 0, - activatedBonusLetters: const [], bonusHistory: const [], - activatedDashNests: const {}, ); final previous = GameState.initial(); @@ -66,9 +64,7 @@ void main() { GameState( score: 10, balls: 0, - activatedBonusLetters: const [], bonusHistory: const [], - activatedDashNests: const {}, ), ); diff --git a/test/game/components/google_word_test.dart b/test/game/components/google_word_test.dart new file mode 100644 index 00000000..fee7bdd0 --- /dev/null +++ b/test/game/components/google_word_test.dart @@ -0,0 +1,73 @@ +// ignore_for_file: cascade_invocations + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockingjay/mockingjay.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('GoogleWord', () { + late GameBloc gameBloc; + + setUp(() { + gameBloc = MockGameBloc(); + whenListen( + gameBloc, + const Stream.empty(), + initialState: const GameState.initial(), + ); + }); + + final flameTester = FlameTester(EmptyPinballTestGame.new); + final flameBlocTester = FlameBlocTester( + gameBuilder: EmptyPinballTestGame.new, + blocBuilder: () => gameBloc, + ); + + flameTester.test( + 'loads the letters correctly', + (game) async { + const word = 'Google'; + final googleWord = GoogleWord(position: Vector2.zero()); + await game.ensureAdd(googleWord); + + final letters = googleWord.children.whereType(); + expect(letters.length, equals(word.length)); + }, + ); + + flameBlocTester.testGameWidget( + 'adds GameBonus.googleWord to the game when all letters are activated', + setUp: (game, _) async { + final ball = Ball(baseColor: const Color(0xFFFF0000)); + final googleWord = GoogleWord(position: Vector2.zero()); + await game.ensureAddAll([googleWord, ball]); + + final letters = googleWord.children.whereType(); + expect(letters, isNotEmpty); + for (final letter in letters) { + beginContact(game, letter, ball); + await game.ready(); + + if (letter == letters.last) { + verify( + () => gameBloc.add(const BonusActivated(GameBonus.googleWord)), + ).called(1); + } else { + verifyNever( + () => gameBloc.add(const BonusActivated(GameBonus.googleWord)), + ); + } + } + }, + ); + }); +} diff --git a/test/game/components/score_effect_controller_test.dart b/test/game/components/score_effect_controller_test.dart index 241f040b..b5c76dc6 100644 --- a/test/game/components/score_effect_controller_test.dart +++ b/test/game/components/score_effect_controller_test.dart @@ -30,9 +30,7 @@ void main() { const current = GameState( score: 10, balls: 3, - activatedBonusLetters: [], bonusHistory: [], - activatedDashNests: {}, ); expect(controller.listenWhen(previous, current), isTrue); }); @@ -44,9 +42,7 @@ void main() { const current = GameState( score: 10, balls: 3, - activatedBonusLetters: [], bonusHistory: [], - activatedDashNests: {}, ); expect(controller.listenWhen(null, current), isTrue); }, @@ -70,9 +66,7 @@ void main() { const state = GameState( score: 10, balls: 3, - activatedBonusLetters: [], bonusHistory: [], - activatedDashNests: {}, ); controller.onNewState(state); @@ -89,9 +83,7 @@ void main() { const GameState( score: 10, balls: 3, - activatedBonusLetters: [], bonusHistory: [], - activatedDashNests: {}, ), ); @@ -99,9 +91,7 @@ void main() { const GameState( score: 14, balls: 3, - activatedBonusLetters: [], bonusHistory: [], - activatedDashNests: {}, ), ); diff --git a/test/game/components/sparky_fire_zone_test.dart b/test/game/components/sparky_fire_zone_test.dart index da8d8404..692af291 100644 --- a/test/game/components/sparky_fire_zone_test.dart +++ b/test/game/components/sparky_fire_zone_test.dart @@ -13,7 +13,7 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); group('SparkyFireZone', () { flameTester.test( @@ -59,7 +59,7 @@ void main() { }); final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, ); diff --git a/test/game/components/wall_test.dart b/test/game/components/wall_test.dart index f8e7483c..63a39991 100644 --- a/test/game/components/wall_test.dart +++ b/test/game/components/wall_test.dart @@ -9,7 +9,7 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); group('Wall', () { flameTester.test( @@ -110,7 +110,7 @@ void main() { }); final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, ); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index ef55b399..c29ee315 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -12,8 +12,8 @@ import '../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(PinballGameTest.new); - final debugModeFlameTester = FlameTester(DebugPinballGameTest.new); + final flameTester = FlameTester(PinballTestGame.new); + final debugModeFlameTester = FlameTester(DebugPinballTestGame.new); group('PinballGame', () { // TODO(alestiago): test if [PinballGame] registers @@ -88,7 +88,7 @@ void main() { }); final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, ); @@ -206,7 +206,7 @@ void main() { final debugModeFlameBlocTester = FlameBlocTester( - gameBuilder: DebugPinballGameTest.new, + gameBuilder: DebugPinballTestGame.new, blocBuilder: () => gameBloc, ); diff --git a/test/game/view/game_hud_test.dart b/test/game/view/game_hud_test.dart index 953b89eb..cdc56832 100644 --- a/test/game/view/game_hud_test.dart +++ b/test/game/view/game_hud_test.dart @@ -12,8 +12,6 @@ void main() { const initialState = GameState( score: 10, balls: 2, - activatedBonusLetters: [], - activatedDashNests: {}, bonusHistory: [], ); diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index 7a1419fb..85f9cfc3 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -11,7 +11,7 @@ import '../../helpers/helpers.dart'; void main() { const theme = PinballTheme(characterTheme: DashTheme()); - final game = PinballGameTest(); + final game = PinballTestGame(); group('PinballGamePage', () { testWidgets('renders PinballGameView', (tester) async { diff --git a/test/helpers/helpers.dart b/test/helpers/helpers.dart index 4b6c29f1..8732035a 100644 --- a/test/helpers/helpers.dart +++ b/test/helpers/helpers.dart @@ -5,11 +5,10 @@ // https://verygood.ventures // license that can be found in the LICENSE file or at export 'builders.dart'; -export 'extensions.dart'; export 'fakes.dart'; export 'forge2d.dart'; export 'key_testers.dart'; export 'mocks.dart'; export 'navigator.dart'; export 'pump_app.dart'; -export 'test_game.dart'; +export 'test_games.dart'; diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index df6728cc..fd01a509 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -31,11 +31,6 @@ class MockContact extends Mock implements Contact {} class MockContactCallback extends Mock implements ContactCallback {} -class MockRampOpening extends Mock implements RampOpening {} - -class MockRampOpeningBallContactCallback extends Mock - implements RampOpeningBallContactCallback {} - class MockGameBloc extends Mock implements GameBloc {} class MockGameState extends Mock implements GameState {} @@ -64,8 +59,6 @@ class MockTapUpInfo extends Mock implements TapUpInfo {} class MockEventPosition extends Mock implements EventPosition {} -class MockBonusLetter extends Mock implements BonusLetter {} - class MockFilter extends Mock implements Filter {} class MockFixture extends Mock implements Fixture {} diff --git a/test/helpers/test_game.dart b/test/helpers/test_game.dart deleted file mode 100644 index 3c6ff42f..00000000 --- a/test/helpers/test_game.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:flame_bloc/flame_bloc.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; - -class TestGame extends Forge2DGame with FlameBloc { - TestGame() { - images.prefix = ''; - } -} diff --git a/test/helpers/extensions.dart b/test/helpers/test_games.dart similarity index 56% rename from test/helpers/extensions.dart rename to test/helpers/test_games.dart index 8e054fe0..3747a231 100644 --- a/test/helpers/extensions.dart +++ b/test/helpers/test_games.dart @@ -1,12 +1,20 @@ // ignore_for_file: must_call_super +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_theme/pinball_theme.dart'; import 'helpers.dart'; -class PinballGameTest extends PinballGame { - PinballGameTest() +class TestGame extends Forge2DGame with FlameBloc { + TestGame() { + images.prefix = ''; + } +} + +class PinballTestGame extends PinballGame { + PinballTestGame() : super( audio: MockPinballAudio(), theme: const PinballTheme( @@ -15,8 +23,8 @@ class PinballGameTest extends PinballGame { ); } -class DebugPinballGameTest extends DebugPinballGame { - DebugPinballGameTest() +class DebugPinballTestGame extends DebugPinballGame { + DebugPinballTestGame() : super( audio: MockPinballAudio(), theme: const PinballTheme( @@ -25,7 +33,7 @@ class DebugPinballGameTest extends DebugPinballGame { ); } -class EmptyPinballGameTest extends PinballGameTest { +class EmptyPinballTestGame extends PinballTestGame { @override Future onLoad() async {} }