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/assets/images/components/background.png b/assets/images/components/background.png index 28cf5cbe..023814e2 100644 Binary files a/assets/images/components/background.png and b/assets/images/components/background.png differ 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..ba604f17 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -11,14 +11,11 @@ class GameBloc extends Bloc { GameBloc() : super(const GameState.initial()) { on(_onBallLost); on(_onScored); - on(_onBonusLetterActivated); + on(_onBonusActivated); on(_onDashNestActivated); on(_onSparkyTurboChargeActivated); } - static const bonusWord = 'GOOGLE'; - static const bonusWordScore = 10000; - void _onBallLost(BallLost event, Emitter emit) { emit(state.copyWith(balls: state.balls - 1)); } @@ -29,29 +26,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 _onBonusActivated(BonusActivated event, Emitter emit) { + emit( + state.copyWith( + bonusHistory: [...state.bonusHistory, event.bonus], + ), + ); } void _onDashNestActivated(DashNestActivated event, Emitter emit) { diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index ee5315ad..392cc50f 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -33,17 +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]; + List get props => [bonus]; } class DashNestActivated extends GameEvent { diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 0d9485e9..aa1144c0 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,7 +22,6 @@ 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"), @@ -32,7 +30,6 @@ class GameState extends Equatable { const GameState.initial() : score = 0, balls = 3, - activatedBonusLetters = const [], activatedDashNests = const {}, bonusHistory = const []; @@ -44,9 +41,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; @@ -57,14 +51,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, }) { @@ -76,8 +65,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,7 +74,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 3d8b75ae..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] @@ -25,9 +25,9 @@ class AlienZone extends Component with HasGameRef { gameRef.addContactCallback(_ControlledAlienBumperBallContactCallback()); final lowerBumper = ControlledAlienBumper.a() - ..initialPosition = Vector2(-32.52, 9.34); + ..initialPosition = Vector2(-32.52, -9.34); final upperBumper = ControlledAlienBumper.b() - ..initialPosition = Vector2(-22.89, 17.43); + ..initialPosition = Vector2(-22.89, -17.43); await addAll([ lowerBumper, diff --git a/lib/game/components/board.dart b/lib/game/components/board.dart index 41581cc3..a9a9451f 100644 --- a/lib/game/components/board.dart +++ b/lib/game/components/board.dart @@ -23,7 +23,7 @@ class Board extends Component { final dino = ChromeDino() ..initialPosition = Vector2( BoardDimensions.bounds.center.dx + 25, - BoardDimensions.bounds.center.dy + 10, + BoardDimensions.bounds.center.dy - 10, ); await addAll([ @@ -77,17 +77,17 @@ class _BottomGroupSide extends Component { final flipper = ControlledFlipper( side: _side, - )..initialPosition = Vector2((11.8 * direction) + centerXAdjustment, -43.6); + )..initialPosition = Vector2((11.8 * direction) + centerXAdjustment, 43.6); final baseboard = Baseboard(side: _side) ..initialPosition = Vector2( (25.58 * direction) + centerXAdjustment, - -28.69, + 28.69, ); final kicker = Kicker( side: _side, )..initialPosition = Vector2( (22.4 * direction) + centerXAdjustment, - -25, + 25, ); await addAll([flipper, baseboard, kicker]); diff --git a/lib/game/components/bonus_word.dart b/lib/game/components/bonus_word.dart deleted file mode 100644 index 3457e84c..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 d4f37bc7..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} @@ -68,7 +68,7 @@ class BallController extends ComponentController await Future.delayed(const Duration(seconds: 1)); component ..resume() - ..boost(Vector2(200, -500)); + ..boost(Vector2(200, 500)); } @override diff --git a/lib/game/components/controlled_flipper.dart b/lib/game/components/controlled_flipper.dart index 9b73b6d3..3c82e719 100644 --- a/lib/game/components/controlled_flipper.dart +++ b/lib/game/components/controlled_flipper.dart @@ -1,7 +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. @@ -19,7 +21,7 @@ class ControlledFlipper extends Flipper with Controls { /// A [ComponentController] that controls a [Flipper]s movement. /// {@endtemplate} class FlipperController extends ComponentController - with KeyboardHandler { + with KeyboardHandler, BlocComponent { /// {@macro flipper_controller} FlipperController(Flipper flipper) : _keys = flipper.side.flipperKeys, @@ -35,6 +37,7 @@ class FlipperController extends ComponentController RawKeyEvent event, Set keysPressed, ) { + if (state?.isGameOver ?? false) return true; if (!_keys.contains(event.logicalKey)) return true; if (event is RawKeyDownEvent) { diff --git a/lib/game/components/controlled_plunger.dart b/lib/game/components/controlled_plunger.dart index 167f129e..d6c622f7 100644 --- a/lib/game/components/controlled_plunger.dart +++ b/lib/game/components/controlled_plunger.dart @@ -1,7 +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. @@ -18,7 +20,7 @@ class ControlledPlunger extends Plunger with Controls { /// A [ComponentController] that controls a [Plunger]s movement. /// {@endtemplate} class PlungerController extends ComponentController - with KeyboardHandler { + with KeyboardHandler, BlocComponent { /// {@macro plunger_controller} PlungerController(Plunger plunger) : super(plunger); @@ -36,6 +38,7 @@ class PlungerController extends ComponentController RawKeyEvent event, Set keysPressed, ) { + if (state?.isGameOver ?? false) return true; if (!_keys.contains(event.logicalKey)) return true; if (event is RawKeyDownEvent) { diff --git a/lib/game/components/controlled_sparky_computer.dart b/lib/game/components/controlled_sparky_computer.dart index 699ebae2..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. @@ -21,7 +21,7 @@ class ControlledSparkyComputer extends SparkyComputer void build(Forge2DGame _) { addContactCallback(SparkyTurboChargeSensorBallContactCallback()); final sparkyTurboChargeSensor = SparkyTurboChargeSensor() - ..initialPosition = Vector2(-13, 49.8); + ..initialPosition = Vector2(-13, -49.8); add(sparkyTurboChargeSensor); super.build(_); } diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart index 76699ec9..29cfa887 100644 --- a/lib/game/components/flutter_forest.dart +++ b/lib/game/components/flutter_forest.dart @@ -4,9 +4,9 @@ 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] @@ -26,17 +26,17 @@ class FlutterForest extends Component with Controls<_FlutterForestController> { @override Future onLoad() async { await super.onLoad(); - final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, 58.3); + final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, -58.3); final bigNest = _ControlledBigDashNestBumper( id: 'big_nest_bumper', - )..initialPosition = Vector2(18.55, 59.35); + )..initialPosition = Vector2(18.55, -59.35); final smallLeftNest = _ControlledSmallDashNestBumper.a( id: 'small_nest_bumper_a', - )..initialPosition = Vector2(8.95, 51.95); + )..initialPosition = Vector2(8.95, -51.95); final smallRightNest = _ControlledSmallDashNestBumper.b( id: 'small_nest_bumper_b', - )..initialPosition = Vector2(23.3, 46.75); + )..initialPosition = Vector2(23.3, -46.75); final dashAnimatronic = DashAnimatronic()..position = Vector2(20, -66); await addAll([ @@ -78,7 +78,7 @@ class _FlutterForestController extends ComponentController await Future.delayed(const Duration(milliseconds: 700)); await gameRef.add( ControlledBall.bonus(theme: gameRef.theme) - ..initialPosition = Vector2(17.2, 52.7), + ..initialPosition = Vector2(17.2, -52.7), ); } } diff --git a/lib/game/components/game_flow_controller.dart b/lib/game/components/game_flow_controller.dart index b0f6f514..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 @@ -28,7 +28,11 @@ class GameFlowController extends ComponentController /// Puts the game on a game over state void gameOver() { - component.firstChild()?.gameOverMode(); + // TODO(erickzanardo): implement score submission and "navigate" to the + // next page + component.firstChild()?.gameOverMode( + score: state?.score ?? 0, + ); component.firstChild()?.focusOnBackboard(); } 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 0b2ad3da..983d8268 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 @@ -15,8 +16,13 @@ class Launcher extends Forge2DBlueprint { @override void build(Forge2DGame gameRef) { +<<<<<<< HEAD plunger = ControlledPlunger(compressionDistance: 20) //12.3 ..initialPosition = Vector2(38.5, -20); +======= + plunger = ControlledPlunger(compressionDistance: 12.3) + ..initialPosition = Vector2(40.1, 38); +>>>>>>> main final _rocket = RocketSpriteComponent()..position = Vector2(43, 62); diff --git a/lib/game/components/score_effect_controller.dart b/lib/game/components/score_effect_controller.dart index 7fafd4b5..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 @@ -37,7 +37,7 @@ class ScoreEffectController extends ComponentController text: newScore.toString(), position: Vector2( _noise(), - _noise() + (-BoardDimensions.bounds.topCenter.dy + 10), + _noise() + (BoardDimensions.bounds.topCenter.dy + 10), ), ), ); diff --git a/lib/game/components/sparky_fire_zone.dart b/lib/game/components/sparky_fire_zone.dart index ee8da614..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] @@ -25,11 +25,11 @@ class SparkyFireZone extends Component with HasGameRef { gameRef.addContactCallback(_ControlledSparkyBumperBallContactCallback()); final lowerLeftBumper = ControlledSparkyBumper.a() - ..initialPosition = Vector2(-23.15, 41.65); + ..initialPosition = Vector2(-23.15, -41.65); final upperLeftBumper = ControlledSparkyBumper.b() - ..initialPosition = Vector2(-21.25, 58.15); + ..initialPosition = Vector2(-21.25, -58.15); final rightBumper = ControlledSparkyBumper.c() - ..initialPosition = Vector2(-3.56, 53.051); + ..initialPosition = Vector2(-3.56, -53.051); await addAll([ lowerLeftBumper, diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index a181404f..e7c7a343 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -15,10 +15,8 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.baseboard.right.keyName), images.load(components.Assets.images.kicker.left.keyName), images.load(components.Assets.images.kicker.right.keyName), - images.load(components.Assets.images.slingshot.leftUpper.keyName), - images.load(components.Assets.images.slingshot.leftLower.keyName), - images.load(components.Assets.images.slingshot.rightUpper.keyName), - images.load(components.Assets.images.slingshot.rightLower.keyName), + images.load(components.Assets.images.slingshot.upper.keyName), + images.load(components.Assets.images.slingshot.lower.keyName), images.load(components.Assets.images.launchRamp.ramp.keyName), images.load( components.Assets.images.launchRamp.foregroundRailing.keyName, @@ -60,6 +58,13 @@ 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 2f135139..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 @@ -41,7 +41,7 @@ class PinballGame extends Forge2DGame unawaited(add(ScoreEffectController(this))); unawaited(add(gameFlowController = GameFlowController(this))); unawaited(add(CameraController(this))); - unawaited(add(Backboard(position: Vector2(0, -88)))); + unawaited(add(Backboard.waiting(position: Vector2(0, -88)))); await _addGameBoundaries(); unawaited(addFromBlueprint(Boundaries())); @@ -59,15 +59,11 @@ class PinballGame extends Forge2DGame unawaited( addFromBlueprint( Spaceship( - position: Vector2(-26.5, 28.5), + position: Vector2(-26.5, -28.5), ), ), ); - unawaited( - addFromBlueprint( - SpaceshipRail(), - ), - ); + unawaited(addFromBlueprint(SpaceshipRail())); controller.attachTo(launcher.plunger); await super.onLoad(); @@ -76,7 +72,6 @@ class PinballGame extends Forge2DGame void _addContactCallbacks() { addContactCallback(BallScorePointsCallback(this)); addContactCallback(BottomWallBallContactCallback()); - addContactCallback(BonusLetterBallContactCallback()); } Future _addGameBoundaries() async { @@ -86,10 +81,10 @@ class PinballGame extends Forge2DGame Future _addBonusWord() async { await add( - BonusWord( + GoogleWord( position: Vector2( - BoardDimensions.bounds.center.dx - 3.07, - BoardDimensions.bounds.center.dy - 2.4, + BoardDimensions.bounds.center.dx - 4.1, + BoardDimensions.bounds.center.dy + 1.8, ), ), ); @@ -127,7 +122,7 @@ class _GameBallsController extends ComponentController theme: gameRef.theme, )..initialPosition = Vector2( _plunger.body.position.x, - _plunger.body.position.y + Ball.size.y, + _plunger.body.position.y - Ball.size.y, ); component.add(ball); } diff --git a/packages/pinball_components/assets/images/backboard/display.png b/packages/pinball_components/assets/images/backboard/display.png new file mode 100644 index 00000000..97dbb50b Binary files /dev/null and b/packages/pinball_components/assets/images/backboard/display.png differ diff --git a/packages/pinball_components/assets/images/slingshot/left_lower.png b/packages/pinball_components/assets/images/slingshot/left_lower.png deleted file mode 100644 index b44b58fb..00000000 Binary files a/packages/pinball_components/assets/images/slingshot/left_lower.png and /dev/null differ diff --git a/packages/pinball_components/assets/images/slingshot/left_upper.png b/packages/pinball_components/assets/images/slingshot/left_upper.png deleted file mode 100644 index c74267ca..00000000 Binary files a/packages/pinball_components/assets/images/slingshot/left_upper.png and /dev/null differ diff --git a/packages/pinball_components/assets/images/slingshot/right_lower.png b/packages/pinball_components/assets/images/slingshot/lower.png similarity index 100% rename from packages/pinball_components/assets/images/slingshot/right_lower.png rename to packages/pinball_components/assets/images/slingshot/lower.png diff --git a/packages/pinball_components/assets/images/slingshot/right_upper.png b/packages/pinball_components/assets/images/slingshot/upper.png similarity index 100% rename from packages/pinball_components/assets/images/slingshot/right_upper.png rename to packages/pinball_components/assets/images/slingshot/upper.png diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 0c895a56..4b1dbc96 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -57,6 +57,10 @@ class $AssetsImagesBackboardGen { /// File path: assets/images/backboard/backboard_scores.png AssetGenImage get backboardScores => const AssetGenImage('assets/images/backboard/backboard_scores.png'); + + /// File path: assets/images/backboard/display.png + AssetGenImage get display => + const AssetGenImage('assets/images/backboard/display.png'); } class $AssetsImagesBaseboardGen { @@ -196,21 +200,13 @@ class $AssetsImagesPlungerGen { class $AssetsImagesSlingshotGen { const $AssetsImagesSlingshotGen(); - /// File path: assets/images/slingshot/left_lower.png - AssetGenImage get leftLower => - const AssetGenImage('assets/images/slingshot/left_lower.png'); - - /// File path: assets/images/slingshot/left_upper.png - AssetGenImage get leftUpper => - const AssetGenImage('assets/images/slingshot/left_upper.png'); - - /// File path: assets/images/slingshot/right_lower.png - AssetGenImage get rightLower => - const AssetGenImage('assets/images/slingshot/right_lower.png'); + /// File path: assets/images/slingshot/lower.png + AssetGenImage get lower => + const AssetGenImage('assets/images/slingshot/lower.png'); - /// File path: assets/images/slingshot/right_upper.png - AssetGenImage get rightUpper => - const AssetGenImage('assets/images/slingshot/right_upper.png'); + /// File path: assets/images/slingshot/upper.png + AssetGenImage get upper => + const AssetGenImage('assets/images/slingshot/upper.png'); } class $AssetsImagesSpaceshipGen { diff --git a/packages/pinball_components/lib/src/components/alien_bumper.dart b/packages/pinball_components/lib/src/components/alien_bumper.dart index 75b0560d..9165cc15 100644 --- a/packages/pinball_components/lib/src/components/alien_bumper.dart +++ b/packages/pinball_components/lib/src/components/alien_bumper.dart @@ -1,5 +1,3 @@ -import 'dart:math' as math; - import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -73,14 +71,15 @@ class AlienBumper extends BodyComponent with InitialPosition { center: Vector2.zero(), majorRadius: _majorRadius, minorRadius: _minorRadius, - )..rotate(15.9 * math.pi / 180); - final fixtureDef = FixtureDef(shape) - ..friction = 0 - ..restitution = 4; - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; + )..rotate(1.29); + 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.dart b/packages/pinball_components/lib/src/components/backboard.dart deleted file mode 100644 index 613cbc05..00000000 --- a/packages/pinball_components/lib/src/components/backboard.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flame/components.dart'; -import 'package:pinball_components/pinball_components.dart'; - -/// {@template backboard} -/// The [Backboard] of the pinball machine. -/// {@endtemplate} -class Backboard extends SpriteComponent with HasGameRef { - /// {@macro backboard} - Backboard({ - required Vector2 position, - }) : super( - // TODO(erickzanardo): remove multiply after - // https://github.com/flame-engine/flame/pull/1506 is merged - position: position..clone().multiply(Vector2(1, -1)), - anchor: Anchor.bottomCenter, - ); - - @override - Future onLoad() async { - await waitingMode(); - } - - /// Puts the Backboard in waiting mode, where the scoreboard is shown. - Future waitingMode() async { - final sprite = await gameRef.loadSprite( - Assets.images.backboard.backboardScores.keyName, - ); - size = sprite.originalSize / 10; - this.sprite = sprite; - } - - /// Puts the Backboard in game over mode, where the score input is shown. - Future gameOverMode() async { - final sprite = await gameRef.loadSprite( - Assets.images.backboard.backboardGameOver.keyName, - ); - size = sprite.originalSize / 10; - this.sprite = sprite; - } -} diff --git a/packages/pinball_components/lib/src/components/backboard/backboard.dart b/packages/pinball_components/lib/src/components/backboard/backboard.dart new file mode 100644 index 00000000..c5c4ac17 --- /dev/null +++ b/packages/pinball_components/lib/src/components/backboard/backboard.dart @@ -0,0 +1,75 @@ +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart'; + +export 'backboard_game_over.dart'; +export 'backboard_letter_prompt.dart'; +export 'backboard_waiting.dart'; + +/// {@template backboard} +/// The [Backboard] of the pinball machine. +/// {@endtemplate} +class Backboard extends PositionComponent with HasGameRef { + /// {@macro backboard} + Backboard({ + required Vector2 position, + }) : super( + position: position, + anchor: Anchor.bottomCenter, + ); + + /// {@macro backboard} + /// + /// Returns a [Backboard] initialized in the waiting mode + factory Backboard.waiting({ + required Vector2 position, + }) { + return Backboard(position: position)..waitingMode(); + } + + /// {@macro backboard} + /// + /// Returns a [Backboard] initialized in the game over mode + factory Backboard.gameOver({ + required Vector2 position, + required int score, + required BackboardOnSubmit onSubmit, + }) { + return Backboard(position: position) + ..gameOverMode( + score: score, + onSubmit: onSubmit, + ); + } + + /// [TextPaint] used on the [Backboard] + static final textPaint = TextPaint( + style: TextStyle( + fontSize: 6, + color: Colors.white, + fontFamily: PinballFonts.pixeloidSans, + ), + ); + + /// Puts the Backboard in waiting mode, where the scoreboard is shown. + Future waitingMode() async { + children.removeWhere((_) => true); + await add(BackboardWaiting()); + } + + /// Puts the Backboard in game over mode, where the score input is shown. + Future gameOverMode({ + required int score, + BackboardOnSubmit? onSubmit, + }) async { + children.removeWhere((_) => true); + await add( + BackboardGameOver( + score: score, + onSubmit: onSubmit, + ), + ); + } +} 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 new file mode 100644 index 00000000..98ae6ae0 --- /dev/null +++ b/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart @@ -0,0 +1,120 @@ +import 'dart:async'; +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] +typedef BackboardOnSubmit = void Function(String); + +/// {@template backboard_game_over} +/// [PositionComponent] that handles the user input on the +/// game over display view. +/// {@endtemplate} +class BackboardGameOver extends PositionComponent with HasGameRef { + /// {@macro backboard_game_over} + BackboardGameOver({ + required int score, + BackboardOnSubmit? onSubmit, + }) : _score = score, + _onSubmit = onSubmit; + + final int _score; + final BackboardOnSubmit? _onSubmit; + + @override + Future onLoad() async { + final backgroundSprite = await gameRef.loadSprite( + Assets.images.backboard.backboardGameOver.keyName, + ); + + unawaited( + add( + SpriteComponent( + sprite: backgroundSprite, + size: backgroundSprite.originalSize / 10, + anchor: Anchor.bottomCenter, + ), + ), + ); + + final displaySprite = await gameRef.loadSprite( + Assets.images.backboard.display.keyName, + ); + + unawaited( + add( + SpriteComponent( + sprite: displaySprite, + size: displaySprite.originalSize / 10, + anchor: Anchor.bottomCenter, + position: Vector2(0, -11.5), + ), + ), + ); + + unawaited( + add( + TextComponent( + text: _score.formatScore(), + position: Vector2(-22, -46.5), + anchor: Anchor.center, + textRenderer: Backboard.textPaint, + ), + ), + ); + + for (var i = 0; i < 3; i++) { + unawaited( + add( + BackboardLetterPrompt( + position: Vector2( + 20 + (6 * i).toDouble(), + -46.5, + ), + hasFocus: i == 0, + ), + ), + ); + } + + unawaited( + add( + KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowLeft: () => _movePrompt(true), + LogicalKeyboardKey.arrowRight: () => _movePrompt(false), + LogicalKeyboardKey.enter: _submit, + }, + ), + ), + ); + } + + /// Returns the current inputed initials + String get initials => children + .whereType() + .map((prompt) => prompt.char) + .join(); + + bool _submit() { + _onSubmit?.call(initials); + return true; + } + + bool _movePrompt(bool left) { + final prompts = children.whereType().toList(); + + final current = prompts.firstWhere((prompt) => prompt.hasFocus) + ..hasFocus = false; + var index = prompts.indexOf(current) + (left ? -1 : 1); + index = min(max(0, index), prompts.length - 1); + + prompts[index].hasFocus = true; + + return false; + } +} 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 new file mode 100644 index 00000000..61d2074d --- /dev/null +++ b/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart @@ -0,0 +1,107 @@ +import 'dart:async'; +import 'dart:math'; + +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 +/// on the [BackboardGameOver] +/// {@endtemplate} +class BackboardLetterPrompt extends PositionComponent { + /// {@macro backboard_letter_prompt} + BackboardLetterPrompt({ + required Vector2 position, + bool hasFocus = false, + }) : _hasFocus = hasFocus, + super( + position: position, + ); + + static const _alphabetCode = 65; + static const _alphabetLength = 25; + var _charIndex = 0; + + bool _hasFocus; + + late RectangleComponent _underscore; + late TextComponent _input; + late TimerComponent _underscoreBlinker; + + @override + Future onLoad() async { + _underscore = RectangleComponent( + size: Vector2( + 4, + 1.2, + ), + anchor: Anchor.center, + position: Vector2(0, 4), + ); + + unawaited(add(_underscore)); + + _input = TextComponent( + text: 'A', + textRenderer: Backboard.textPaint, + anchor: Anchor.center, + ); + unawaited(add(_input)); + + _underscoreBlinker = TimerComponent( + period: 0.6, + repeat: true, + autoStart: _hasFocus, + onTick: () { + _underscore.paint.color = (_underscore.paint.color == Colors.white) + ? Colors.transparent + : Colors.white; + }, + ); + + unawaited(add(_underscoreBlinker)); + + unawaited( + add( + KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowUp: () => _cycle(true), + LogicalKeyboardKey.arrowDown: () => _cycle(false), + }, + ), + ), + ); + } + + /// Returns the current selected character + String get char => String.fromCharCode(_alphabetCode + _charIndex); + + bool _cycle(bool up) { + if (_hasFocus) { + final newCharCode = + min(max(_charIndex + (up ? 1 : -1), 0), _alphabetLength); + _input.text = String.fromCharCode(_alphabetCode + newCharCode); + _charIndex = newCharCode; + + return false; + } + return true; + } + + /// Returns if this prompt has focus on it + bool get hasFocus => _hasFocus; + + /// Updates this prompt focus + set hasFocus(bool hasFocus) { + if (hasFocus) { + _underscoreBlinker.timer.resume(); + } else { + _underscoreBlinker.timer.pause(); + } + _underscore.paint.color = Colors.white; + _hasFocus = hasFocus; + } +} diff --git a/packages/pinball_components/lib/src/components/backboard/backboard_waiting.dart b/packages/pinball_components/lib/src/components/backboard/backboard_waiting.dart new file mode 100644 index 00000000..f7fa84bf --- /dev/null +++ b/packages/pinball_components/lib/src/components/backboard/backboard_waiting.dart @@ -0,0 +1,17 @@ +import 'package:flame/components.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// [PositionComponent] that shows the leaderboard while the player +/// has not started the game yet. +class BackboardWaiting extends SpriteComponent with HasGameRef { + @override + Future onLoad() async { + final sprite = await gameRef.loadSprite( + Assets.images.backboard.backboardScores.keyName, + ); + + this.sprite = sprite; + size = sprite.originalSize / 10; + anchor = Anchor.bottomCenter; + } +} diff --git a/packages/pinball_components/lib/src/components/ball.dart b/packages/pinball_components/lib/src/components/ball.dart index b8c8e231..f0fb2e7c 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math' as math; import 'dart:ui'; import 'package:flame/components.dart'; @@ -63,11 +64,15 @@ class Ball extends BodyComponent @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); } @@ -79,7 +84,7 @@ class Ball extends BodyComponent // TODO(allisonryan0002): prevent motion from contact with other balls. void stop() { body - ..gravityScale = 0 + ..gravityScale = Vector2.zero() ..linearVelocity = Vector2.zero() ..angularVelocity = 0; } @@ -88,7 +93,7 @@ class Ball extends BodyComponent /// /// If previously [stop]ped, the previous ball's velocity is not kept. void resume() { - body.gravityScale = 1; + body.gravityScale = Vector2(0, 1); } @override @@ -99,15 +104,16 @@ class Ball extends BodyComponent final direction = body.linearVelocity.normalized(); final effect = FireEffect( burstPower: _boostTimer, - direction: -direction, - position: Vector2(body.position.x, -body.position.y), + direction: direction, + position: Vector2(body.position.x, body.position.y), priority: priority - 1, ); unawaited(gameRef.add(effect)); } - _rescale(); + _rescaleSize(); + _setPositionalGravity(); } /// Applies a boost on this [Ball]. @@ -116,19 +122,36 @@ class Ball extends BodyComponent _boostTimer = _boostDuration; } - void _rescale() { - final boardHeight = BoardDimensions.size.y; - const maxShrinkAmount = BoardDimensions.perspectiveShrinkFactor; + void _rescaleSize() { + final boardHeight = BoardDimensions.bounds.height; + const maxShrinkValue = BoardDimensions.perspectiveShrinkFactor; - final adjustedYPosition = body.position.y + (boardHeight / 2); + final standardizedYPosition = body.position.y + (boardHeight / 2); - final scaleFactor = ((boardHeight - adjustedYPosition) / - BoardDimensions.shrinkAdjustedHeight) + - maxShrinkAmount; + final scaleFactor = maxShrinkValue + + ((standardizedYPosition / boardHeight) * (1 - maxShrinkValue)); body.fixtures.first.shape.radius = (size.x / 2) * scaleFactor; _spriteComponent.scale = Vector2.all(scaleFactor); } + + void _setPositionalGravity() { + final defaultGravity = gameRef.world.gravity.y; + final maxXDeviationFromCenter = BoardDimensions.bounds.width / 2; + const maxXGravityPercentage = + (1 - BoardDimensions.perspectiveShrinkFactor) / 2; + final xDeviationFromCenter = body.position.x; + + final positionalXForce = ((xDeviationFromCenter / maxXDeviationFromCenter) * + maxXGravityPercentage) * + defaultGravity; + + final positionalYForce = math.sqrt( + math.pow(defaultGravity, 2) - math.pow(positionalXForce, 2), + ); + + body.gravityOverride = Vector2(positionalXForce, positionalYForce); + } } class _BallSpriteComponent extends SpriteComponent with HasGameRef { diff --git a/packages/pinball_components/lib/src/components/baseboard.dart b/packages/pinball_components/lib/src/components/baseboard.dart index 56b7c978..03826d6c 100644 --- a/packages/pinball_components/lib/src/components/baseboard.dart +++ b/packages/pinball_components/lib/src/components/baseboard.dart @@ -19,37 +19,37 @@ class Baseboard extends BodyComponent with InitialPosition { List _createFixtureDefs() { final fixturesDef = []; final direction = _side.direction; - final arcsAngle = -1.11 * direction; - const arcsRotation = math.pi / 2.08; + const arcsAngle = 1.11; + final arcsRotation = (_side.isLeft) ? -2.7 : -1.6; final pegBumperShape = CircleShape()..radius = 0.7; - pegBumperShape.position.setValues(11.11 * direction, 7.15); + pegBumperShape.position.setValues(11.11 * direction, -7.15); final pegBumperFixtureDef = FixtureDef(pegBumperShape); fixturesDef.add(pegBumperFixtureDef); final topCircleShape = CircleShape()..radius = 0.7; - topCircleShape.position.setValues(9.71 * direction, 4.95); + topCircleShape.position.setValues(9.71 * direction, -4.95); final topCircleFixtureDef = FixtureDef(topCircleShape); fixturesDef.add(topCircleFixtureDef); final innerEdgeShape = EdgeShape() ..set( - Vector2(9.01 * direction, 5.35), - Vector2(5.29 * direction, -0.95), + Vector2(9.01 * direction, -5.35), + Vector2(5.29 * direction, 0.95), ); final innerEdgeShapeFixtureDef = FixtureDef(innerEdgeShape); fixturesDef.add(innerEdgeShapeFixtureDef); final outerEdgeShape = EdgeShape() ..set( - Vector2(10.41 * direction, 4.75), - Vector2(3.79 * direction, -5.95), + Vector2(10.41 * direction, -4.75), + Vector2(3.79 * direction, 5.95), ); final outerEdgeShapeFixtureDef = FixtureDef(outerEdgeShape); fixturesDef.add(outerEdgeShapeFixtureDef); final upperArcShape = ArcShape( - center: Vector2(0.09 * direction, 2.15), + center: Vector2(0.09 * direction, -2.15), arcRadius: 6.1, angle: arcsAngle, rotation: arcsRotation, @@ -58,7 +58,7 @@ class Baseboard extends BodyComponent with InitialPosition { fixturesDef.add(upperArcFixtureDef); final lowerArcShape = ArcShape( - center: Vector2(0.09 * direction, -3.35), + center: Vector2(0.09 * direction, 3.35), arcRadius: 4.5, angle: arcsAngle, rotation: arcsRotation, @@ -70,7 +70,7 @@ class Baseboard extends BodyComponent with InitialPosition { ..setAsBox( 6.8, 2, - Vector2(-6.3 * direction, -5.85), + Vector2(-6.3 * direction, 5.85), 0, ); final bottomRectangleFixtureDef = FixtureDef(bottomRectangle); @@ -89,10 +89,10 @@ class Baseboard extends BodyComponent with InitialPosition { @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/board_dimensions.dart b/packages/pinball_components/lib/src/components/board_dimensions.dart index b4db8c3c..3d547996 100644 --- a/packages/pinball_components/lib/src/components/board_dimensions.dart +++ b/packages/pinball_components/lib/src/components/board_dimensions.dart @@ -14,7 +14,7 @@ class BoardDimensions { static final bounds = Rect.fromCenter( center: Offset.zero, width: size.x, - height: -size.y, + height: size.y, ); /// 3D perspective angle of the board in radians. @@ -22,8 +22,4 @@ class BoardDimensions { /// Factor the board shrinks by from the closest point to the farthest. static const perspectiveShrinkFactor = 0.63; - - /// Board height based on the [perspectiveShrinkFactor]. - static final shrinkAdjustedHeight = - (1 / (1 - perspectiveShrinkFactor)) * size.y; } diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart index e5aab5af..6d582d8c 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]. @@ -30,9 +31,9 @@ class _BottomBoundary extends BodyComponent with InitialPosition { final bottomLeftCurve = BezierCurveShape( controlPoints: [ - Vector2(-43.9, -41.8), - Vector2(-35.7, -43), - Vector2(-19.9, -51), + Vector2(-43.9, 41.8), + Vector2(-35.7, 43), + Vector2(-19.9, 51), ], ); final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurve); @@ -40,9 +41,9 @@ class _BottomBoundary extends BodyComponent with InitialPosition { final bottomRightCurve = BezierCurveShape( controlPoints: [ - Vector2(31.8, -44.8), - Vector2(21.95, -47.7), - Vector2(12.3, -52.1), + Vector2(31.8, 44.8), + Vector2(21.95, 47.7), + Vector2(12.3, 52.1), ], ); final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurve); @@ -95,17 +96,17 @@ class _OuterBoundary extends BodyComponent with InitialPosition { final topWall = EdgeShape() ..set( - Vector2(3.6, 70.2), - Vector2(-14.1, 70.2), + Vector2(3.6, -70.2), + Vector2(-14.1, -70.2), ); final topWallFixtureDef = FixtureDef(topWall); fixturesDefs.add(topWallFixtureDef); final topLeftCurve = BezierCurveShape( controlPoints: [ - Vector2(-32.3, 57.2), - Vector2(-31.5, 69.9), - Vector2(-14.1, 70.2), + Vector2(-32.3, -57.2), + Vector2(-31.5, -69.9), + Vector2(-14.1, -70.2), ], ); final topLeftCurveFixtureDef = FixtureDef(topLeftCurve); @@ -113,8 +114,8 @@ class _OuterBoundary extends BodyComponent with InitialPosition { final leftWall = EdgeShape() ..set( - Vector2(-32.3, 57.2), - Vector2(-43.9, -41.8), + Vector2(-32.3, -57.2), + Vector2(-43.9, 41.8), ); final leftWallFixtureDef = FixtureDef(leftWall); fixturesDefs.add(leftWallFixtureDef); diff --git a/packages/pinball_components/lib/src/components/chrome_dino.dart b/packages/pinball_components/lib/src/components/chrome_dino.dart index 327e14f5..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 = 0 - ..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 e719783f..7b4e1ddd 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -1,5 +1,5 @@ export 'alien_bumper.dart'; -export 'backboard.dart'; +export 'backboard/backboard.dart'; export 'ball.dart'; export 'baseboard.dart'; export 'board_dimensions.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 cec5b42a..a16fa376 100644 --- a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart @@ -77,12 +77,12 @@ class BigDashNestBumper extends DashNestBumper { center: Vector2.zero(), majorRadius: 5.1, minorRadius: 3.75, - )..rotate(math.pi / 2.1); + )..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 aec4c7aa..0c287491 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]. @@ -35,8 +36,8 @@ class _DinoTopWall extends BodyComponent with InitialPosition { final topStraightShape = EdgeShape() ..set( - Vector2(29.5, 35.1), - Vector2(28.4, 35.1), + Vector2(28.4, -35.1), + Vector2(29.5, -35.1), ); final topStraightFixtureDef = FixtureDef(topStraightShape); fixturesDef.add(topStraightFixtureDef); @@ -44,8 +45,8 @@ class _DinoTopWall extends BodyComponent with InitialPosition { final topCurveShape = BezierCurveShape( controlPoints: [ topStraightShape.vertex1, - Vector2(17.4, 26.38), - Vector2(25.5, 20.7), + Vector2(17.4, -26.38), + Vector2(25.5, -20.7), ], ); fixturesDef.add(FixtureDef(topCurveShape)); @@ -53,8 +54,8 @@ class _DinoTopWall extends BodyComponent with InitialPosition { final middleCurveShape = BezierCurveShape( controlPoints: [ topCurveShape.vertices.last, - Vector2(27.8, 20.1), - Vector2(26.8, 19.5), + Vector2(27.8, -20.1), + Vector2(26.8, -19.5), ], ); fixturesDef.add(FixtureDef(middleCurveShape)); @@ -62,8 +63,8 @@ class _DinoTopWall extends BodyComponent with InitialPosition { final bottomCurveShape = BezierCurveShape( controlPoints: [ middleCurveShape.vertices.last, - Vector2(21.15, 16), - Vector2(25.6, 15.2), + Vector2(21.15, -16), + Vector2(25.6, -15.2), ], ); fixturesDef.add(FixtureDef(bottomCurveShape)); @@ -71,7 +72,7 @@ class _DinoTopWall extends BodyComponent with InitialPosition { final bottomStraightShape = EdgeShape() ..set( bottomCurveShape.vertices.last, - Vector2(31, 14.5), + Vector2(31, -14.5), ); final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); fixturesDef.add(bottomStraightFixtureDef); @@ -81,10 +82,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( @@ -128,51 +130,65 @@ class _DinoBottomWall extends BodyComponent with InitialPosition { List _createFixtureDefs() { final fixturesDef = []; + const restitution = 1.0; final topStraightControlPoints = [ - Vector2(32.4, 8.3), - Vector2(25, 7.7), + Vector2(32.4, -8.3), + Vector2(25, -7.7), ]; final topStraightShape = EdgeShape() ..set( topStraightControlPoints.first, topStraightControlPoints.last, ); - final topStraightFixtureDef = FixtureDef(topStraightShape); + final topStraightFixtureDef = FixtureDef( + topStraightShape, + restitution: restitution, + ); fixturesDef.add(topStraightFixtureDef); final topLeftCurveControlPoints = [ topStraightControlPoints.last, - Vector2(21.8, 7), - Vector2(29.5, -13.8), + Vector2(21.8, -7), + Vector2(29.5, 13.8), ]; final topLeftCurveShape = BezierCurveShape( controlPoints: topLeftCurveControlPoints, ); - fixturesDef.add(FixtureDef(topLeftCurveShape)); + final topLeftCurveFixtureDef = FixtureDef( + topLeftCurveShape, + restitution: restitution, + ); + fixturesDef.add(topLeftCurveFixtureDef); final bottomLeftStraightControlPoints = [ topLeftCurveControlPoints.last, - Vector2(31.8, -44.1), + Vector2(31.8, 44.1), ]; final bottomLeftStraightShape = EdgeShape() ..set( bottomLeftStraightControlPoints.first, bottomLeftStraightControlPoints.last, ); - final bottomLeftStraightFixtureDef = FixtureDef(bottomLeftStraightShape); + final bottomLeftStraightFixtureDef = FixtureDef( + bottomLeftStraightShape, + restitution: restitution, + ); fixturesDef.add(bottomLeftStraightFixtureDef); final bottomStraightControlPoints = [ bottomLeftStraightControlPoints.last, - Vector2(37.8, -44.1), + Vector2(37.8, 44.1), ]; final bottomStraightShape = EdgeShape() ..set( bottomStraightControlPoints.first, bottomStraightControlPoints.last, ); - final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); + final bottomStraightFixtureDef = FixtureDef( + bottomStraightShape, + restitution: restitution, + ); fixturesDef.add(bottomStraightFixtureDef); return fixturesDef; @@ -180,19 +196,13 @@ 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; } diff --git a/packages/pinball_components/lib/src/components/fire_effect.dart b/packages/pinball_components/lib/src/components/fire_effect.dart index cf8c3707..14639527 100644 --- a/packages/pinball_components/lib/src/components/fire_effect.dart +++ b/packages/pinball_components/lib/src/components/fire_effect.dart @@ -73,7 +73,7 @@ class FireEffect extends ParticleSystemComponent { spreadTween.transform(random.nextDouble()), spreadTween.transform(random.nextDouble()), ); - final finalDirection = Vector2(direction.x, -direction.y) + spread; + final finalDirection = Vector2(direction.x, direction.y) + spread; final speed = finalDirection * (burstPower * 20); return AcceleratedParticle( diff --git a/packages/pinball_components/lib/src/components/flipper.dart b/packages/pinball_components/lib/src/components/flipper.dart index c9580510..f825070a 100644 --- a/packages/pinball_components/lib/src/components/flipper.dart +++ b/packages/pinball_components/lib/src/components/flipper.dart @@ -32,13 +32,13 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { /// Applies downward linear velocity to the [Flipper], moving it to its /// resting position. void moveDown() { - body.linearVelocity = Vector2(0, -_speed); + body.linearVelocity = Vector2(0, _speed); } /// Applies upward linear velocity to the [Flipper], moving it to its highest /// position. void moveUp() { - body.linearVelocity = Vector2(0, _speed); + body.linearVelocity = Vector2(0, -_speed); } /// Anchors the [Flipper] to the [RevoluteJoint] that controls its arc motion. @@ -99,9 +99,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; @@ -118,10 +120,12 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { @override Body createBody() { - final bodyDef = BodyDef() - ..position = initialPosition - ..gravityScale = 0 - ..type = BodyType.dynamic; + final bodyDef = BodyDef( + position: initialPosition, + gravityScale: Vector2.zero(), + type: BodyType.dynamic, + ); + final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -161,7 +165,7 @@ class _FlipperAnchor extends JointAnchor { initialPosition = Vector2( (Flipper.size.x * flipper.side.direction) / 2 - (1.65 * flipper.side.direction), - 0.15, + -0.15, ); } } @@ -209,8 +213,8 @@ class _FlipperJoint extends RevoluteJoint { void lock() { const angle = _halfSweepingAngle; setLimits( - -angle * side.direction, - -angle * side.direction, + angle * side.direction, + angle * side.direction, ); } 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..026c68b9 100644 --- a/packages/pinball_components/lib/src/components/flutter_sign_post.dart +++ b/packages/pinball_components/lib/src/components/flutter_sign_post.dart @@ -18,7 +18,9 @@ class FlutterSignPost extends BodyComponent with InitialPosition { 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 de009595..a932b441 100644 --- a/packages/pinball_components/lib/src/components/kicker.dart +++ b/packages/pinball_components/lib/src/components/kicker.dart @@ -34,16 +34,16 @@ class Kicker extends BodyComponent with InitialPosition { const quarterPi = math.pi / 4; final upperCircle = CircleShape()..radius = 1.6; - upperCircle.position.setValues(0, -upperCircle.radius / 2); - final upperCircleFixtureDef = FixtureDef(upperCircle)..friction = 0; + upperCircle.position.setValues(0, upperCircle.radius / 2); + final upperCircleFixtureDef = FixtureDef(upperCircle); fixturesDefs.add(upperCircleFixtureDef); final lowerCircle = CircleShape()..radius = 1.6; lowerCircle.position.setValues( size.x * -direction, - -size.y - 0.8, + size.y + 0.8, ); - final lowerCircleFixtureDef = FixtureDef(lowerCircle)..friction = 0; + final lowerCircleFixtureDef = FixtureDef(lowerCircle); fixturesDefs.add(lowerCircleFixtureDef); final wallFacingEdge = EdgeShape() @@ -53,9 +53,9 @@ class Kicker extends BodyComponent with InitialPosition { upperCircle.radius * direction, 0, ), - Vector2(2.5 * direction, -size.y + 2), + Vector2(2.5 * direction, size.y - 2), ); - final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge)..friction = 0; + final wallFacingLineFixtureDef = FixtureDef(wallFacingEdge); fixturesDefs.add(wallFacingLineFixtureDef); final bottomEdge = EdgeShape() @@ -64,10 +64,10 @@ class Kicker extends BodyComponent with InitialPosition { lowerCircle.position + Vector2( lowerCircle.radius * math.cos(quarterPi) * direction, - -lowerCircle.radius * math.sin(quarterPi), + lowerCircle.radius * math.sin(quarterPi), ), ); - final bottomLineFixtureDef = FixtureDef(bottomEdge)..friction = 0; + final bottomLineFixtureDef = FixtureDef(bottomEdge); fixturesDefs.add(bottomLineFixtureDef); final bouncyEdge = EdgeShape() @@ -75,19 +75,20 @@ class Kicker extends BodyComponent with InitialPosition { upperCircle.position + Vector2( upperCircle.radius * math.cos(quarterPi) * -direction, - upperCircle.radius * math.sin(quarterPi), + -upperCircle.radius * math.sin(quarterPi), ), lowerCircle.position + Vector2( lowerCircle.radius * math.cos(quarterPi) * -direction, - lowerCircle.radius * math.sin(quarterPi), + -lowerCircle.radius * math.sin(quarterPi), ), ); - 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. @@ -97,7 +98,7 @@ class Kicker extends BodyComponent with InitialPosition { lowerCircle.position + Vector2( lowerCircle.radius * math.cos(quarterPi) * -direction, - -lowerCircle.radius * math.sin(quarterPi), + lowerCircle.radius * math.sin(quarterPi), ), wallFacingEdge.vertex2, ], @@ -111,7 +112,9 @@ 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); diff --git a/packages/pinball_components/lib/src/components/launch_ramp.dart b/packages/pinball_components/lib/src/components/launch_ramp.dart index e92e1124..df59b4ce 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 @@ -22,10 +23,10 @@ class LaunchRamp extends Forge2DBlueprint { final launchRampForegroundRailing = _LaunchRampForegroundRailing(); final launchRampExit = _LaunchRampExit(rotation: math.pi / 2) - ..initialPosition = Vector2(0.6, 34); + ..initialPosition = Vector2(0.6, -34); final launchRampCloseWall = _LaunchRampCloseWall() - ..initialPosition = Vector2(4, 66.5); + ..initialPosition = Vector2(4, -69.5); addAll([ launchRampBase, @@ -50,50 +51,50 @@ class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { final rightStraightShape = EdgeShape() ..set( - Vector2(31.4, 61.4), - Vector2(46.5, -68.4), + Vector2(31.4, -61.4), + Vector2(46.5, 68.4), ); final rightStraightFixtureDef = FixtureDef(rightStraightShape); fixturesDef.add(rightStraightFixtureDef); final leftStraightShape = EdgeShape() ..set( - Vector2(27.8, 61.4), - Vector2(41.5, -68.4), + Vector2(27.8, -61.4), + Vector2(41.5, 68.4), ); final leftStraightFixtureDef = FixtureDef(leftStraightShape); fixturesDef.add(leftStraightFixtureDef); final topCurveShape = ArcShape( - center: Vector2(20.5, 61.1), + center: Vector2(20.5, -61.1), arcRadius: 11, angle: 1.6, - rotation: -1.65, + rotation: 0.1, ); final topCurveFixtureDef = FixtureDef(topCurveShape); fixturesDef.add(topCurveFixtureDef); final bottomCurveShape = ArcShape( - center: Vector2(19.3, 60.3), + center: Vector2(19.3, -60.3), arcRadius: 8.5, angle: 1.48, - rotation: -1.58, + rotation: 0.1, ); final bottomCurveFixtureDef = FixtureDef(bottomCurveShape); fixturesDef.add(bottomCurveFixtureDef); final topStraightShape = EdgeShape() ..set( - Vector2(3.7, 70.1), - Vector2(19.1, 72.1), + Vector2(3.7, -70.1), + Vector2(19.1, -72.1), ); final topStraightFixtureDef = FixtureDef(topStraightShape); fixturesDef.add(topStraightFixtureDef); final bottomStraightShape = EdgeShape() ..set( - Vector2(3.7, 66.9), - Vector2(19.1, 68.8), + Vector2(3.7, -66.9), + Vector2(19.1, -68.8), ); final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); fixturesDef.add(bottomStraightFixtureDef); @@ -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); @@ -150,25 +152,25 @@ class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition { final rightStraightShape = EdgeShape() ..set( - Vector2(27.6, 57.9), - Vector2(30, 35.1), + Vector2(27.6, -57.9), + Vector2(30, -35.1), ); final rightStraightFixtureDef = FixtureDef(rightStraightShape); fixturesDef.add(rightStraightFixtureDef); final curveShape = ArcShape( - center: Vector2(20.1, 59.3), + center: Vector2(20.1, -59.3), arcRadius: 7.5, angle: 1.8, - rotation: -1.63, + rotation: -0.13, ); final curveFixtureDef = FixtureDef(curveShape); fixturesDef.add(curveFixtureDef); final topStraightShape = EdgeShape() ..set( - Vector2(3.7, 66.8), - Vector2(19.7, 66.8), + Vector2(3.7, -66.8), + Vector2(19.7, -66.8), ); final topStraightFixtureDef = FixtureDef(topStraightShape); fixturesDef.add(topStraightFixtureDef); @@ -221,7 +223,7 @@ class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final shape = EdgeShape()..set(Vector2.zero(), Vector2(0, 4)); + final shape = EdgeShape()..set(Vector2.zero(), Vector2(0, 3)); final fixtureDef = FixtureDef(shape); diff --git a/packages/pinball_components/lib/src/components/plunger.dart b/packages/pinball_components/lib/src/components/plunger.dart index eb5a945c..f01c007c 100644 --- a/packages/pinball_components/lib/src/components/plunger.dart +++ b/packages/pinball_components/lib/src/components/plunger.dart @@ -33,18 +33,19 @@ class Plunger extends BodyComponent with InitialPosition, Layered { final fixtureDef = FixtureDef(shape)..density = 80; - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this - ..type = BodyType.dynamic - ..gravityScale = 0; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + type: BodyType.dynamic, + gravityScale: Vector2.zero(), + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } /// Set a constant downward velocity on the [Plunger]. void pull() { - body.linearVelocity = Vector2(0, -7); + body.linearVelocity = Vector2(0, 7); } /// Set an upward velocity on the [Plunger]. @@ -107,7 +108,7 @@ class PlungerAnchor extends JointAnchor { }) { initialPosition = Vector2( 0, - -plunger.compressionDistance, + plunger.compressionDistance, ); } } diff --git a/packages/pinball_components/lib/src/components/ramp_opening.dart b/packages/pinball_components/lib/src/components/ramp_opening.dart index a76c0d9e..9a7f4162 100644 --- a/packages/pinball_components/lib/src/components/ramp_opening.dart +++ b/packages/pinball_components/lib/src/components/ramp_opening.dart @@ -2,6 +2,7 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template ramp_orientation} /// Determines if a ramp is facing [up] or [down] on the Board. @@ -65,12 +66,14 @@ abstract class RampOpening extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final fixtureDef = FixtureDef(shape)..isSensor = true; - - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..type = BodyType.static; + final fixtureDef = FixtureDef( + shape, + isSensor: true, + ); + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } @@ -112,9 +115,9 @@ class RampOpeningBallContactCallback // now doesn't work position.y comparison final isBallOutsideOpening = (opening.orientation == RampOrientation.down && - ball.body.linearVelocity.y < 0) || + ball.body.linearVelocity.y > 0) || (opening.orientation == RampOrientation.up && - ball.body.linearVelocity.y > 0); + ball.body.linearVelocity.y < 0); if (isBallOutsideOpening) { ball diff --git a/packages/pinball_components/lib/src/components/slingshot.dart b/packages/pinball_components/lib/src/components/slingshot.dart index 0ebe13ce..a95055cb 100644 --- a/packages/pinball_components/lib/src/components/slingshot.dart +++ b/packages/pinball_components/lib/src/components/slingshot.dart @@ -1,47 +1,32 @@ // ignore_for_file: avoid_renaming_method_parameters -import 'dart:math' as math; - import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template slingshots} -/// A [Blueprint] which creates the left and right pairs of [Slingshot]s. +/// A [Blueprint] which creates the pair of [Slingshot]s on the right side of +/// the board. /// {@endtemplate} class Slingshots extends Forge2DBlueprint { @override void build(_) { - // TODO(allisonryan0002): use radians values instead of converting degrees. - final leftUpperSlingshot = Slingshot( - length: 5.66, - angle: -1.5 * (math.pi / 180), - spritePath: Assets.images.slingshot.leftUpper.keyName, - )..initialPosition = Vector2(-29, 1.5); - - final leftLowerSlingshot = Slingshot( - length: 3.54, - angle: -29.1 * (math.pi / 180), - spritePath: Assets.images.slingshot.leftLower.keyName, - )..initialPosition = Vector2(-31, -6.2); - - final rightUpperSlingshot = Slingshot( + final upperSlingshot = Slingshot( length: 5.64, - angle: 1 * (math.pi / 180), - spritePath: Assets.images.slingshot.rightUpper.keyName, - )..initialPosition = Vector2(22.3, 1.58); + angle: -0.017, + spritePath: Assets.images.slingshot.upper.keyName, + )..initialPosition = Vector2(22.3, -1.58); - final rightLowerSlingshot = Slingshot( + final lowerSlingshot = Slingshot( length: 3.46, - angle: 26.8 * (math.pi / 180), - spritePath: Assets.images.slingshot.rightLower.keyName, - )..initialPosition = Vector2(24.7, -6.2); + angle: -0.468, + spritePath: Assets.images.slingshot.lower.keyName, + )..initialPosition = Vector2(24.7, 6.2); addAll([ - leftUpperSlingshot, - leftLowerSlingshot, - rightUpperSlingshot, - rightLowerSlingshot, + upperSlingshot, + lowerSlingshot, ]); } } @@ -71,13 +56,13 @@ class Slingshot extends BodyComponent with InitialPosition { const circleRadius = 1.55; final topCircleShape = CircleShape()..radius = circleRadius; - topCircleShape.position.setValues(0, _length / 2); - final topCircleFixtureDef = FixtureDef(topCircleShape)..friction = 0; + topCircleShape.position.setValues(0, -_length / 2); + final topCircleFixtureDef = FixtureDef(topCircleShape); fixturesDef.add(topCircleFixtureDef); final bottomCircleShape = CircleShape()..radius = circleRadius; - bottomCircleShape.position.setValues(0, -_length / 2); - final bottomCircleFixtureDef = FixtureDef(bottomCircleShape)..friction = 0; + bottomCircleShape.position.setValues(0, _length / 2); + final bottomCircleFixtureDef = FixtureDef(bottomCircleShape); fixturesDef.add(bottomCircleFixtureDef); final leftEdgeShape = EdgeShape() @@ -85,9 +70,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() @@ -95,9 +82,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; @@ -105,10 +93,11 @@ 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); @@ -131,7 +120,7 @@ class Slingshot extends BodyComponent with InitialPosition { sprite: sprite, size: sprite.originalSize / 10, anchor: Anchor.center, - angle: _angle, + angle: -_angle, ), ); } diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index 2cf37a13..84ee8733 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. @@ -35,8 +36,8 @@ class Spaceship extends Forge2DBlueprint { SpaceshipHole( outsideLayer: Layer.spaceshipExitRail, outsidePriority: Ball.spaceshipRailPriority, - )..initialPosition = position - Vector2(5.2, 4.8), - SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8), + )..initialPosition = position - Vector2(5.2, -4.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); } } @@ -107,10 +108,10 @@ class AndroidHead extends BodyComponent with InitialPosition, Layered { 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( @@ -197,10 +198,10 @@ class SpaceshipHole extends RampOpening { @override Shape get shape { return ArcShape( - center: Vector2(0, 3.2), + center: Vector2(0, -3.2), arcRadius: 5, angle: 1, - rotation: 60 * pi / 180, + rotation: -2, ); } } @@ -246,18 +247,16 @@ class SpaceshipWall extends BodyComponent with InitialPosition, Layered { Body createBody() { renderBody = false; - final wallShape = _SpaceshipWallShape(); + final shape = _SpaceshipWallShape(); + final fixtureDef = FixtureDef(shape); - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition - ..angle = 90 * pi / 172 - ..type = BodyType.static; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + angle: -1.7, + ); - return world.createBody(bodyDef) - ..createFixture( - FixtureDef(wallShape)..restitution = 1, - ); + 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 45c769a1..406cc340 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. @@ -23,9 +24,9 @@ class SpaceshipRail extends Forge2DBlueprint { final railRamp = _SpaceshipRailRamp(); final railEnd = SpaceshipRailExit(); final topBase = _SpaceshipRailBase(radius: 0.55) - ..initialPosition = Vector2(-26.15, 18.65); + ..initialPosition = Vector2(-26.15, -18.65); final bottomBase = _SpaceshipRailBase(radius: 0.8) - ..initialPosition = Vector2(-25.5, -12.9); + ..initialPosition = Vector2(-25.5, 12.9); final railForeground = _SpaceshipRailForeground(); addAll([ @@ -51,19 +52,19 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { final fixturesDefs = []; final topArcShape = ArcShape( - center: Vector2(-35.5, 30.9), + center: Vector2(-35.5, -30.9), arcRadius: 2.5, angle: math.pi, - rotation: 2.9, + rotation: 0.2, ); final topArcFixtureDef = FixtureDef(topArcShape); fixturesDefs.add(topArcFixtureDef); final topLeftCurveShape = BezierCurveShape( controlPoints: [ - Vector2(-37.9, 30.4), - Vector2(-38, 23.9), - Vector2(-30.93, 18.2), + Vector2(-37.9, -30.4), + Vector2(-38, -23.9), + Vector2(-30.93, -18.2), ], ); final topLeftCurveFixtureDef = FixtureDef(topLeftCurveShape); @@ -72,8 +73,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { final middleLeftCurveShape = BezierCurveShape( controlPoints: [ topLeftCurveShape.vertices.last, - Vector2(-22.6, 10.3), - Vector2(-30, 0.2), + Vector2(-22.6, -10.3), + Vector2(-30, -0.2), ], ); final middleLeftCurveFixtureDef = FixtureDef(middleLeftCurveShape); @@ -82,8 +83,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { final bottomLeftCurveShape = BezierCurveShape( controlPoints: [ middleLeftCurveShape.vertices.last, - Vector2(-36, -8.6), - Vector2(-32.04, -18.3), + Vector2(-36, 8.6), + Vector2(-32.04, 18.3), ], ); final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurveShape); @@ -91,8 +92,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { final topRightStraightShape = EdgeShape() ..set( - Vector2(-33, 31.3), - Vector2(-27.2, 21.3), + Vector2(-33, -31.3), + Vector2(-27.2, -21.3), ); final topRightStraightFixtureDef = FixtureDef(topRightStraightShape); fixturesDefs.add(topRightStraightFixtureDef); @@ -100,8 +101,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { final middleRightCurveShape = BezierCurveShape( controlPoints: [ topRightStraightShape.vertex1, - Vector2(-16.5, 11.4), - Vector2(-25.29, -1.7), + Vector2(-16.5, -11.4), + Vector2(-25.29, 1.7), ], ); final middleRightCurveFixtureDef = FixtureDef(middleRightCurveShape); @@ -110,8 +111,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { final bottomRightCurveShape = BezierCurveShape( controlPoints: [ middleRightCurveShape.vertices.last, - Vector2(-29.91, -8.5), - Vector2(-26.8, -15.7), + Vector2(-29.91, 8.5), + Vector2(-26.8, 15.7), ], ); final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurveShape); @@ -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,12 +191,11 @@ 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); } @@ -219,10 +220,10 @@ class SpaceshipRailExit extends RampOpening { @override Shape get shape { return ArcShape( - center: Vector2(-29, -19), + center: Vector2(-29, 19), arcRadius: 2.5, angle: math.pi * 0.4, - rotation: 0.26, + rotation: -1.4, ); } } diff --git a/packages/pinball_components/lib/src/components/spaceship_ramp.dart b/packages/pinball_components/lib/src/components/spaceship_ramp.dart index 1e0125b6..bb2eac59 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]. @@ -25,14 +26,14 @@ class SpaceshipRamp extends Forge2DBlueprint { outsidePriority: 1, rotation: math.pi, ) - ..initialPosition = Vector2(1.7, 19.8) + ..initialPosition = Vector2(1.7, -19.8) ..layer = Layer.opening; final leftOpening = _SpaceshipRampOpening( outsideLayer: Layer.spaceship, outsidePriority: Ball.spaceshipPriority, rotation: math.pi, ) - ..initialPosition = Vector2(-13.7, 18.6) + ..initialPosition = Vector2(-13.7, -18.6) ..layer = Layer.spaceshipEntranceRamp; final spaceshipRamp = _SpaceshipRampBackground(); @@ -43,7 +44,7 @@ class SpaceshipRamp extends Forge2DBlueprint { final spaceshipRampForegroundRailing = _SpaceshipRampForegroundRailing(); - final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, 20); + final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20); addAll([ spaceshipRampBoardOpeningSprite, @@ -70,30 +71,28 @@ class _SpaceshipRampBackground extends BodyComponent final outerLeftCurveShape = BezierCurveShape( controlPoints: [ - Vector2(-30.75, 37.3), - Vector2(-32.5, 71.25), - Vector2(-14.2, 71.25), + Vector2(-30.75, -37.3), + Vector2(-32.5, -71.25), + Vector2(-14.2, -71.25), ], ); - final outerLeftCurveFixtureDef = FixtureDef(outerLeftCurveShape); fixturesDef.add(outerLeftCurveFixtureDef); final outerRightCurveShape = BezierCurveShape( controlPoints: [ outerLeftCurveShape.vertices.last, - Vector2(2.5, 71.9), - Vector2(6.1, 44.9), + Vector2(2.5, -71.9), + Vector2(6.1, -44.9), ], ); - final outerRightCurveFixtureDef = FixtureDef(outerRightCurveShape); fixturesDef.add(outerRightCurveFixtureDef); final boardOpeningEdgeShape = EdgeShape() ..set( outerRightCurveShape.vertices.last, - Vector2(7.3, 41.1), + Vector2(7.3, -41.1), ); final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape); fixturesDef.add(boardOpeningEdgeShapeFixtureDef); @@ -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); @@ -179,9 +179,9 @@ class _SpaceshipRampForegroundRailing extends BodyComponent final innerLeftCurveShape = BezierCurveShape( controlPoints: [ - Vector2(-24.5, 38), - Vector2(-26.3, 64), - Vector2(-13.8, 64.5), + Vector2(-24.5, -38), + Vector2(-26.3, -64), + Vector2(-13.8, -64.5), ], ); @@ -191,8 +191,8 @@ class _SpaceshipRampForegroundRailing extends BodyComponent final innerRightCurveShape = BezierCurveShape( controlPoints: [ innerLeftCurveShape.vertices.last, - Vector2(-2.5, 66.2), - Vector2(0, 44.5), + Vector2(-2.5, -66.2), + Vector2(0, -44.5), ], ); @@ -202,7 +202,7 @@ class _SpaceshipRampForegroundRailing extends BodyComponent final boardOpeningEdgeShape = EdgeShape() ..set( innerRightCurveShape.vertices.last, - Vector2(-0.85, 40.8), + Vector2(-0.85, -40.8), ); final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape); fixturesDef.add(boardOpeningEdgeShapeFixtureDef); @@ -212,9 +212,10 @@ 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); @@ -259,17 +260,17 @@ class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered { controlPoints: [ Vector2(initialPosition.x - baseWidth / 2, initialPosition.y), Vector2(initialPosition.x - baseWidth / 2, initialPosition.y) + - Vector2(2, 5), + Vector2(2, -5), Vector2(initialPosition.x + baseWidth / 2, initialPosition.y) + - Vector2(-2, 5), + Vector2(-2, -5), Vector2(initialPosition.x + baseWidth / 2, initialPosition.y) ], ); final fixtureDef = FixtureDef(baseShape); - - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; + final bodyDef = BodyDef( + position: initialPosition, + userData: this, + ); return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/packages/pinball_components/lib/src/components/sparky_bumper.dart b/packages/pinball_components/lib/src/components/sparky_bumper.dart index c4798624..3022ed38 100644 --- a/packages/pinball_components/lib/src/components/sparky_bumper.dart +++ b/packages/pinball_components/lib/src/components/sparky_bumper.dart @@ -89,11 +89,11 @@ class SparkyBumper extends BodyComponent with InitialPosition { center: Vector2.zero(), majorRadius: _majorRadius, minorRadius: _minorRadius, - )..rotate(math.pi / 1.9); - final fixtureDef = FixtureDef(shape) - ..friction = 0 - ..restitution = 4; - + )..rotate(math.pi / 2.1); + 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 6933a9ca..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 @@ -29,24 +30,24 @@ class _ComputerBase extends BodyComponent with InitialPosition { final leftEdge = EdgeShape() ..set( - Vector2(-14.9, 46), - Vector2(-15.3, 49.6), + Vector2(-14.9, -46), + Vector2(-15.3, -49.6), ); final leftEdgeFixtureDef = FixtureDef(leftEdge); fixturesDef.add(leftEdgeFixtureDef); final topEdge = EdgeShape() ..set( - Vector2(-15.3, 49.6), - Vector2(-10.7, 50.6), + Vector2(-15.3, -49.6), + Vector2(-10.7, -50.6), ); final topEdgeFixtureDef = FixtureDef(topEdge); fixturesDef.add(topEdgeFixtureDef); final rightEdge = EdgeShape() ..set( - Vector2(-10.7, 50.6), - Vector2(-9, 47.2), + Vector2(-10.7, -50.6), + Vector2(-9, -47.2), ); final rightEdgeFixtureDef = FixtureDef(rightEdge); fixturesDef.add(rightEdgeFixtureDef); @@ -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/extensions/extensions.dart b/packages/pinball_components/lib/src/extensions/extensions.dart new file mode 100644 index 00000000..4be86fd3 --- /dev/null +++ b/packages/pinball_components/lib/src/extensions/extensions.dart @@ -0,0 +1 @@ +export 'score.dart'; diff --git a/packages/pinball_components/lib/src/extensions/score.dart b/packages/pinball_components/lib/src/extensions/score.dart new file mode 100644 index 00000000..bd60d27e --- /dev/null +++ b/packages/pinball_components/lib/src/extensions/score.dart @@ -0,0 +1,11 @@ +import 'package:intl/intl.dart'; + +final _numberFormat = NumberFormat('#,###'); + +/// Adds score related extensions to int +extension ScoreX on int { + /// Formats this number as a score value + String formatScore() { + return _numberFormat.format(this); + } +} 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 9af8dba6..00000000 --- a/packages/pinball_components/lib/src/flame/flame.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'blueprint.dart'; -export 'priority.dart'; diff --git a/packages/pinball_components/lib/src/pinball_components.dart b/packages/pinball_components/lib/src/pinball_components.dart index bd8f99de..e50f9875 100644 --- a/packages/pinball_components/lib/src/pinball_components.dart +++ b/packages/pinball_components/lib/src/pinball_components.dart @@ -1,2 +1,2 @@ export 'components/components.dart'; -export 'flame/flame.dart'; +export 'extensions/extensions.dart'; diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 1c54095a..be06949c 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -7,12 +7,15 @@ environment: sdk: ">=2.16.0 <3.0.0" dependencies: - flame: ^1.1.0 - flame_forge2d: ^0.10.0 + flame: ^1.1.1 + flame_forge2d: ^0.11.0 flutter: sdk: flutter geometry: path: ../geometry + intl: ^0.17.0 + pinball_flame: + path: ../pinball_flame dev_dependencies: diff --git a/packages/pinball_components/sandbox/lib/common/games.dart b/packages/pinball_components/sandbox/lib/common/games.dart index 4aae07cb..4fb158bd 100644 --- a/packages/pinball_components/sandbox/lib/common/games.dart +++ b/packages/pinball_components/sandbox/lib/common/games.dart @@ -11,6 +11,9 @@ abstract class BasicGame extends Forge2DGame { } } +abstract class BasicKeyboardGame extends BasicGame + with HasKeyboardHandlerComponents {} + abstract class LineGame extends BasicGame with PanDetector { Vector2? _lineEnd; diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 5cf36b3d..73ae296c 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -32,6 +32,7 @@ void main() { addGoogleWordStories(dashbook); addLaunchRampStories(dashbook); addScoreTextStories(dashbook); + addBackboardStories(dashbook); runApp(dashbook); } diff --git a/packages/pinball_components/sandbox/lib/stories/backboard/game_over.dart b/packages/pinball_components/sandbox/lib/stories/backboard/game_over.dart new file mode 100644 index 00000000..a513276f --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/backboard/game_over.dart @@ -0,0 +1,37 @@ +import 'package:flame/components.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; + +class BackboardGameOverGame extends BasicKeyboardGame { + BackboardGameOverGame(this.score); + + static const info = ''' + Simple example showing the waiting mode of the backboard. + '''; + + final int score; + + @override + Future onLoad() async { + camera + ..followVector2(Vector2.zero()) + ..zoom = 5; + + await add( + Backboard.gameOver( + position: Vector2(0, 20), + score: score, + onSubmit: (initials) { + add( + ScoreText( + text: 'User $initials made $score', + position: Vector2(0, 50), + color: Colors.pink, + ), + ); + }, + ), + ); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/backboard/stories.dart b/packages/pinball_components/sandbox/lib/stories/backboard/stories.dart new file mode 100644 index 00000000..f85e6685 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/backboard/stories.dart @@ -0,0 +1,27 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:flame/game.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/backboard/game_over.dart'; +import 'package:sandbox/stories/backboard/waiting.dart'; + +void addBackboardStories(Dashbook dashbook) { + dashbook.storiesOf('Backboard') + ..add( + 'Waiting mode', + (context) => GameWidget( + game: BackboardWaitingGame(), + ), + codeLink: buildSourceLink('backboard/waiting.dart'), + info: BackboardWaitingGame.info, + ) + ..add( + 'Game over', + (context) => GameWidget( + game: BackboardGameOverGame( + context.numberProperty('score', 9000000000).toInt(), + ), + ), + codeLink: buildSourceLink('backboard/game_over.dart'), + info: BackboardGameOverGame.info, + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/backboard/waiting.dart b/packages/pinball_components/sandbox/lib/stories/backboard/waiting.dart new file mode 100644 index 00000000..71f5c09a --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/backboard/waiting.dart @@ -0,0 +1,19 @@ +import 'package:flame/components.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; + +class BackboardWaitingGame extends BasicGame { + static const info = ''' + Simple example showing the waiting mode of the backboard. + '''; + + @override + Future onLoad() async { + camera + ..followVector2(Vector2.zero()) + ..zoom = 5; + + final backboard = Backboard.waiting(position: Vector2(0, 20)); + await add(backboard); + } +} 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/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index cdcf0825..338ca384 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -1,4 +1,5 @@ export 'alien_zone/stories.dart'; +export 'backboard/stories.dart'; export 'ball/stories.dart'; export 'baseboard/stories.dart'; export 'boundaries/stories.dart'; diff --git a/packages/pinball_components/sandbox/pubspec.lock b/packages/pinball_components/sandbox/pubspec.lock index b0de4903..7f78e365 100644 --- a/packages/pinball_components/sandbox/pubspec.lock +++ b/packages/pinball_components/sandbox/pubspec.lock @@ -91,14 +91,14 @@ packages: name: flame url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" flame_forge2d: dependency: "direct main" description: name: flame_forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.10.0" + version: "0.11.0" flutter: dependency: "direct main" description: flutter @@ -134,7 +134,7 @@ packages: name: forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.10.0" + version: "0.11.0" freezed_annotation: dependency: transitive description: @@ -149,6 +149,13 @@ packages: relative: true source: path version: "1.0.0+1" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" js: dependency: transitive description: @@ -233,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 94c0479b..03a46ee0 100644 --- a/packages/pinball_components/sandbox/pubspec.yaml +++ b/packages/pinball_components/sandbox/pubspec.yaml @@ -8,12 +8,14 @@ environment: dependencies: dashbook: ^0.1.7 - flame: ^1.1.0 - flame_forge2d: ^0.10.0 + flame: ^1.1.1 + flame_forge2d: ^0.11.0 flutter: sdk: flutter pinball_components: path: ../ + pinball_flame: + path: ../../pinball_flame dev_dependencies: flutter_test: diff --git a/packages/pinball_components/test/helpers/test_game.dart b/packages/pinball_components/test/helpers/test_game.dart index a1219868..5bd4b30d 100644 --- a/packages/pinball_components/test/helpers/test_game.dart +++ b/packages/pinball_components/test/helpers/test_game.dart @@ -1,3 +1,4 @@ +import 'package:flame/input.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; class TestGame extends Forge2DGame { @@ -5,3 +6,5 @@ class TestGame extends Forge2DGame { images.prefix = ''; } } + +class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents {} diff --git a/packages/pinball_components/test/src/components/backboard_test.dart b/packages/pinball_components/test/src/components/backboard_test.dart index 2d95cc47..5e868bfc 100644 --- a/packages/pinball_components/test/src/components/backboard_test.dart +++ b/packages/pinball_components/test/src/components/backboard_test.dart @@ -1,7 +1,9 @@ -// ignore_for_file: unawaited_futures +// ignore_for_file: unawaited_futures, cascade_invocations import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -9,7 +11,7 @@ import '../../helpers/helpers.dart'; void main() { group('Backboard', () { - final tester = FlameTester(TestGame.new); + final tester = FlameTester(KeyboardTestGame.new); group('on waitingMode', () { tester.testGameWidget( @@ -17,7 +19,7 @@ void main() { setUp: (game, tester) async { game.camera.zoom = 2; game.camera.followVector2(Vector2.zero()); - await game.ensureAdd(Backboard(position: Vector2(0, 15))); + await game.ensureAdd(Backboard.waiting(position: Vector2(0, 15))); }, verify: (game, tester) async { await expectLater( @@ -34,20 +36,145 @@ void main() { setUp: (game, tester) async { game.camera.zoom = 2; game.camera.followVector2(Vector2.zero()); - final backboard = Backboard(position: Vector2(0, 15)); + final backboard = Backboard.gameOver( + position: Vector2(0, 15), + score: 1000, + onSubmit: (_) {}, + ); + await game.ensureAdd(backboard); + }, + verify: (game, tester) async { + final prompts = + game.descendants().whereType().length; + expect(prompts, equals(3)); + + final score = game.descendants().firstWhere( + (component) => + component is TextComponent && component.text == '1,000', + ); + + expect(score, isNotNull); + }, + ); + + tester.testGameWidget( + 'can change the initials', + setUp: (game, tester) async { + final backboard = Backboard.gameOver( + position: Vector2(0, 15), + score: 1000, + onSubmit: (_) {}, + ); await game.ensureAdd(backboard); - await backboard.gameOverMode(); - await game.ready(); + // Focus is already on the first letter + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); + await tester.pump(); + + // Move to the next an press up again + await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); + await tester.pump(); + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); + await tester.pump(); + + // One more time + await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); + await tester.pump(); + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); + await tester.pump(); + + // Back to the previous and increase one more + await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); + await tester.pump(); + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); await tester.pump(); }, verify: (game, tester) async { - await expectLater( - find.byGame(), - matchesGoldenFile('golden/backboard/game_over.png'), + final backboard = game + .descendants() + .firstWhere((component) => component is BackboardGameOver) + as BackboardGameOver; + + expect(backboard.initials, equals('BCB')); + }, + ); + + String? submitedInitials; + tester.testGameWidget( + 'submits the initials', + setUp: (game, tester) async { + final backboard = Backboard.gameOver( + position: Vector2(0, 15), + score: 1000, + onSubmit: (value) { + submitedInitials = value; + }, ); + await game.ensureAdd(backboard); + + await tester.sendKeyEvent(LogicalKeyboardKey.enter); + await tester.pump(); + }, + verify: (game, tester) async { + expect(submitedInitials, equals('AAA')); }, ); }); }); + + group('BackboardLetterPrompt', () { + final tester = FlameTester(KeyboardTestGame.new); + + tester.testGameWidget( + 'cycles the char up and down when it has focus', + setUp: (game, tester) async { + await game.ensureAdd( + BackboardLetterPrompt(hasFocus: true, position: Vector2.zero()), + ); + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); + await tester.pump(); + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); + await tester.pump(); + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); + await tester.pump(); + await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); + await tester.pump(); + }, + verify: (game, tester) async { + final prompt = game.firstChild(); + expect(prompt?.char, equals('C')); + }, + ); + + tester.testGameWidget( + "does nothing when it doesn't have focus", + setUp: (game, tester) async { + await game.ensureAdd( + BackboardLetterPrompt(position: Vector2.zero()), + ); + await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); + await tester.pump(); + }, + verify: (game, tester) async { + final prompt = game.firstChild(); + expect(prompt?.char, equals('A')); + }, + ); + + tester.testGameWidget( + 'blinks the prompt when it has the focus', + setUp: (game, tester) async { + await game.ensureAdd( + BackboardLetterPrompt(position: Vector2.zero(), hasFocus: true), + ); + }, + verify: (game, tester) async { + final underscore = game.descendants().whereType().first; + expect(underscore.paint.color, Colors.white); + + game.update(2); + expect(underscore.paint.color, Colors.transparent); + }, + ); + }); } diff --git a/packages/pinball_components/test/src/components/ball_test.dart b/packages/pinball_components/test/src/components/ball_test.dart index 4fb8b5ff..a0a73e2b 100644 --- a/packages/pinball_components/test/src/components/ball_test.dart +++ b/packages/pinball_components/test/src/components/ball_test.dart @@ -48,7 +48,7 @@ void main() { final ball = Ball(baseColor: Colors.blue); await game.ensureAdd(ball); - ball.body.gravityScale = 0; + ball.body.gravityScale = Vector2.zero(); ball.body.linearVelocity.setValues(10, 10); game.update(1); expect(ball.body.position, isNot(equals(ball.initialPosition))); @@ -153,7 +153,7 @@ void main() { ball.stop(); ball.resume(); - ball.body.gravityScale = 0; + ball.body.gravityScale = Vector2.zero(); ball.body.linearVelocity.setValues(10, 10); game.update(1); expect(ball.body.position, isNot(equals(ball.initialPosition))); diff --git a/packages/pinball_components/test/src/components/baseboard_test.dart b/packages/pinball_components/test/src/components/baseboard_test.dart index b1ce58e2..82a6475e 100644 --- a/packages/pinball_components/test/src/components/baseboard_test.dart +++ b/packages/pinball_components/test/src/components/baseboard_test.dart @@ -9,11 +9,30 @@ import '../../helpers/helpers.dart'; void main() { group('Baseboard', () { - // TODO(allisonryan0002): Add golden tests. - TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(TestGame.new); + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + final leftBaseboard = Baseboard( + side: BoardSide.left, + )..initialPosition = Vector2(-20, 0); + final rightBaseboard = Baseboard( + side: BoardSide.right, + )..initialPosition = Vector2(20, 0); + + await game.ensureAddAll([leftBaseboard, rightBaseboard]); + game.camera.followVector2(Vector2.zero()); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/baseboard.png'), + ); + }, + ); + flameTester.test( 'loads correctly', (game) async { @@ -57,8 +76,8 @@ void main() { ); await game.ensureAddAll([leftBaseboard, rightBaseboard]); - expect(leftBaseboard.body.angle, isNegative); - expect(rightBaseboard.body.angle, isPositive); + expect(leftBaseboard.body.angle, isPositive); + expect(rightBaseboard.body.angle, isNegative); }, ); }); diff --git a/packages/pinball_components/test/src/components/board_dimensions_test.dart b/packages/pinball_components/test/src/components/board_dimensions_test.dart index afd4a2d8..2529cac1 100644 --- a/packages/pinball_components/test/src/components/board_dimensions_test.dart +++ b/packages/pinball_components/test/src/components/board_dimensions_test.dart @@ -19,9 +19,5 @@ void main() { test('has perspectiveShrinkFactor', () { expect(BoardDimensions.perspectiveShrinkFactor, equals(0.63)); }); - - test('has shrinkAdjustedHeight', () { - expect(BoardDimensions.shrinkAdjustedHeight, isNotNull); - }); }); } diff --git a/packages/pinball_components/test/src/components/boundaries_test.dart b/packages/pinball_components/test/src/components/boundaries_test.dart index 2c6fe1da..7911026d 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'; @@ -15,17 +16,15 @@ void main() { 'render correctly', setUp: (game, tester) async { await game.addFromBlueprint(Boundaries()); - await game.ready(); game.camera.followVector2(Vector2.zero()); game.camera.zoom = 3.9; }, - // TODO(allisonryan0002): enable test when workflows are fixed. - // verify: (game, tester) async { - // await expectLater( - // find.byGame(), - // matchesGoldenFile('golden/boundaries.png'), - // ); - // }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/boundaries.png'), + ); + }, ); }); } diff --git a/packages/pinball_components/test/src/components/dash_animatronic_test.dart b/packages/pinball_components/test/src/components/dash_animatronic_test.dart index b268fee0..e4b31ca6 100644 --- a/packages/pinball_components/test/src/components/dash_animatronic_test.dart +++ b/packages/pinball_components/test/src/components/dash_animatronic_test.dart @@ -1,5 +1,6 @@ // ignore_for_file: cascade_invocations +import 'package:flame/extensions.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -11,6 +12,33 @@ void main() { final flameTester = FlameTester(TestGame.new); group('DashAnimatronic', () { + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.ensureAdd(DashAnimatronic()..playing = true); + game.camera.followVector2(Vector2.zero()); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/dash_animatronic/start.png'), + ); + + game.update(1); + await tester.pump(); + await expectLater( + find.byGame(), + matchesGoldenFile('golden/dash_animatronic/middle.png'), + ); + + game.update(4); + await tester.pump(); + await expectLater( + find.byGame(), + matchesGoldenFile('golden/dash_animatronic/end.png'), + ); + }, + ); flameTester.test( 'loads correctly', (game) async { 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 bb85bc8e..f124f26e 100644 --- a/packages/pinball_components/test/src/components/dino_walls_test.dart +++ b/packages/pinball_components/test/src/components/dino_walls_test.dart @@ -1,8 +1,10 @@ // ignore_for_file: cascade_invocations +import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import '../../helpers/helpers.dart'; @@ -11,6 +13,21 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(TestGame.new); + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.addFromBlueprint(DinoWalls()); + game.camera.followVector2(Vector2.zero()); + game.camera.zoom = 6.5; + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/dino-walls.png'), + ); + }, + ); + flameTester.test( 'loads correctly', (game) async { diff --git a/packages/pinball_components/test/src/components/failures/waiting_isolatedDiff.png b/packages/pinball_components/test/src/components/failures/waiting_isolatedDiff.png deleted file mode 100644 index 0b04f9c4..00000000 Binary files a/packages/pinball_components/test/src/components/failures/waiting_isolatedDiff.png and /dev/null differ diff --git a/packages/pinball_components/test/src/components/failures/waiting_maskedDiff.png b/packages/pinball_components/test/src/components/failures/waiting_maskedDiff.png deleted file mode 100644 index 34105852..00000000 Binary files a/packages/pinball_components/test/src/components/failures/waiting_maskedDiff.png and /dev/null differ diff --git a/packages/pinball_components/test/src/components/failures/waiting_masterImage.png b/packages/pinball_components/test/src/components/failures/waiting_masterImage.png deleted file mode 100644 index 25e24a6b..00000000 Binary files a/packages/pinball_components/test/src/components/failures/waiting_masterImage.png and /dev/null differ diff --git a/packages/pinball_components/test/src/components/failures/waiting_testImage.png b/packages/pinball_components/test/src/components/failures/waiting_testImage.png deleted file mode 100644 index 00164289..00000000 Binary files a/packages/pinball_components/test/src/components/failures/waiting_testImage.png and /dev/null differ diff --git a/packages/pinball_components/test/src/components/flipper_test.dart b/packages/pinball_components/test/src/components/flipper_test.dart index efd4d2b0..f413fade 100644 --- a/packages/pinball_components/test/src/components/flipper_test.dart +++ b/packages/pinball_components/test/src/components/flipper_test.dart @@ -13,9 +13,29 @@ void main() { final flameTester = FlameTester(TestGame.new); group('Flipper', () { - // TODO(alestiago): Add golden tests. // TODO(alestiago): Consider testing always both left and right Flipper. + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + final leftFlipper = Flipper( + side: BoardSide.left, + )..initialPosition = Vector2(-10, 0); + final rightFlipper = Flipper( + side: BoardSide.right, + )..initialPosition = Vector2(10, 0); + + await game.ensureAddAll([leftFlipper, rightFlipper]); + game.camera.followVector2(Vector2.zero()); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/flipper.png'), + ); + }, + ); + flameTester.test( 'loads correctly', (game) async { @@ -55,7 +75,7 @@ void main() { final flipper = Flipper(side: BoardSide.left); await game.ensureAdd(flipper); - expect(flipper.body.gravityScale, isZero); + expect(flipper.body.gravityScale, equals(Vector2.zero())); }, ); @@ -113,7 +133,7 @@ void main() { expect(flipper.body.linearVelocity, equals(Vector2.zero())); flipper.moveDown(); - expect(flipper.body.linearVelocity.y, lessThan(0)); + expect(flipper.body.linearVelocity.y, isPositive); }, ); @@ -126,7 +146,7 @@ void main() { expect(flipper.body.linearVelocity, equals(Vector2.zero())); flipper.moveUp(); - expect(flipper.body.linearVelocity.y, greaterThan(0)); + expect(flipper.body.linearVelocity.y, isNegative); }, ); }); diff --git a/packages/pinball_components/test/src/components/flutter_sign_post_test.dart b/packages/pinball_components/test/src/components/flutter_sign_post_test.dart index 98815af7..0dee4482 100644 --- a/packages/pinball_components/test/src/components/flutter_sign_post_test.dart +++ b/packages/pinball_components/test/src/components/flutter_sign_post_test.dart @@ -1,5 +1,6 @@ // ignore_for_file: cascade_invocations +import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -11,6 +12,20 @@ void main() { final flameTester = FlameTester(TestGame.new); group('FlutterSignPost', () { + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.ensureAdd(FlutterSignPost()); + game.camera.followVector2(Vector2.zero()); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/flutter-sign-post.png'), + ); + }, + ); + flameTester.test( 'loads correctly', (game) async { diff --git a/packages/pinball_components/test/src/components/golden/backboard/game_over.png b/packages/pinball_components/test/src/components/golden/backboard/game_over.png deleted file mode 100644 index 04a8e3ad..00000000 Binary files a/packages/pinball_components/test/src/components/golden/backboard/game_over.png and /dev/null differ diff --git a/packages/pinball_components/test/src/components/golden/baseboard.png b/packages/pinball_components/test/src/components/golden/baseboard.png new file mode 100644 index 00000000..df83fa4c Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/baseboard.png differ diff --git a/packages/pinball_components/test/src/components/golden/dash_animatronic/end.png b/packages/pinball_components/test/src/components/golden/dash_animatronic/end.png new file mode 100644 index 00000000..8c99f674 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/dash_animatronic/end.png differ diff --git a/packages/pinball_components/test/src/components/golden/dash_animatronic/middle.png b/packages/pinball_components/test/src/components/golden/dash_animatronic/middle.png new file mode 100644 index 00000000..b2bf16a3 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/dash_animatronic/middle.png differ diff --git a/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png b/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png new file mode 100644 index 00000000..8c99f674 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png differ diff --git a/packages/pinball_components/test/src/components/golden/dino-walls.png b/packages/pinball_components/test/src/components/golden/dino-walls.png new file mode 100644 index 00000000..80b07157 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/dino-walls.png differ diff --git a/packages/pinball_components/test/src/components/golden/flipper.png b/packages/pinball_components/test/src/components/golden/flipper.png new file mode 100644 index 00000000..79b7ca5b Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/flipper.png differ diff --git a/packages/pinball_components/test/src/components/golden/flutter-sign-post.png b/packages/pinball_components/test/src/components/golden/flutter-sign-post.png new file mode 100644 index 00000000..68388670 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/flutter-sign-post.png differ diff --git a/packages/pinball_components/test/src/components/golden/plunger/plunger.png b/packages/pinball_components/test/src/components/golden/plunger.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/plunger/plunger.png rename to packages/pinball_components/test/src/components/golden/plunger.png diff --git a/packages/pinball_components/test/src/components/golden/plunger/rocket.png b/packages/pinball_components/test/src/components/golden/rocket.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/plunger/rocket.png rename to packages/pinball_components/test/src/components/golden/rocket.png diff --git a/packages/pinball_components/test/src/components/golden/slingshots.png b/packages/pinball_components/test/src/components/golden/slingshots.png index 2e4ada7b..2cbb1f5c 100644 Binary files a/packages/pinball_components/test/src/components/golden/slingshots.png and b/packages/pinball_components/test/src/components/golden/slingshots.png differ diff --git a/packages/pinball_components/test/src/components/golden/spaceship-rail.png b/packages/pinball_components/test/src/components/golden/spaceship-rail.png new file mode 100644 index 00000000..d81f7dba Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/spaceship-rail.png differ diff --git a/packages/pinball_components/test/src/components/golden/spaceship-ramp.png b/packages/pinball_components/test/src/components/golden/spaceship-ramp.png index a6825d00..b88ae491 100644 Binary files a/packages/pinball_components/test/src/components/golden/spaceship-ramp.png and b/packages/pinball_components/test/src/components/golden/spaceship-ramp.png differ diff --git a/packages/pinball_components/test/src/components/golden/spaceship.png b/packages/pinball_components/test/src/components/golden/spaceship.png index da665718..d43db8c7 100644 Binary files a/packages/pinball_components/test/src/components/golden/spaceship.png and b/packages/pinball_components/test/src/components/golden/spaceship.png differ diff --git a/packages/pinball_components/test/src/components/golden/sparky-computer.png b/packages/pinball_components/test/src/components/golden/sparky-computer.png index 2f7ff65b..109f9903 100644 Binary files a/packages/pinball_components/test/src/components/golden/sparky-computer.png and b/packages/pinball_components/test/src/components/golden/sparky-computer.png differ diff --git a/packages/pinball_components/test/src/components/kicker_test.dart b/packages/pinball_components/test/src/components/kicker_test.dart index 55802703..8c48a1fb 100644 --- a/packages/pinball_components/test/src/components/kicker_test.dart +++ b/packages/pinball_components/test/src/components/kicker_test.dart @@ -21,17 +21,15 @@ void main() { side: BoardSide.right, )..initialPosition = Vector2(20, 0); - await game.addAll([leftKicker, rightKicker]); - await game.ready(); + await game.ensureAddAll([leftKicker, rightKicker]); game.camera.followVector2(Vector2.zero()); }, - // TODO(ruimiguel): enable test when workflows are fixed. - //verify: (game, tester) async { - // await expectLater( - // find.byGame(), - // matchesGoldenFile('golden/kickers.png'), - // ); - //}, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/kickers.png'), + ); + }, ); flameTester.test( 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 c7c7b1b2..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'; @@ -15,17 +16,15 @@ void main() { 'renders correctly', setUp: (game, tester) async { await game.addFromBlueprint(LaunchRamp()); - await game.ready(); game.camera.followVector2(Vector2.zero()); game.camera.zoom = 4.1; }, - // TODO(allisonryan0002): enable test when workflows are fixed. - // verify: (game, tester) async { - // await expectLater( - // find.byGame(), - // matchesGoldenFile('golden/launch-ramp.png'), - // ); - // }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/launch-ramp.png'), + ); + }, ); }); } diff --git a/packages/pinball_components/test/src/components/plunger_test.dart b/packages/pinball_components/test/src/components/plunger_test.dart index 5695b0e9..7922f060 100644 --- a/packages/pinball_components/test/src/components/plunger_test.dart +++ b/packages/pinball_components/test/src/components/plunger_test.dart @@ -17,19 +17,15 @@ void main() { flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { - await game.add( - Plunger( - compressionDistance: compressionDistance, - ), - ); - await game.ready(); + await game.ensureAdd(Plunger(compressionDistance: compressionDistance)); + game.camera.followVector2(Vector2.zero()); game.camera.zoom = 4.1; }, verify: (game, tester) async { await expectLater( - find.byGame(), - matchesGoldenFile('golden/plunger/plunger.png'), + find.byGame(), + matchesGoldenFile('golden/plunger.png'), ); }, ); @@ -68,7 +64,7 @@ void main() { ); await game.ensureAdd(plunger); - expect(plunger.body.gravityScale, isZero); + expect(plunger.body.gravityScale, equals(Vector2.zero())); }, ); }); @@ -123,7 +119,7 @@ void main() { await game.ensureAdd(plunger); plunger.pull(); - expect(plunger.body.linearVelocity.y, isNegative); + expect(plunger.body.linearVelocity.y, isPositive); expect(plunger.body.linearVelocity.x, isZero); }, ); @@ -142,10 +138,10 @@ void main() { 'moves upwards when release is called ' 'and plunger is below its starting position', (game) async { await game.ensureAdd(plunger); - plunger.body.setTransform(Vector2(0, -1), 0); + plunger.body.setTransform(Vector2(0, 1), 0); plunger.release(); - expect(plunger.body.linearVelocity.y, isPositive); + expect(plunger.body.linearVelocity.y, isNegative); expect(plunger.body.linearVelocity.x, isZero); }); @@ -179,7 +175,7 @@ void main() { expect( plungerAnchor.body.position.y, - equals(plunger.body.position.y - compressionDistance), + equals(plunger.body.position.y + compressionDistance), ); }, ); @@ -296,7 +292,7 @@ void main() { await tester.pump(const Duration(seconds: 1)); }, verify: (game, tester) async { - expect(plunger.body.position.y > anchor.body.position.y, isTrue); + expect(plunger.body.position.y < anchor.body.position.y, isTrue); }, ); diff --git a/packages/pinball_components/test/src/components/ramp_opening_test.dart b/packages/pinball_components/test/src/components/ramp_opening_test.dart index e79842bd..37b8edb0 100644 --- a/packages/pinball_components/test/src/components/ramp_opening_test.dart +++ b/packages/pinball_components/test/src/components/ramp_opening_test.dart @@ -180,7 +180,7 @@ void main() { when(() => ball.body).thenReturn(body); when(() => ball.priority).thenReturn(1); when(() => body.position).thenReturn(Vector2.zero()); - when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); + when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); when(() => ball.layer).thenReturn(Layer.board); callback.begin(ball, area, MockContact()); @@ -205,7 +205,7 @@ void main() { when(() => ball.body).thenReturn(body); when(() => ball.priority).thenReturn(1); when(() => body.position).thenReturn(Vector2.zero()); - when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); + when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); when(() => ball.layer).thenReturn(Layer.board); callback.begin(ball, area, MockContact()); @@ -230,7 +230,7 @@ void main() { when(() => ball.body).thenReturn(body); when(() => ball.priority).thenReturn(1); when(() => body.position).thenReturn(Vector2.zero()); - when(() => body.linearVelocity).thenReturn(Vector2(0, 1)); + when(() => body.linearVelocity).thenReturn(Vector2(0, -1)); when(() => ball.layer).thenReturn(Layer.board); callback.begin(ball, area, MockContact()); diff --git a/packages/pinball_components/test/src/components/rocket_test.dart b/packages/pinball_components/test/src/components/rocket_test.dart index c6a92561..87cfe515 100644 --- a/packages/pinball_components/test/src/components/rocket_test.dart +++ b/packages/pinball_components/test/src/components/rocket_test.dart @@ -20,7 +20,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/plunger/rocket.png'), + matchesGoldenFile('golden/rocket.png'), ); }, ); diff --git a/packages/pinball_components/test/src/components/slingshot_test.dart b/packages/pinball_components/test/src/components/slingshot_test.dart index 6f015e13..9a0be664 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'; @@ -12,22 +13,20 @@ void main() { final flameTester = FlameTester(TestGame.new); const length = 2.0; const angle = 0.0; - final spritePath = Assets.images.slingshot.leftUpper.keyName; + final spritePath = Assets.images.slingshot.upper.keyName; flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { await game.addFromBlueprint(Slingshots()); - await game.ready(); game.camera.followVector2(Vector2.zero()); }, - // TODO(allisonryan0002): enable test when workflows are fixed. - // verify: (game, tester) async { - // await expectLater( - // find.byGame(), - // matchesGoldenFile('golden/slingshots.png'), - // ); - // }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/slingshots.png'), + ); + }, ); flameTester.test( 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 6c5410b1..c9d49918 100644 --- a/packages/pinball_components/test/src/components/spaceship_rail_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_rail_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'; @@ -13,6 +14,21 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(TestGame.new); + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.addFromBlueprint(SpaceshipRail()); + game.camera.followVector2(Vector2.zero()); + game.camera.zoom = 8; + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/spaceship-rail.png'), + ); + }, + ); + flameTester.test( 'loads correctly', (game) async { 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 7e5f1c46..da97ea68 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'; @@ -15,16 +16,14 @@ void main() { 'renders correctly', setUp: (game, tester) async { await game.addFromBlueprint(SpaceshipRamp()); - await game.ready(); game.camera.followVector2(Vector2(-13, -50)); }, - // TODO(allisonryan0002): enable test when workflows are fixed. - // verify: (game, tester) async { - // await expectLater( - // find.byGame(), - // matchesGoldenFile('golden/spaceship-ramp.png'), - // ); - // }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/spaceship-ramp.png'), + ); + }, ); }); } diff --git a/packages/pinball_components/test/src/components/spaceship_test.dart b/packages/pinball_components/test/src/components/spaceship_test.dart index 4d980c69..d5c6b491 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'; @@ -43,16 +44,15 @@ void main() { tester.testGameWidget( 'renders correctly', setUp: (game, tester) async { - await game.addFromBlueprint(Spaceship(position: Vector2(30, -30))); - await game.ready(); - await tester.pump(); + final position = Vector2(30, -30); + await game.addFromBlueprint(Spaceship(position: position)); + game.camera.followVector2(position); }, verify: (game, tester) async { - // FIXME(erickzanardo): Failing pipeline. - // await expectLater( - // find.byGame(), - // matchesGoldenFile('golden/spaceship.png'), - // ); + await expectLater( + find.byGame(), + matchesGoldenFile('golden/spaceship.png'), + ); }, ); }); 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 7e761b97..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'; @@ -15,16 +16,14 @@ void main() { 'renders correctly', setUp: (game, tester) async { await game.addFromBlueprint(SparkyComputer()); - await game.ready(); game.camera.followVector2(Vector2(-15, -50)); }, - // TODO(allisonryan0002): enable test when workflows are fixed. - // verify: (game, tester) async { - // await expectLater( - // find.byGame(), - // matchesGoldenFile('golden/sparky-computer.png'), - // ); - // }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/sparky-computer.png'), + ); + }, ); }); } 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..e8f8624b --- /dev/null +++ b/packages/pinball_flame/lib/pinball_flame.dart @@ -0,0 +1,6 @@ +library pinball_flame; + +export 'src/blueprint.dart'; +export 'src/component_controller.dart'; +export 'src/keyboard_input_controller.dart'; +export 'src/priority.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_flame/lib/src/keyboard_input_controller.dart b/packages/pinball_flame/lib/src/keyboard_input_controller.dart new file mode 100644 index 00000000..8249e599 --- /dev/null +++ b/packages/pinball_flame/lib/src/keyboard_input_controller.dart @@ -0,0 +1,34 @@ +import 'package:flame/components.dart'; +import 'package:flutter/services.dart'; + +/// The signature for a key handle function +typedef KeyHandlerCallback = bool Function(); + +/// {@template keyboard_input_controller} +/// A [Component] that receives keyboard input and executes registered methods. +/// {@endtemplate} +class KeyboardInputController extends Component with KeyboardHandler { + /// {@macro keyboard_input_controller} + KeyboardInputController({ + Map keyUp = const {}, + Map keyDown = const {}, + }) : _keyUp = keyUp, + _keyDown = keyDown; + + final Map _keyUp; + final Map _keyDown; + + @override + bool onKeyEvent(RawKeyEvent event, Set keysPressed) { + final isUp = event is RawKeyUpEvent; + + final handlers = isUp ? _keyUp : _keyDown; + final handler = handlers[event.logicalKey]; + + if (handler != null) { + return handler(); + } + + return true; + } +} diff --git a/packages/pinball_components/lib/src/flame/priority.dart b/packages/pinball_flame/lib/src/priority.dart similarity index 100% rename from packages/pinball_components/lib/src/flame/priority.dart rename to packages/pinball_flame/lib/src/priority.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_flame/test/src/keyboard_input_controller_test.dart b/packages/pinball_flame/test/src/keyboard_input_controller_test.dart new file mode 100644 index 00000000..99a0006b --- /dev/null +++ b/packages/pinball_flame/test/src/keyboard_input_controller_test.dart @@ -0,0 +1,78 @@ +// ignore_for_file: cascade_invocations, one_member_abstracts + +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_flame/pinball_flame.dart'; + +abstract class _KeyCallStub { + bool onCall(); +} + +class KeyCallStub extends Mock implements _KeyCallStub {} + +class MockRawKeyUpEvent extends Mock implements RawKeyUpEvent { + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return super.toString(); + } +} + +RawKeyUpEvent _mockKeyUp(LogicalKeyboardKey key) { + final event = MockRawKeyUpEvent(); + when(() => event.logicalKey).thenReturn(key); + return event; +} + +void main() { + group('KeyboardInputController', () { + test('calls registered handlers', () { + final stub = KeyCallStub(); + when(stub.onCall).thenReturn(true); + + final input = KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowUp: stub.onCall, + }, + ); + + input.onKeyEvent(_mockKeyUp(LogicalKeyboardKey.arrowUp), {}); + verify(stub.onCall).called(1); + }); + + test( + 'returns false the handler return value', + () { + final stub = KeyCallStub(); + when(stub.onCall).thenReturn(false); + + final input = KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowUp: stub.onCall, + }, + ); + + expect( + input.onKeyEvent(_mockKeyUp(LogicalKeyboardKey.arrowUp), {}), + isFalse, + ); + }, + ); + + test( + 'returns true (allowing event to bubble) when no handler is registered', + () { + final stub = KeyCallStub(); + when(stub.onCall).thenReturn(true); + + final input = KeyboardInputController(); + + expect( + input.onKeyEvent(_mockKeyUp(LogicalKeyboardKey.arrowUp), {}), + isTrue, + ); + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/flame/priority_test.dart b/packages/pinball_flame/test/src/priority_test.dart similarity index 98% rename from packages/pinball_components/test/src/flame/priority_test.dart rename to packages/pinball_flame/test/src/priority_test.dart index 231c7744..41e362d8 100644 --- a/packages/pinball_components/test/src/flame/priority_test.dart +++ b/packages/pinball_flame/test/src/priority_test.dart @@ -3,9 +3,9 @@ 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 'package:pinball_flame/pinball_flame.dart'; -import '../../helpers/helpers.dart'; +import '../helpers/helpers.dart'; class TestBodyComponent extends BodyComponent { @override diff --git a/pubspec.lock b/pubspec.lock index fc1e96a6..1a502f37 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -196,7 +196,7 @@ packages: name: flame url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" flame_audio: dependency: transitive description: @@ -217,7 +217,7 @@ packages: name: flame_forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.10.0" + version: "0.11.0" flame_test: dependency: "direct dev" description: @@ -258,7 +258,7 @@ packages: name: forge2d url: "https://pub.dartlang.org" source: hosted - version: "0.10.0" + version: "0.11.0" frontend_server_client: dependency: transitive description: @@ -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 d497e561..f7676247 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,9 +10,9 @@ dependencies: bloc: ^8.0.2 cloud_firestore: ^3.1.10 equatable: ^2.0.3 - flame: ^1.1.0 + flame: ^1.1.1 flame_bloc: ^1.2.0 - flame_forge2d: ^0.10.0 + flame_forge2d: ^0.11.0 flutter: sdk: flutter flutter_bloc: ^8.0.1 @@ -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..e83c35d3 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -21,7 +21,6 @@ void main() { const GameState( score: 0, balls: 2, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ), @@ -41,14 +40,12 @@ void main() { const GameState( score: 2, balls: 3, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ), const GameState( score: 5, balls: 3, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ), @@ -69,21 +66,18 @@ 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: [], ), @@ -91,103 +85,6 @@ void main() { ); }); - 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', @@ -200,21 +97,18 @@ void main() { 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], ), @@ -222,6 +116,33 @@ void main() { ); }); + 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, + activatedDashNests: {}, + bonusHistory: [GameBonus.googleWord], + ), + GameState( + score: 0, + balls: 3, + activatedDashNests: {}, + bonusHistory: [GameBonus.googleWord, GameBonus.dashNest], + ), + ], + ); + }, + ); + group('SparkyTurboChargeActivated', () { blocTest( 'adds game bonus', @@ -231,7 +152,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..ef2a9f54 100644 --- a/test/game/bloc/game_event_test.dart +++ b/test/game/bloc/game_event_test.dart @@ -41,61 +41,51 @@ 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); - }); + 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'))), - ); - }); + 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..81ca29f1 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -10,7 +10,6 @@ void main() { GameState( score: 0, balls: 0, - activatedBonusLetters: const [], activatedDashNests: const {}, bonusHistory: const [], ), @@ -18,7 +17,6 @@ void main() { const GameState( score: 0, balls: 0, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ), @@ -32,7 +30,6 @@ void main() { const GameState( score: 0, balls: 0, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ), @@ -49,7 +46,6 @@ void main() { () => GameState( balls: -1, score: 0, - activatedBonusLetters: const [], activatedDashNests: const {}, bonusHistory: const [], ), @@ -66,7 +62,6 @@ void main() { () => GameState( balls: 0, score: -1, - activatedBonusLetters: const [], activatedDashNests: const {}, bonusHistory: const [], ), @@ -82,7 +77,6 @@ void main() { const gameState = GameState( balls: 0, score: 0, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ); @@ -95,7 +89,6 @@ void main() { const gameState = GameState( balls: 1, score: 0, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ); @@ -103,36 +96,6 @@ void main() { }); }); - 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,7 +104,6 @@ void main() { const gameState = GameState( balls: 0, score: 2, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ); @@ -159,7 +121,6 @@ void main() { const gameState = GameState( balls: 0, score: 2, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [], ); @@ -177,16 +138,14 @@ 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,7 +153,6 @@ void main() { gameState.copyWith( score: otherGameState.score, balls: otherGameState.balls, - activatedBonusLetters: otherGameState.activatedBonusLetters, activatedDashNests: otherGameState.activatedDashNests, bonusHistory: otherGameState.bonusHistory, ), 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 03c51830..3c0fc1b0 100644 --- a/test/game/components/controlled_flipper_test.dart +++ b/test/game/components/controlled_flipper_test.dart @@ -1,5 +1,6 @@ import 'dart:collection'; +import 'package:bloc_test/bloc_test.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -10,7 +11,22 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); + + final flameBlocTester = FlameBlocTester( + gameBuilder: EmptyPinballTestGame.new, + blocBuilder: () { + final bloc = MockGameBloc(); + const state = GameState( + score: 0, + balls: 0, + bonusHistory: [], + activatedDashNests: {}, + ); + whenListen(bloc, Stream.value(state), initialState: state); + return bloc; + }, + ); group('FlipperController', () { group('onKeyEvent', () { @@ -42,46 +58,45 @@ void main() { await game.add(flipper); controller.onKeyEvent(event, {}); - expect(flipper.body.linearVelocity.y, isPositive); + expect(flipper.body.linearVelocity.y, isNegative); expect(flipper.body.linearVelocity.x, isZero); }, ); }); - testRawKeyUpEvents(leftKeys, (event) { - flameTester.test( - 'moves downwards ' - 'when ${event.logicalKey.keyLabel} is released', - (game) async { - await game.ready(); - await game.add(flipper); + testRawKeyDownEvents(leftKeys, (event) { + flameBlocTester.testGameWidget( + 'does nothing when is game over', + setUp: (game, tester) async { + await game.ensureAdd(flipper); controller.onKeyEvent(event, {}); - - expect(flipper.body.linearVelocity.y, isNegative); + }, + verify: (game, tester) async { + expect(flipper.body.linearVelocity.y, isZero); expect(flipper.body.linearVelocity.x, isZero); }, ); }); - testRawKeyUpEvents(rightKeys, (event) { + testRawKeyUpEvents(leftKeys, (event) { flameTester.test( - 'does nothing ' + 'moves downwards ' 'when ${event.logicalKey.keyLabel} is released', (game) async { await game.ready(); await game.add(flipper); controller.onKeyEvent(event, {}); - expect(flipper.body.linearVelocity.y, isZero); + expect(flipper.body.linearVelocity.y, isPositive); expect(flipper.body.linearVelocity.x, isZero); }, ); }); - testRawKeyDownEvents(rightKeys, (event) { + testRawKeyUpEvents(rightKeys, (event) { flameTester.test( 'does nothing ' - 'when ${event.logicalKey.keyLabel} is pressed', + 'when ${event.logicalKey.keyLabel} is released', (game) async { await game.ready(); await game.add(flipper); @@ -113,7 +128,7 @@ void main() { await game.add(flipper); controller.onKeyEvent(event, {}); - expect(flipper.body.linearVelocity.y, isPositive); + expect(flipper.body.linearVelocity.y, isNegative); expect(flipper.body.linearVelocity.x, isZero); }, ); @@ -128,31 +143,30 @@ void main() { await game.add(flipper); controller.onKeyEvent(event, {}); - expect(flipper.body.linearVelocity.y, isNegative); + expect(flipper.body.linearVelocity.y, isPositive); expect(flipper.body.linearVelocity.x, isZero); }, ); }); - testRawKeyUpEvents(leftKeys, (event) { - flameTester.test( - 'does nothing ' - 'when ${event.logicalKey.keyLabel} is released', - (game) async { - await game.ready(); - await game.add(flipper); + testRawKeyDownEvents(rightKeys, (event) { + flameBlocTester.testGameWidget( + 'does nothing when is game over', + setUp: (game, tester) async { + await game.ensureAdd(flipper); controller.onKeyEvent(event, {}); - + }, + verify: (game, tester) async { expect(flipper.body.linearVelocity.y, isZero); expect(flipper.body.linearVelocity.x, isZero); }, ); }); - testRawKeyDownEvents(leftKeys, (event) { + testRawKeyUpEvents(leftKeys, (event) { flameTester.test( 'does nothing ' - 'when ${event.logicalKey.keyLabel} is pressed', + 'when ${event.logicalKey.keyLabel} is released', (game) async { await game.ready(); await game.add(flipper); diff --git a/test/game/components/controlled_plunger_test.dart b/test/game/components/controlled_plunger_test.dart index bb965fc1..a377487e 100644 --- a/test/game/components/controlled_plunger_test.dart +++ b/test/game/components/controlled_plunger_test.dart @@ -1,5 +1,6 @@ import 'dart:collection'; +import 'package:bloc_test/bloc_test.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/services.dart'; @@ -11,7 +12,22 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballGameTest.new); + final flameTester = FlameTester(EmptyPinballTestGame.new); + + final flameBlocTester = FlameBlocTester( + gameBuilder: EmptyPinballTestGame.new, + blocBuilder: () { + final bloc = MockGameBloc(); + const state = GameState( + score: 0, + balls: 0, + bonusHistory: [], + activatedDashNests: {}, + ); + whenListen(bloc, Stream.value(state), initialState: state); + return bloc; + }, + ); group('PlungerController', () { group('onKeyEvent', () { @@ -38,7 +54,7 @@ void main() { await game.ensureAdd(plunger); controller.onKeyEvent(event, {}); - expect(plunger.body.linearVelocity.y, isNegative); + expect(plunger.body.linearVelocity.y, isPositive); expect(plunger.body.linearVelocity.x, isZero); }, ); @@ -51,10 +67,10 @@ void main() { 'and plunger is below its starting position', (game) async { await game.ensureAdd(plunger); - plunger.body.setTransform(Vector2(0, -1), 0); + plunger.body.setTransform(Vector2(0, 1), 0); controller.onKeyEvent(event, {}); - expect(plunger.body.linearVelocity.y, isPositive); + expect(plunger.body.linearVelocity.y, isNegative); expect(plunger.body.linearVelocity.x, isZero); }, ); @@ -73,6 +89,20 @@ void main() { }, ); }); + + testRawKeyDownEvents(downKeys, (event) { + flameBlocTester.testGameWidget( + 'does nothing when is game over', + setUp: (game, tester) async { + await game.ensureAdd(plunger); + controller.onKeyEvent(event, {}); + }, + verify: (game, tester) async { + expect(plunger.body.linearVelocity.y, isZero); + expect(plunger.body.linearVelocity.x, isZero); + }, + ); + }); }); }); } 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..7ad5a3de 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( @@ -95,7 +95,6 @@ void main() { const state = GameState( score: 0, balls: 3, - activatedBonusLetters: [], activatedDashNests: {}, bonusHistory: [GameBonus.dashNest], ); @@ -158,7 +157,7 @@ void main() { }); final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, + gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, ); diff --git a/test/game/components/game_flow_controller_test.dart b/test/game/components/game_flow_controller_test.dart index dc1d9ab8..8bb81a6c 100644 --- a/test/game/components/game_flow_controller_test.dart +++ b/test/game/components/game_flow_controller_test.dart @@ -15,7 +15,6 @@ void main() { final state = GameState( score: 10, balls: 0, - activatedBonusLetters: const [], bonusHistory: const [], activatedDashNests: const {}, ); @@ -42,7 +41,12 @@ void main() { gameFlowController = GameFlowController(game); overlays = MockActiveOverlaysNotifier(); - when(backboard.gameOverMode).thenAnswer((_) async {}); + when( + () => backboard.gameOverMode( + score: any(named: 'score'), + onSubmit: any(named: 'onSubmit'), + ), + ).thenAnswer((_) async {}); when(backboard.waitingMode).thenAnswer((_) async {}); when(cameraController.focusOnBackboard).thenAnswer((_) async {}); when(cameraController.focusOnGame).thenAnswer((_) async {}); @@ -61,13 +65,17 @@ void main() { GameState( score: 10, balls: 0, - activatedBonusLetters: const [], bonusHistory: const [], activatedDashNests: const {}, ), ); - verify(backboard.gameOverMode).called(1); + verify( + () => backboard.gameOverMode( + score: 0, + onSubmit: any(named: 'onSubmit'), + ), + ).called(1); verify(cameraController.focusOnBackboard).called(1); }, ); 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..9d2b5310 100644 --- a/test/game/components/score_effect_controller_test.dart +++ b/test/game/components/score_effect_controller_test.dart @@ -30,7 +30,6 @@ void main() { const current = GameState( score: 10, balls: 3, - activatedBonusLetters: [], bonusHistory: [], activatedDashNests: {}, ); @@ -44,7 +43,6 @@ void main() { const current = GameState( score: 10, balls: 3, - activatedBonusLetters: [], bonusHistory: [], activatedDashNests: {}, ); @@ -70,7 +68,6 @@ void main() { const state = GameState( score: 10, balls: 3, - activatedBonusLetters: [], bonusHistory: [], activatedDashNests: {}, ); @@ -89,7 +86,6 @@ void main() { const GameState( score: 10, balls: 3, - activatedBonusLetters: [], bonusHistory: [], activatedDashNests: {}, ), @@ -99,7 +95,6 @@ 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..2d5f50d9 100644 --- a/test/game/view/game_hud_test.dart +++ b/test/game/view/game_hud_test.dart @@ -12,7 +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..12e6d366 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -64,8 +64,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 {} }