diff --git a/assets/images/bonus_animation/android.png b/assets/images/bonus_animation/android.png new file mode 100644 index 00000000..200e1c6c Binary files /dev/null and b/assets/images/bonus_animation/android.png differ diff --git a/assets/images/bonus_animation/dash_nest.png b/assets/images/bonus_animation/dash_nest.png new file mode 100644 index 00000000..210f17cb Binary files /dev/null and b/assets/images/bonus_animation/dash_nest.png differ diff --git a/assets/images/bonus_animation/dino.png b/assets/images/bonus_animation/dino.png new file mode 100644 index 00000000..a8a9cfe3 Binary files /dev/null and b/assets/images/bonus_animation/dino.png differ diff --git a/assets/images/bonus_animation/google.png b/assets/images/bonus_animation/google.png new file mode 100644 index 00000000..7adab3b4 Binary files /dev/null and b/assets/images/bonus_animation/google.png differ diff --git a/assets/images/bonus_animation/sparky_turbo_charge.png b/assets/images/bonus_animation/sparky_turbo_charge.png new file mode 100644 index 00000000..8b3491e8 Binary files /dev/null and b/assets/images/bonus_animation/sparky_turbo_charge.png differ diff --git a/lib/game/components/controlled_ball.dart b/lib/game/components/controlled_ball.dart index f224ca1a..6b983cea 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -37,8 +37,7 @@ class ControlledBall extends Ball with Controls { /// [Ball] used in [DebugPinballGame]. ControlledBall.debug() : super(baseColor: const Color(0xFFFF0000)) { controller = DebugBallController(this); - priority = RenderPriority.ballOnSpaceshipRamp; - layer = Layer.spaceshipEntranceRamp; + priority = RenderPriority.ballOnBoard; } } diff --git a/lib/game/components/controlled_sparky_computer.dart b/lib/game/components/controlled_sparky_computer.dart index 6a8e9e59..65190a1f 100644 --- a/lib/game/components/controlled_sparky_computer.dart +++ b/lib/game/components/controlled_sparky_computer.dart @@ -11,19 +11,16 @@ import 'package:pinball_flame/pinball_flame.dart'; /// [SparkyComputer] with a [SparkyComputerController] attached. /// {@endtemplate} class ControlledSparkyComputer extends SparkyComputer - with Controls, HasGameRef { + with Controls, HasGameRef { /// {@macro controlled_sparky_computer} - ControlledSparkyComputer() { + ControlledSparkyComputer() : super() { controller = SparkyComputerController(this); } @override - void build(Forge2DGame _) { - addContactCallback(SparkyTurboChargeSensorBallContactCallback()); - final sparkyTurboChargeSensor = SparkyTurboChargeSensor() - ..initialPosition = Vector2(-13, -49.8); - add(sparkyTurboChargeSensor); - super.build(_); + Future onLoad() async { + await super.onLoad(); + gameRef.addContactCallback(SparkyComputerSensorBallContactCallback()); } } @@ -39,46 +36,17 @@ class SparkyComputerController : super(controlledComputer); } -/// {@template sparky_turbo_charge_sensor} -/// Small sensor body used to detect when a ball has entered the -/// [SparkyComputer] with the [SparkyTurboChargeSensorBallContactCallback]. +/// {@template sparky_computer_sensor_ball_contact_callback} +/// Turbo charges the [Ball] when it enters the [SparkyComputer] /// {@endtemplate} @visibleForTesting -class SparkyTurboChargeSensor extends BodyComponent with InitialPosition { - /// {@macro sparky_turbo_charge_sensor} - SparkyTurboChargeSensor() { - renderBody = false; - } - - @override - Body createBody() { - final shape = CircleShape()..radius = 0.1; - - final fixtureDef = FixtureDef(shape)..isSensor = true; - - final bodyDef = BodyDef() - ..position = initialPosition - ..userData = this; - - return world.createBody(bodyDef)..createFixture(fixtureDef); - } -} - -/// {@template sparky_turbo_charge_sensor_ball_contact_callback} -/// Turbo charges the [Ball] on contact with [SparkyTurboChargeSensor]. -/// {@endtemplate} -@visibleForTesting -class SparkyTurboChargeSensorBallContactCallback - extends ContactCallback { - /// {@macro sparky_turbo_charge_sensor_ball_contact_callback} - SparkyTurboChargeSensorBallContactCallback(); +class SparkyComputerSensorBallContactCallback + extends ContactCallback { + /// {@macro sparky_computer_sensor_ball_contact_callback} + SparkyComputerSensorBallContactCallback(); @override - void begin( - SparkyTurboChargeSensor sparkyTurboChargeSensor, - ControlledBall ball, - _, - ) { + void begin(_, ControlledBall ball, __) { ball.controller.turboCharge(); } } diff --git a/lib/game/components/game_flow_controller.dart b/lib/game/components/game_flow_controller.dart index 957689a9..77afebe6 100644 --- a/lib/game/components/game_flow_controller.dart +++ b/lib/game/components/game_flow_controller.dart @@ -32,6 +32,8 @@ class GameFlowController extends ComponentController // next page component.firstChild()?.gameOverMode( score: state?.score ?? 0, + characterIconPath: + component.theme.characterTheme.leaderboardIcon.keyName, ); component.firstChild()?.focusOnBackboard(); } diff --git a/lib/game/components/launcher.dart b/lib/game/components/launcher.dart index d2e69174..7aef09d2 100644 --- a/lib/game/components/launcher.dart +++ b/lib/game/components/launcher.dart @@ -7,21 +7,15 @@ import 'package:pinball_flame/pinball_flame.dart'; /// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and /// [LaunchRamp]. /// {@endtemplate} -class Launcher extends Forge2DBlueprint { +class Launcher extends Blueprint { /// {@macro launcher} - Launcher(); - - /// [Plunger] to launch the [Ball] onto the board. - late final Plunger plunger; - - @override - void build(Forge2DGame gameRef) { - plunger = ControlledPlunger(compressionDistance: 14) - ..initialPosition = Vector2(40.7, 38); - - final _rocket = RocketSpriteComponent()..position = Vector2(43, 62); - - addAll([_rocket, plunger]); - addBlueprint(LaunchRamp()); - } + Launcher() + : super( + components: [ + ControlledPlunger(compressionDistance: 14) + ..initialPosition = Vector2(40.7, 38), + RocketSpriteComponent()..position = Vector2(43, 62), + ], + blueprints: [LaunchRamp()], + ); } diff --git a/lib/game/components/wall.dart b/lib/game/components/wall.dart index ba8af5e7..aaae1d23 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -39,22 +39,6 @@ class Wall extends BodyComponent { } } -/// Create top, left, and right [Wall]s for the game board. -List createBoundaries(Forge2DGame game) { - final topLeft = BoardDimensions.bounds.topLeft.toVector2() + Vector2(18.6, 0); - final bottomRight = BoardDimensions.bounds.bottomRight.toVector2(); - - final topRight = - BoardDimensions.bounds.topRight.toVector2() - Vector2(18.6, 0); - final bottomLeft = BoardDimensions.bounds.bottomLeft.toVector2(); - - return [ - Wall(start: topLeft, end: topRight), - Wall(start: topRight, end: bottomRight), - Wall(start: topLeft, end: bottomLeft), - ]; -} - /// {@template bottom_wall} /// [Wall] located at the bottom of the board. /// diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index a1988eda..1cd4f194 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -1,11 +1,17 @@ import 'package:pinball/game/game.dart'; import 'package:pinball/gen/assets.gen.dart'; import 'package:pinball_components/pinball_components.dart' as components; +import 'package:pinball_theme/pinball_theme.dart' hide Assets; /// Add methods to help loading and caching game assets. extension PinballGameAssetsX on PinballGame { /// Returns a list of assets to be loaded List preLoadAssets() { + const dashTheme = DashTheme(); + const sparkyTheme = SparkyTheme(); + const androidTheme = AndroidTheme(); + const dinoTheme = DinoTheme(); + return [ images.load(components.Assets.images.ball.ball.keyName), images.load(components.Assets.images.ball.flameEffect.keyName), @@ -93,6 +99,10 @@ extension PinballGameAssetsX on PinballGame { 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(dashTheme.leaderboardIcon.keyName), + images.load(sparkyTheme.leaderboardIcon.keyName), + images.load(androidTheme.leaderboardIcon.keyName), + images.load(dinoTheme.leaderboardIcon.keyName), images.load(Assets.images.components.background.path), ]; } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index dad3c149..5c2fefd5 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -46,7 +46,8 @@ class PinballGame extends Forge2DGame unawaited(add(CameraController(this))); unawaited(add(Backboard.waiting(position: Vector2(0, -88)))); - await _addGameBoundaries(); + // TODO(allisonryan0002): banish Wall and Board classes in later PR. + await add(BottomWall()); unawaited(addFromBlueprint(Boundaries())); unawaited(addFromBlueprint(ControlledSparkyComputer())); @@ -68,7 +69,7 @@ class PinballGame extends Forge2DGame ); unawaited(addFromBlueprint(SpaceshipRail())); - controller.attachTo(launcher.plunger); + controller.attachTo(launcher.components.whereType().first); await super.onLoad(); } @@ -77,11 +78,6 @@ class PinballGame extends Forge2DGame addContactCallback(BottomWallBallContactCallback()); } - Future _addGameBoundaries() async { - await add(BottomWall()); - createBoundaries(this).forEach(add); - } - Future _addBonusWord() async { await add( GoogleWord( diff --git a/lib/game/view/widgets/bonus_animation.dart b/lib/game/view/widgets/bonus_animation.dart new file mode 100644 index 00000000..39cee913 --- /dev/null +++ b/lib/game/view/widgets/bonus_animation.dart @@ -0,0 +1,106 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flame/flame.dart'; +import 'package:flame/sprite.dart'; +import 'package:flame/widgets.dart'; +import 'package:flutter/material.dart' hide Image; +import 'package:pinball/gen/assets.gen.dart'; + +class BonusAnimation extends StatelessWidget { + const BonusAnimation._( + this.imagePath, { + VoidCallback? onCompleted, + Key? key, + }) : _onCompleted = onCompleted, + super(key: key); + + BonusAnimation.dashNest({ + Key? key, + VoidCallback? onCompleted, + }) : this._( + Assets.images.bonusAnimation.dashNest.keyName, + onCompleted: onCompleted, + key: key, + ); + + BonusAnimation.sparkyTurboCharge({ + Key? key, + VoidCallback? onCompleted, + }) : this._( + Assets.images.bonusAnimation.sparkyTurboCharge.keyName, + onCompleted: onCompleted, + key: key, + ); + + BonusAnimation.dino({ + Key? key, + VoidCallback? onCompleted, + }) : this._( + Assets.images.bonusAnimation.dino.keyName, + onCompleted: onCompleted, + key: key, + ); + + BonusAnimation.android({ + Key? key, + VoidCallback? onCompleted, + }) : this._( + Assets.images.bonusAnimation.android.keyName, + onCompleted: onCompleted, + key: key, + ); + + BonusAnimation.google({ + Key? key, + VoidCallback? onCompleted, + }) : this._( + Assets.images.bonusAnimation.google.keyName, + onCompleted: onCompleted, + key: key, + ); + + final String imagePath; + + final VoidCallback? _onCompleted; + + static Future loadAssets() { + Flame.images.prefix = ''; + return Flame.images.loadAll([ + Assets.images.bonusAnimation.dashNest.keyName, + Assets.images.bonusAnimation.sparkyTurboCharge.keyName, + Assets.images.bonusAnimation.dino.keyName, + Assets.images.bonusAnimation.android.keyName, + Assets.images.bonusAnimation.google.keyName, + ]); + } + + @override + Widget build(BuildContext context) { + final spriteSheet = SpriteSheet.fromColumnsAndRows( + image: Flame.images.fromCache(imagePath), + columns: 8, + rows: 9, + ); + final animation = spriteSheet.createAnimation( + row: 0, + stepTime: 1 / 24, + to: spriteSheet.rows * spriteSheet.columns, + loop: false, + ); + + Future.delayed( + Duration(seconds: animation.totalDuration().ceil()), + () { + _onCompleted?.call(); + }, + ); + + return SizedBox( + width: double.infinity, + height: double.infinity, + child: SpriteAnimationWidget( + animation: animation, + ), + ); + } +} diff --git a/lib/game/view/widgets/widgets.dart b/lib/game/view/widgets/widgets.dart index 7e9db5c3..674577af 100644 --- a/lib/game/view/widgets/widgets.dart +++ b/lib/game/view/widgets/widgets.dart @@ -1,2 +1,3 @@ +export 'bonus_animation.dart'; export 'game_hud.dart'; export 'play_button_overlay.dart'; diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 97be7f3e..3e52e399 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -10,10 +10,36 @@ import 'package:flutter/widgets.dart'; class $AssetsImagesGen { const $AssetsImagesGen(); + $AssetsImagesBonusAnimationGen get bonusAnimation => + const $AssetsImagesBonusAnimationGen(); $AssetsImagesComponentsGen get components => const $AssetsImagesComponentsGen(); } +class $AssetsImagesBonusAnimationGen { + const $AssetsImagesBonusAnimationGen(); + + /// File path: assets/images/bonus_animation/android.png + AssetGenImage get android => + const AssetGenImage('assets/images/bonus_animation/android.png'); + + /// File path: assets/images/bonus_animation/dash_nest.png + AssetGenImage get dashNest => + const AssetGenImage('assets/images/bonus_animation/dash_nest.png'); + + /// File path: assets/images/bonus_animation/dino.png + AssetGenImage get dino => + const AssetGenImage('assets/images/bonus_animation/dino.png'); + + /// File path: assets/images/bonus_animation/google.png + AssetGenImage get google => + const AssetGenImage('assets/images/bonus_animation/google.png'); + + /// File path: assets/images/bonus_animation/sparky_turbo_charge.png + AssetGenImage get sparkyTurboCharge => const AssetGenImage( + 'assets/images/bonus_animation/sparky_turbo_charge.png'); +} + class $AssetsImagesComponentsGen { const $AssetsImagesComponentsGen(); diff --git a/packages/pinball_components/assets/images/baseboard/left.png b/packages/pinball_components/assets/images/baseboard/left.png index 17253554..d13b4e31 100644 Binary files a/packages/pinball_components/assets/images/baseboard/left.png and b/packages/pinball_components/assets/images/baseboard/left.png differ diff --git a/packages/pinball_components/assets/images/baseboard/right.png b/packages/pinball_components/assets/images/baseboard/right.png index 081a1782..8ad93045 100644 Binary files a/packages/pinball_components/assets/images/baseboard/right.png and b/packages/pinball_components/assets/images/baseboard/right.png differ diff --git a/packages/pinball_components/assets/images/boundary/outer.png b/packages/pinball_components/assets/images/boundary/outer.png index c0b81e96..3c06cb6c 100644 Binary files a/packages/pinball_components/assets/images/boundary/outer.png and b/packages/pinball_components/assets/images/boundary/outer.png differ diff --git a/packages/pinball_components/assets/images/dash/animatronic.png b/packages/pinball_components/assets/images/dash/animatronic.png index e67cafef..13f7b794 100644 Binary files a/packages/pinball_components/assets/images/dash/animatronic.png and b/packages/pinball_components/assets/images/dash/animatronic.png differ diff --git a/packages/pinball_components/assets/images/flipper/left.png b/packages/pinball_components/assets/images/flipper/left.png index 42798f28..3aefa225 100644 Binary files a/packages/pinball_components/assets/images/flipper/left.png and b/packages/pinball_components/assets/images/flipper/left.png differ diff --git a/packages/pinball_components/assets/images/flipper/right.png b/packages/pinball_components/assets/images/flipper/right.png index 86fbc81d..3627c86c 100644 Binary files a/packages/pinball_components/assets/images/flipper/right.png and b/packages/pinball_components/assets/images/flipper/right.png differ diff --git a/packages/pinball_components/assets/images/slingshot/lower.png b/packages/pinball_components/assets/images/slingshot/lower.png index 71a6a277..61e01ba1 100644 Binary files a/packages/pinball_components/assets/images/slingshot/lower.png and b/packages/pinball_components/assets/images/slingshot/lower.png differ diff --git a/packages/pinball_components/assets/images/slingshot/upper.png b/packages/pinball_components/assets/images/slingshot/upper.png index e6b42ded..d86bd925 100644 Binary files a/packages/pinball_components/assets/images/slingshot/upper.png and b/packages/pinball_components/assets/images/slingshot/upper.png differ diff --git a/packages/pinball_components/lib/gen/pinball_fonts.dart b/packages/pinball_components/lib/gen/pinball_fonts.dart index 59dcaa6e..de6c5782 100644 --- a/packages/pinball_components/lib/gen/pinball_fonts.dart +++ b/packages/pinball_components/lib/gen/pinball_fonts.dart @@ -1,6 +1,6 @@ import 'package:pinball_components/gen/fonts.gen.dart'; -const String _fontPath = 'packages/pinball_components/'; +const String _fontPath = 'packages/pinball_components'; /// Class with the fonts available on the pinball game class PinballFonts { diff --git a/packages/pinball_components/lib/src/components/backboard/backboard.dart b/packages/pinball_components/lib/src/components/backboard/backboard.dart index dc3cacb3..fe5fd37c 100644 --- a/packages/pinball_components/lib/src/components/backboard/backboard.dart +++ b/packages/pinball_components/lib/src/components/backboard/backboard.dart @@ -34,12 +34,14 @@ class Backboard extends PositionComponent with HasGameRef { /// Returns a [Backboard] initialized in the game over mode factory Backboard.gameOver({ required Vector2 position, + required String characterIconPath, required int score, required BackboardOnSubmit onSubmit, }) { return Backboard(position: position) ..gameOverMode( score: score, + characterIconPath: characterIconPath, onSubmit: onSubmit, ); } @@ -62,12 +64,14 @@ class Backboard extends PositionComponent with HasGameRef { /// Puts the Backboard in game over mode, where the score input is shown. Future gameOverMode({ required int score, + required String characterIconPath, BackboardOnSubmit? onSubmit, }) async { children.removeWhere((_) => true); await add( BackboardGameOver( score: score, + characterIconPath: characterIconPath, 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 index 98ae6ae0..cfea0bc6 100644 --- a/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart +++ b/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart @@ -18,78 +18,41 @@ class BackboardGameOver extends PositionComponent with HasGameRef { /// {@macro backboard_game_over} BackboardGameOver({ required int score, + required String characterIconPath, BackboardOnSubmit? onSubmit, - }) : _score = score, - _onSubmit = onSubmit; + }) : _onSubmit = onSubmit, + super( + children: [ + _BackboardSpriteComponent(), + _BackboardDisplaySpriteComponent(), + _ScoreTextComponent(score.formatScore()), + _CharacterIconSpriteComponent(characterIconPath), + ], + ); - 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, + await add( + BackboardLetterPrompt( + position: Vector2( + 24.3 + (4.5 * i), + -45, ), + hasFocus: i == 0, ), ); } - unawaited( - add( - KeyboardInputController( - keyUp: { - LogicalKeyboardKey.arrowLeft: () => _movePrompt(true), - LogicalKeyboardKey.arrowRight: () => _movePrompt(false), - LogicalKeyboardKey.enter: _submit, - }, - ), + await add( + KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowLeft: () => _movePrompt(true), + LogicalKeyboardKey.arrowRight: () => _movePrompt(false), + LogicalKeyboardKey.enter: _submit, + }, ), ); } @@ -118,3 +81,64 @@ class BackboardGameOver extends PositionComponent with HasGameRef { return false; } } + +class _BackboardSpriteComponent extends SpriteComponent with HasGameRef { + _BackboardSpriteComponent() : super(anchor: Anchor.bottomCenter); + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = await gameRef.loadSprite( + Assets.images.backboard.backboardGameOver.keyName, + ); + this.sprite = sprite; + size = sprite.originalSize / 10; + } +} + +class _BackboardDisplaySpriteComponent extends SpriteComponent with HasGameRef { + _BackboardDisplaySpriteComponent() + : super( + anchor: Anchor.bottomCenter, + position: Vector2(0, -11.5), + ); + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = await gameRef.loadSprite( + Assets.images.backboard.display.keyName, + ); + this.sprite = sprite; + size = sprite.originalSize / 10; + } +} + +class _ScoreTextComponent extends TextComponent { + _ScoreTextComponent(String score) + : super( + text: score, + anchor: Anchor.centerLeft, + position: Vector2(-34, -45), + textRenderer: Backboard.textPaint, + ); +} + +class _CharacterIconSpriteComponent extends SpriteComponent with HasGameRef { + _CharacterIconSpriteComponent(String characterIconPath) + : _characterIconPath = characterIconPath, + super( + anchor: Anchor.center, + position: Vector2(18.4, -45), + ); + + final String _characterIconPath; + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = Sprite(gameRef.images.fromCache(_characterIconPath)); + this.sprite = sprite; + size = sprite.originalSize / 10; + } +} diff --git a/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart b/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart index 61d2074d..fe582210 100644 --- a/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart +++ b/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart @@ -34,22 +34,19 @@ class BackboardLetterPrompt extends PositionComponent { @override Future onLoad() async { _underscore = RectangleComponent( - size: Vector2( - 4, - 1.2, - ), + size: Vector2(3.8, 0.8), anchor: Anchor.center, - position: Vector2(0, 4), + position: Vector2(-0.3, 4), ); - unawaited(add(_underscore)); + await add(_underscore); _input = TextComponent( text: 'A', textRenderer: Backboard.textPaint, anchor: Anchor.center, ); - unawaited(add(_input)); + await add(_input); _underscoreBlinker = TimerComponent( period: 0.6, @@ -62,16 +59,14 @@ class BackboardLetterPrompt extends PositionComponent { }, ); - unawaited(add(_underscoreBlinker)); + await add(_underscoreBlinker); - unawaited( - add( - KeyboardInputController( - keyUp: { - LogicalKeyboardKey.arrowUp: () => _cycle(true), - LogicalKeyboardKey.arrowDown: () => _cycle(false), - }, - ), + await add( + KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowUp: () => _cycle(true), + LogicalKeyboardKey.arrowDown: () => _cycle(false), + }, ), ); } diff --git a/packages/pinball_components/lib/src/components/baseboard.dart b/packages/pinball_components/lib/src/components/baseboard.dart index 29d7285b..07f39070 100644 --- a/packages/pinball_components/lib/src/components/baseboard.dart +++ b/packages/pinball_components/lib/src/components/baseboard.dart @@ -22,7 +22,6 @@ class Baseboard extends BodyComponent with InitialPosition { final BoardSide _side; List _createFixtureDefs() { - final fixturesDef = []; final direction = _side.direction; const arcsAngle = 1.11; final arcsRotation = (_side.isLeft) ? -2.7 : -1.6; @@ -30,12 +29,10 @@ class Baseboard extends BodyComponent with InitialPosition { final pegBumperShape = CircleShape()..radius = 0.7; 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); final topCircleFixtureDef = FixtureDef(topCircleShape); - fixturesDef.add(topCircleFixtureDef); final innerEdgeShape = EdgeShape() ..set( @@ -43,7 +40,6 @@ class Baseboard extends BodyComponent with InitialPosition { Vector2(5.29 * direction, 0.95), ); final innerEdgeShapeFixtureDef = FixtureDef(innerEdgeShape); - fixturesDef.add(innerEdgeShapeFixtureDef); final outerEdgeShape = EdgeShape() ..set( @@ -51,7 +47,6 @@ class Baseboard extends BodyComponent with InitialPosition { Vector2(3.79 * direction, 5.95), ); final outerEdgeShapeFixtureDef = FixtureDef(outerEdgeShape); - fixturesDef.add(outerEdgeShapeFixtureDef); final upperArcShape = ArcShape( center: Vector2(0.09 * direction, -2.15), @@ -60,7 +55,6 @@ class Baseboard extends BodyComponent with InitialPosition { rotation: arcsRotation, ); final upperArcFixtureDef = FixtureDef(upperArcShape); - fixturesDef.add(upperArcFixtureDef); final lowerArcShape = ArcShape( center: Vector2(0.09 * direction, 3.35), @@ -69,7 +63,6 @@ class Baseboard extends BodyComponent with InitialPosition { rotation: arcsRotation, ); final lowerArcFixtureDef = FixtureDef(lowerArcShape); - fixturesDef.add(lowerArcFixtureDef); final bottomRectangle = PolygonShape() ..setAsBox( @@ -79,9 +72,16 @@ class Baseboard extends BodyComponent with InitialPosition { 0, ); final bottomRectangleFixtureDef = FixtureDef(bottomRectangle); - fixturesDef.add(bottomRectangleFixtureDef); - return fixturesDef; + return [ + pegBumperFixtureDef, + topCircleFixtureDef, + innerEdgeShapeFixtureDef, + outerEdgeShapeFixtureDef, + upperArcFixtureDef, + lowerArcFixtureDef, + bottomRectangleFixtureDef, + ]; } @override @@ -100,21 +100,26 @@ class Baseboard extends BodyComponent with InitialPosition { } class _BaseboardSpriteComponent extends SpriteComponent with HasGameRef { - _BaseboardSpriteComponent({required BoardSide side}) : _side = side; + _BaseboardSpriteComponent({required BoardSide side}) + : _side = side, + super( + anchor: Anchor.center, + position: Vector2(0.4 * -side.direction, 0), + ); final BoardSide _side; @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - (_side.isLeft) - ? Assets.images.baseboard.left.keyName - : Assets.images.baseboard.right.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + (_side.isLeft) + ? Assets.images.baseboard.left.keyName + : Assets.images.baseboard.right.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - position = Vector2(0.4 * -_side.direction, 0); - anchor = Anchor.center; } } diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart index 8c59d598..254cbfcf 100644 --- a/packages/pinball_components/lib/src/components/boundaries.dart +++ b/packages/pinball_components/lib/src/components/boundaries.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -8,14 +6,15 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template boundaries} /// A [Blueprint] which creates the [_BottomBoundary] and [_OuterBoundary]. ///{@endtemplate boundaries} -class Boundaries extends Forge2DBlueprint { - @override - void build(_) { - final bottomBoundary = _BottomBoundary(); - final outerBoundary = _OuterBoundary(); - - addAll([outerBoundary, bottomBoundary]); - } +class Boundaries extends Blueprint { + /// {@macro boundaries} + Boundaries() + : super( + components: [ + _BottomBoundary(), + _OuterBoundary(), + ], + ); } /// {@template bottom_boundary} @@ -33,8 +32,6 @@ class _BottomBoundary extends BodyComponent with InitialPosition { } List _createFixtureDefs() { - final fixturesDefs = []; - final bottomLeftCurve = BezierCurveShape( controlPoints: [ Vector2(-43.9, 41.8), @@ -43,7 +40,6 @@ class _BottomBoundary extends BodyComponent with InitialPosition { ], ); final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurve); - fixturesDefs.add(bottomLeftCurveFixtureDef); final bottomRightCurve = BezierCurveShape( controlPoints: [ @@ -53,9 +49,8 @@ class _BottomBoundary extends BodyComponent with InitialPosition { ], ); final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurve); - fixturesDefs.add(bottomRightCurveFixtureDef); - return fixturesDefs; + return [bottomLeftCurveFixtureDef, bottomRightCurveFixtureDef]; } @override @@ -69,16 +64,22 @@ class _BottomBoundary extends BodyComponent with InitialPosition { } class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef { + _BottomBoundarySpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(-5.4, 55.6), + ); + @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.boundary.bottom.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + Assets.images.boundary.bottom.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(-5.4, 55.6); } } @@ -97,15 +98,12 @@ class _OuterBoundary extends BodyComponent with InitialPosition { } List _createFixtureDefs() { - final fixturesDefs = []; - final topWall = EdgeShape() ..set( Vector2(3.6, -70.2), Vector2(-14.1, -70.2), ); final topWallFixtureDef = FixtureDef(topWall); - fixturesDefs.add(topWallFixtureDef); final topLeftCurve = BezierCurveShape( controlPoints: [ @@ -115,7 +113,6 @@ class _OuterBoundary extends BodyComponent with InitialPosition { ], ); final topLeftCurveFixtureDef = FixtureDef(topLeftCurve); - fixturesDefs.add(topLeftCurveFixtureDef); final leftWall = EdgeShape() ..set( @@ -123,9 +120,12 @@ class _OuterBoundary extends BodyComponent with InitialPosition { Vector2(-43.9, 41.8), ); final leftWallFixtureDef = FixtureDef(leftWall); - fixturesDefs.add(leftWallFixtureDef); - return fixturesDefs; + return [ + topWallFixtureDef, + topLeftCurveFixtureDef, + leftWallFixtureDef, + ]; } @override @@ -139,15 +139,21 @@ class _OuterBoundary extends BodyComponent with InitialPosition { } class _OuterBoundarySpriteComponent extends SpriteComponent with HasGameRef { + _OuterBoundarySpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(0, -7.8), + ); + @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.boundary.outer.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + Assets.images.boundary.outer.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(-0.2, -1.4); } } diff --git a/packages/pinball_components/lib/src/components/dash_animatronic.dart b/packages/pinball_components/lib/src/components/dash_animatronic.dart index 634c5574..f1c5b145 100644 --- a/packages/pinball_components/lib/src/components/dash_animatronic.dart +++ b/packages/pinball_components/lib/src/components/dash_animatronic.dart @@ -17,12 +17,12 @@ class DashAnimatronic extends SpriteAnimationComponent with HasGameRef { Future onLoad() async { await super.onLoad(); - final spriteSheet = await gameRef.images.load( + final spriteSheet = gameRef.images.fromCache( Assets.images.dash.animatronic.keyName, ); - const amountPerRow = 12; - const amountPerColumn = 8; + const amountPerRow = 13; + const amountPerColumn = 6; final textureSize = Vector2( spriteSheet.width / amountPerRow, spriteSheet.height / amountPerColumn, diff --git a/packages/pinball_components/lib/src/components/dino_walls.dart b/packages/pinball_components/lib/src/components/dino_walls.dart index 139e391b..9ce5a523 100644 --- a/packages/pinball_components/lib/src/components/dino_walls.dart +++ b/packages/pinball_components/lib/src/components/dino_walls.dart @@ -1,5 +1,3 @@ -// ignore_for_file: comment_references, avoid_renaming_method_parameters - import 'dart:async'; import 'package:flame/components.dart'; @@ -11,17 +9,15 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template dinowalls} /// A [Blueprint] which creates walls for the [ChromeDino]. /// {@endtemplate} -class DinoWalls extends Forge2DBlueprint { +class DinoWalls extends Blueprint { /// {@macro dinowalls} - DinoWalls(); - - @override - void build(_) { - addAll([ - _DinoTopWall(), - _DinoBottomWall(), - ]); - } + DinoWalls() + : super( + components: [ + _DinoTopWall(), + _DinoBottomWall(), + ], + ); } /// {@template dino_top_wall} diff --git a/packages/pinball_components/lib/src/components/flipper.dart b/packages/pinball_components/lib/src/components/flipper.dart index 1d0a3c5d..bd826668 100644 --- a/packages/pinball_components/lib/src/components/flipper.dart +++ b/packages/pinball_components/lib/src/components/flipper.dart @@ -60,7 +60,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { } List _createFixtureDefs() { - final fixturesDef = []; final direction = side.direction; final assetShadow = Flipper.size.x * 0.012 * -direction; @@ -77,7 +76,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { 0, ); final bigCircleFixtureDef = FixtureDef(bigCircleShape); - fixturesDef.add(bigCircleFixtureDef); final smallCircleShape = CircleShape()..radius = size.y * 0.23; smallCircleShape.position.setValues( @@ -87,7 +85,6 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { 0, ); final smallCircleFixtureDef = FixtureDef(smallCircleShape); - fixturesDef.add(smallCircleFixtureDef); final trapeziumVertices = side.isLeft ? [ @@ -108,9 +105,12 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { density: 50, // TODO(alestiago): Use a proper density. friction: .1, // TODO(alestiago): Use a proper friction. ); - fixturesDef.add(trapeziumFixtureDef); - return fixturesDef; + return [ + bigCircleFixtureDef, + smallCircleFixtureDef, + trapeziumFixtureDef, + ]; } @override @@ -136,21 +136,24 @@ class Flipper extends BodyComponent with KeyboardHandler, InitialPosition { } class _FlipperSpriteComponent extends SpriteComponent with HasGameRef { - _FlipperSpriteComponent({required BoardSide side}) : _side = side; + _FlipperSpriteComponent({required BoardSide side}) + : _side = side, + super(anchor: Anchor.center); final BoardSide _side; @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - (_side.isLeft) - ? Assets.images.flipper.left.keyName - : Assets.images.flipper.right.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + (_side.isLeft) + ? Assets.images.flipper.left.keyName + : Assets.images.flipper.right.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; } } diff --git a/packages/pinball_components/lib/src/components/launch_ramp.dart b/packages/pinball_components/lib/src/components/launch_ramp.dart index 2ac9ee91..baa54744 100644 --- a/packages/pinball_components/lib/src/components/launch_ramp.dart +++ b/packages/pinball_components/lib/src/components/launch_ramp.dart @@ -11,36 +11,23 @@ import 'package:pinball_flame/pinball_flame.dart'; /// A [Blueprint] which creates the [_LaunchRampBase] and /// [_LaunchRampForegroundRailing]. /// {@endtemplate} -class LaunchRamp extends Forge2DBlueprint { - @override - void build(_) { - addAllContactCallback([ - LayerSensorBallContactCallback<_LaunchRampExit>(), - ]); - - final launchRampBase = _LaunchRampBase(); - - final launchRampForegroundRailing = _LaunchRampForegroundRailing(); - - final launchRampExit = _LaunchRampExit(rotation: math.pi / 2) - ..initialPosition = Vector2(0.6, -34); - - final launchRampCloseWall = _LaunchRampCloseWall() - ..initialPosition = Vector2(4, -69.5); - - addAll([ - launchRampBase, - launchRampForegroundRailing, - launchRampExit, - launchRampCloseWall, - ]); - } +class LaunchRamp extends Blueprint { + /// {@macro launch_ramp} + LaunchRamp() + : super( + components: [ + _LaunchRampBase(), + _LaunchRampForegroundRailing(), + _LaunchRampExit()..initialPosition = Vector2(0.6, -34), + _LaunchRampCloseWall()..initialPosition = Vector2(4, -69.5), + ], + ); } /// {@template launch_ramp_base} /// Ramp the [Ball] is launched from at the beginning of each ball life. /// {@endtemplate} -class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { +class _LaunchRampBase extends BodyComponent with Layered { /// {@macro launch_ramp_base} _LaunchRampBase() : super( @@ -115,16 +102,18 @@ class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - - final body = world.createBody(bodyDef); + final body = world.createBody(BodyDef()); _createFixtureDefs().forEach(body.createFixture); return body; } + + @override + Future onLoad() async { + await super.onLoad(); + gameRef + .addContactCallback(LayerSensorBallContactCallback<_LaunchRampExit>()); + } } class _LaunchRampBaseSpriteComponent extends SpriteComponent with HasGameRef { @@ -162,7 +151,7 @@ class _LaunchRampBackgroundRailingSpriteComponent extends SpriteComponent /// Foreground railing for the [_LaunchRampBase] to render in front of the /// [Ball]. /// {@endtemplate} -class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition { +class _LaunchRampForegroundRailing extends BodyComponent { /// {@macro launch_ramp_foreground_railing} _LaunchRampForegroundRailing() : super( @@ -205,11 +194,7 @@ class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition { @override Body createBody() { - final bodyDef = BodyDef() - ..userData = this - ..position = initialPosition; - - final body = world.createBody(bodyDef); + final body = world.createBody(BodyDef()); _createFixtureDefs().forEach(body.createFixture); return body; @@ -258,10 +243,8 @@ class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered { /// {@endtemplate} class _LaunchRampExit extends LayerSensor { /// {@macro launch_ramp_exit} - _LaunchRampExit({ - required double rotation, - }) : _rotation = rotation, - super( + _LaunchRampExit() + : super( insideLayer: Layer.launcher, outsideLayer: Layer.board, orientation: LayerEntranceOrientation.down, @@ -272,8 +255,6 @@ class _LaunchRampExit extends LayerSensor { renderBody = false; } - final double _rotation; - static final Vector2 _size = Vector2(1.6, 0.1); @override @@ -282,6 +263,6 @@ class _LaunchRampExit extends LayerSensor { _size.x, _size.y, initialPosition, - _rotation, + math.pi / 2, ); } diff --git a/packages/pinball_components/lib/src/components/render_priority.dart b/packages/pinball_components/lib/src/components/render_priority.dart index 1b02326f..b179cce9 100644 --- a/packages/pinball_components/lib/src/components/render_priority.dart +++ b/packages/pinball_components/lib/src/components/render_priority.dart @@ -49,7 +49,7 @@ abstract class RenderPriority { static const int launchRamp = _above + outerBoudary; - static const int launchRampForegroundRailing = _above + ballOnLaunchRamp; + static const int launchRampForegroundRailing = _below + ballOnBoard; static const int plunger = _above + launchRamp; @@ -63,7 +63,7 @@ abstract class RenderPriority { static const int dinoBottomWall = _above + dino; - static const int slingshot = _above + ballOnBoard; + static const int slingshot = _above + dinoBottomWall; // Flutter Forest @@ -71,7 +71,7 @@ abstract class RenderPriority { static const int dashBumper = _above + ballOnBoard; - static const int dashAnimatronic = _above + launchRampForegroundRailing; + static const int dashAnimatronic = 2 * _above + launchRamp; // Sparky Fire Zone diff --git a/packages/pinball_components/lib/src/components/slingshot.dart b/packages/pinball_components/lib/src/components/slingshot.dart index 52cdb698..35346584 100644 --- a/packages/pinball_components/lib/src/components/slingshot.dart +++ b/packages/pinball_components/lib/src/components/slingshot.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -9,26 +7,23 @@ import 'package:pinball_flame/pinball_flame.dart'; /// A [Blueprint] which creates the pair of [Slingshot]s on the right side of /// the board. /// {@endtemplate} -class Slingshots extends Forge2DBlueprint { - @override - void build(_) { - final upperSlingshot = Slingshot( - length: 5.64, - angle: -0.017, - spritePath: Assets.images.slingshot.upper.keyName, - )..initialPosition = Vector2(22.3, -1.58); - - final lowerSlingshot = Slingshot( - length: 3.46, - angle: -0.468, - spritePath: Assets.images.slingshot.lower.keyName, - )..initialPosition = Vector2(24.7, 6.2); - - addAll([ - upperSlingshot, - lowerSlingshot, - ]); - } +class Slingshots extends Blueprint { + /// {@macro slingshots} + Slingshots() + : super( + components: [ + Slingshot( + length: 5.64, + angle: -0.017, + spritePath: Assets.images.slingshot.upper.keyName, + )..initialPosition = Vector2(22.3, -1.58), + Slingshot( + length: 3.46, + angle: -0.468, + spritePath: Assets.images.slingshot.lower.keyName, + )..initialPosition = Vector2(24.7, 6.2), + ], + ); } /// {@template slingshot} @@ -54,18 +49,15 @@ class Slingshot extends BodyComponent with InitialPosition { final double _angle; List _createFixtureDefs() { - final fixturesDef = []; const circleRadius = 1.55; final topCircleShape = CircleShape()..radius = circleRadius; 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); - fixturesDef.add(bottomCircleFixtureDef); final leftEdgeShape = EdgeShape() ..set( @@ -77,8 +69,6 @@ class Slingshot extends BodyComponent with InitialPosition { restitution: 5, ); - fixturesDef.add(leftEdgeShapeFixtureDef); - final rightEdgeShape = EdgeShape() ..set( Vector2(-circleRadius, _length / 2), @@ -88,9 +78,13 @@ class Slingshot extends BodyComponent with InitialPosition { rightEdgeShape, restitution: 5, ); - fixturesDef.add(rightEdgeShapeFixtureDef); - return fixturesDef; + return [ + topCircleFixtureDef, + bottomCircleFixtureDef, + leftEdgeShapeFixtureDef, + rightEdgeShapeFixtureDef, + ]; } @override @@ -123,7 +117,7 @@ class _SlinghsotSpriteComponent extends SpriteComponent with HasGameRef { @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite(_path); + final sprite = Sprite(gameRef.images.fromCache(_path)); this.sprite = sprite; size = sprite.originalSize / 10; } diff --git a/packages/pinball_components/lib/src/components/spaceship.dart b/packages/pinball_components/lib/src/components/spaceship.dart index b3d674dc..4ea4e05a 100644 --- a/packages/pinball_components/lib/src/components/spaceship.dart +++ b/packages/pinball_components/lib/src/components/spaceship.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'dart:async'; import 'dart:math'; @@ -12,38 +10,28 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship} /// A [Blueprint] which creates the spaceship feature. /// {@endtemplate} -class Spaceship extends Forge2DBlueprint { +class Spaceship extends Blueprint { /// {@macro spaceship} - Spaceship({required this.position}); + Spaceship({required Vector2 position}) + : super( + components: [ + SpaceshipSaucer()..initialPosition = position, + _SpaceshipEntrance()..initialPosition = position, + AndroidHead()..initialPosition = position, + _SpaceshipHole( + outsideLayer: Layer.spaceshipExitRail, + outsidePriority: RenderPriority.ballOnSpaceshipRail, + )..initialPosition = position - Vector2(5.2, -4.8), + _SpaceshipHole( + outsideLayer: Layer.board, + outsidePriority: RenderPriority.ballOnBoard, + )..initialPosition = position - Vector2(-7.2, -0.8), + SpaceshipWall()..initialPosition = position, + ], + ); /// Total size of the spaceship. static final size = Vector2(25, 19); - - /// The [position] where the elements will be created - final Vector2 position; - - @override - void build(_) { - addAllContactCallback([ - LayerSensorBallContactCallback<_SpaceshipEntrance>(), - LayerSensorBallContactCallback<_SpaceshipHole>(), - ]); - - addAll([ - SpaceshipSaucer()..initialPosition = position, - _SpaceshipEntrance()..initialPosition = position, - AndroidHead()..initialPosition = position, - _SpaceshipHole( - outsideLayer: Layer.spaceshipExitRail, - outsidePriority: RenderPriority.ballOnSpaceshipRail, - )..initialPosition = position - Vector2(5.2, -4.8), - _SpaceshipHole( - outsideLayer: Layer.board, - outsidePriority: RenderPriority.ballOnBoard, - )..initialPosition = position - Vector2(-7.2, -0.8), - SpaceshipWall()..initialPosition = position, - ]); - } } /// {@template spaceship_saucer} @@ -51,26 +39,28 @@ class Spaceship extends Forge2DBlueprint { /// {@endtemplate} class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { /// {@macro spaceship_saucer} - SpaceshipSaucer() : super(priority: RenderPriority.spaceshipSaucer) { + SpaceshipSaucer() + : super( + priority: RenderPriority.spaceshipSaucer, + children: [ + _SpaceshipSaucerSpriteComponent(), + ], + ) { layer = Layer.spaceship; + renderBody = false; } @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.spaceship.saucer.keyName, - ); - - await add( - SpriteComponent( - sprite: sprite, - size: Spaceship.size, - anchor: Anchor.center, - ), - ); - renderBody = false; + gameRef + ..addContactCallback( + LayerSensorBallContactCallback<_SpaceshipEntrance>(), + ) + ..addContactCallback( + LayerSensorBallContactCallback<_SpaceshipHole>(), + ); } @override @@ -89,6 +79,25 @@ class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered { } } +class _SpaceshipSaucerSpriteComponent extends SpriteComponent with HasGameRef { + _SpaceshipSaucerSpriteComponent() + : super( + anchor: Anchor.center, + // TODO(alestiago): Refactor to use sprite orignial size instead. + size: Spaceship.size, + ); + + @override + Future onLoad() async { + await super.onLoad(); + + // TODO(alestiago): Use cached sprite. + sprite = await gameRef.loadSprite( + Assets.images.spaceship.saucer.keyName, + ); + } +} + /// {@template spaceship_bridge} /// A [BodyComponent] that provides both the collision and the rotation /// animation for the bridge. diff --git a/packages/pinball_components/lib/src/components/spaceship_rail.dart b/packages/pinball_components/lib/src/components/spaceship_rail.dart index 49fdd475..1175384b 100644 --- a/packages/pinball_components/lib/src/components/spaceship_rail.dart +++ b/packages/pinball_components/lib/src/components/spaceship_rail.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'dart:math' as math; import 'package:flame/components.dart'; @@ -11,38 +9,30 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship_rail} /// A [Blueprint] for the spaceship drop tube. /// {@endtemplate} -class SpaceshipRail extends Forge2DBlueprint { +class SpaceshipRail extends Blueprint { /// {@macro spaceship_rail} - SpaceshipRail(); - - @override - void build(_) { - addAllContactCallback([ - LayerSensorBallContactCallback<_SpaceshipRailExit>(), - ]); - - final railRamp = _SpaceshipRailRamp(); - final railEnd = _SpaceshipRailExit(); - final topBase = _SpaceshipRailBase(radius: 0.55) - ..initialPosition = Vector2(-26.15, -18.65); - final bottomBase = _SpaceshipRailBase(radius: 0.8) - ..initialPosition = Vector2(-25.5, 12.9); - final railForeground = _SpaceshipRailForeground(); - - addAll([ - railRamp, - railEnd, - topBase, - bottomBase, - railForeground, - ]); - } + SpaceshipRail() + : super( + components: [ + _SpaceshipRailRamp(), + _SpaceshipRailExit(), + _SpaceshipRailBase(radius: 0.55) + ..initialPosition = Vector2(-26.15, -18.65), + _SpaceshipRailBase(radius: 0.8) + ..initialPosition = Vector2(-25.5, 12.9), + _SpaceshipRailForeground() + ], + ); } -/// Represents the spaceship drop rail from the [Spaceship]. -class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { - _SpaceshipRailRamp() : super(priority: RenderPriority.spaceshipRail) { +class _SpaceshipRailRamp extends BodyComponent with Layered { + _SpaceshipRailRamp() + : super( + priority: RenderPriority.spaceshipRail, + children: [_SpaceshipRailRampSpriteComponent()], + ) { layer = Layer.spaceshipExitRail; + renderBody = false; } List _createFixtureDefs() { @@ -120,23 +110,17 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - - final body = world.createBody(bodyDef); + final body = world.createBody(BodyDef()); _createFixtureDefs().forEach(body.createFixture); - return body; } @override Future onLoad() async { await super.onLoad(); - renderBody = false; - - await add(_SpaceshipRailRampSpriteComponent()); + gameRef.addContactCallback( + LayerSensorBallContactCallback<_SpaceshipRailExit>(), + ); } } @@ -188,7 +172,6 @@ class _SpaceshipRailBase extends BodyComponent with InitialPosition { final fixtureDef = FixtureDef(shape); final bodyDef = BodyDef( position: initialPosition, - userData: this, ); return world.createBody(bodyDef)..createFixture(fixtureDef); diff --git a/packages/pinball_components/lib/src/components/spaceship_ramp.dart b/packages/pinball_components/lib/src/components/spaceship_ramp.dart index d118115c..30211251 100644 --- a/packages/pinball_components/lib/src/components/spaceship_ramp.dart +++ b/packages/pinball_components/lib/src/components/spaceship_ramp.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_renaming_method_parameters - import 'dart:math' as math; import 'package:flame/components.dart'; @@ -12,62 +10,41 @@ import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship_ramp} /// A [Blueprint] which creates the ramp leading into the [Spaceship]. /// {@endtemplate} -class SpaceshipRamp extends Forge2DBlueprint { +class SpaceshipRamp extends Blueprint { /// {@macro spaceship_ramp} - SpaceshipRamp(); - - /// [SpriteGroupComponent] representing the arrow that lights up. - @visibleForTesting - late final SpaceshipRampArrowSpriteComponent spaceshipRampArrow; + SpaceshipRamp() + : super( + components: [ + _SpaceshipRampOpening( + outsidePriority: RenderPriority.ballOnBoard, + rotation: math.pi, + ) + ..initialPosition = Vector2(1.7, -19.8) + ..layer = Layer.opening, + _SpaceshipRampOpening( + outsideLayer: Layer.spaceship, + outsidePriority: RenderPriority.ballOnSpaceship, + rotation: math.pi, + ) + ..initialPosition = Vector2(-13.7, -18.6) + ..layer = Layer.spaceshipEntranceRamp, + _SpaceshipRampBackground(), + _SpaceshipRampBoardOpeningSpriteComponent() + ..position = Vector2(3.4, -39.5), + _SpaceshipRampForegroundRailing(), + _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20), + _SpaceshipRampBackgroundRailingSpriteComponent(), + _SpaceshipRampArrowSpriteComponent(), + ], + ); /// Forwards the sprite to the next [SpaceshipRampArrowSpriteState]. /// /// If the current state is the last one it cycles back to the initial state. - void progress() => spaceshipRampArrow.progress(); - - @override - void build(_) { - addAllContactCallback([ - LayerSensorBallContactCallback<_SpaceshipRampOpening>(), - ]); - - final rightOpening = _SpaceshipRampOpening( - outsidePriority: RenderPriority.ballOnBoard, - rotation: -5 * math.pi / 180, - ) - ..initialPosition = Vector2(1.7, -19.12) - ..layer = Layer.opening; - final leftOpening = _SpaceshipRampOpening( - outsideLayer: Layer.spaceship, - outsidePriority: RenderPriority.ballOnSpaceship, - rotation: -5 * math.pi / 180, - ) - ..initialPosition = Vector2(-13.7, -19) - ..layer = Layer.spaceshipEntranceRamp; - - final spaceshipRamp = _SpaceshipRampBackground(); - - spaceshipRampArrow = SpaceshipRampArrowSpriteComponent(); - - final spaceshipRampBoardOpeningSprite = - _SpaceshipRampBoardOpeningSpriteComponent() - ..position = Vector2(3.4, -39.5); - - final spaceshipRampForegroundRailing = _SpaceshipRampForegroundRailing(); - - final baseRight = _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20); - - addAll([ - spaceshipRampBoardOpeningSprite, - rightOpening, - leftOpening, - baseRight, - _SpaceshipRampBackgroundRailingSpriteComponent(), - spaceshipRamp, - spaceshipRampArrow, - spaceshipRampForegroundRailing, - ]); - } + void progress() => components + .whereType<_SpaceshipRampArrowSpriteComponent>() + .first + .progress(); } /// Indicates the state of the arrow on the [SpaceshipRamp]. @@ -133,8 +110,6 @@ class _SpaceshipRampBackground extends BodyComponent static const width = 5.0; List _createFixtureDefs() { - final fixturesDef = []; - final outerLeftCurveShape = BezierCurveShape( controlPoints: [ Vector2(-30.75, -37.3), @@ -142,9 +117,6 @@ class _SpaceshipRampBackground extends BodyComponent Vector2(-14.2, -71.25), ], ); - final outerLeftCurveFixtureDef = FixtureDef(outerLeftCurveShape); - fixturesDef.add(outerLeftCurveFixtureDef); - final outerRightCurveShape = BezierCurveShape( controlPoints: [ outerLeftCurveShape.vertices.last, @@ -152,32 +124,35 @@ class _SpaceshipRampBackground extends BodyComponent Vector2(6.1, -44.9), ], ); - final outerRightCurveFixtureDef = FixtureDef(outerRightCurveShape); - fixturesDef.add(outerRightCurveFixtureDef); - final boardOpeningEdgeShape = EdgeShape() ..set( outerRightCurveShape.vertices.last, Vector2(7.3, -41.1), ); - final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape); - fixturesDef.add(boardOpeningEdgeShapeFixtureDef); - return fixturesDef; + return [ + FixtureDef(outerRightCurveShape), + FixtureDef(outerLeftCurveShape), + FixtureDef(boardOpeningEdgeShape), + ]; } @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - + final bodyDef = BodyDef(position: initialPosition); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); return body; } + + @override + Future onLoad() async { + await super.onLoad(); + gameRef.addContactCallback( + LayerSensorBallContactCallback<_SpaceshipRampOpening>(), + ); + } } class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent @@ -188,6 +163,7 @@ class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent position: Vector2(-11.7, -54.3), priority: RenderPriority.spaceshipRampBackgroundRailing, ); + @override Future onLoad() async { await super.onLoad(); @@ -203,6 +179,12 @@ class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent with HasGameRef { + _SpaceshipRampBackgroundRampSpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(-10.7, -53.6), + ); + @override Future onLoad() async { await super.onLoad(); @@ -213,22 +195,19 @@ class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(-10.7, -53.6); } } /// {@template spaceship_ramp_arrow_sprite_component} /// An arrow inside [SpaceshipRamp]. /// -/// Lights up a each dash whenever a [Ball] gets into [SpaceshipRamp]. +/// Lights progressively whenever a [Ball] gets into [SpaceshipRamp]. /// {@endtemplate} -@visibleForTesting -class SpaceshipRampArrowSpriteComponent +class _SpaceshipRampArrowSpriteComponent extends SpriteGroupComponent with HasGameRef { /// {@macro spaceship_ramp_arrow_sprite_component} - SpaceshipRampArrowSpriteComponent() + _SpaceshipRampArrowSpriteComponent() : super( anchor: Anchor.center, position: Vector2(-3.9, -56.5), @@ -256,6 +235,8 @@ class SpaceshipRampArrowSpriteComponent class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent with HasGameRef { + _SpaceshipRampBoardOpeningSpriteComponent() : super(anchor: Anchor.center); + @override Future onLoad() async { await super.onLoad(); @@ -266,7 +247,6 @@ class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; } } @@ -282,8 +262,6 @@ class _SpaceshipRampForegroundRailing extends BodyComponent } List _createFixtureDefs() { - final fixturesDef = []; - final innerLeftCurveShape = BezierCurveShape( controlPoints: [ Vector2(-24.5, -38), @@ -291,10 +269,6 @@ class _SpaceshipRampForegroundRailing extends BodyComponent Vector2(-13.8, -64.5), ], ); - - final innerLeftCurveFixtureDef = FixtureDef(innerLeftCurveShape); - fixturesDef.add(innerLeftCurveFixtureDef); - final innerRightCurveShape = BezierCurveShape( controlPoints: [ innerLeftCurveShape.vertices.last, @@ -302,28 +276,22 @@ class _SpaceshipRampForegroundRailing extends BodyComponent Vector2(0, -44.5), ], ); - - final innerRightCurveFixtureDef = FixtureDef(innerRightCurveShape); - fixturesDef.add(innerRightCurveFixtureDef); - final boardOpeningEdgeShape = EdgeShape() ..set( innerRightCurveShape.vertices.last, Vector2(-0.85, -40.8), ); - final boardOpeningEdgeShapeFixtureDef = FixtureDef(boardOpeningEdgeShape); - fixturesDef.add(boardOpeningEdgeShapeFixtureDef); - return fixturesDef; + return [ + FixtureDef(innerLeftCurveShape), + FixtureDef(innerRightCurveShape), + FixtureDef(boardOpeningEdgeShape), + ]; } @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - + final bodyDef = BodyDef(position: initialPosition); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); @@ -372,10 +340,7 @@ class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered { ], ); final fixtureDef = FixtureDef(baseShape); - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); + final bodyDef = BodyDef(position: initialPosition); return world.createBody(bodyDef)..createFixture(fixtureDef); } diff --git a/packages/pinball_components/lib/src/components/sparky_computer.dart b/packages/pinball_components/lib/src/components/sparky_computer.dart index 427847ae..4cec4c59 100644 --- a/packages/pinball_components/lib/src/components/sparky_computer.dart +++ b/packages/pinball_components/lib/src/components/sparky_computer.dart @@ -6,75 +6,64 @@ import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; /// {@template sparky_computer} -/// A [Blueprint] which creates the [_ComputerBase] and -/// [_ComputerTopSpriteComponent]. +/// A computer owned by Sparky. +/// +/// Register a [ContactCallback] for [SparkyComputerSensor] to listen when +/// something enters the [SparkyComputer]. /// {@endtemplate} -class SparkyComputer extends Forge2DBlueprint { - @override - void build(_) { - final computerBase = _ComputerBase(); - final computerTop = _ComputerTopSpriteComponent(); - - addAll([ - computerBase, - computerTop, - ]); - } +class SparkyComputer extends Blueprint { + /// {@macro sparky_computer} + SparkyComputer() + : super( + components: [ + _ComputerBase(), + _ComputerTopSpriteComponent(), + SparkyComputerSensor(), + ], + ); } class _ComputerBase extends BodyComponent with InitialPosition { - _ComputerBase() : super(priority: RenderPriority.computerBase); + _ComputerBase() + : super( + priority: RenderPriority.computerBase, + children: [_ComputerBaseSpriteComponent()], + ) { + renderBody = false; + } List _createFixtureDefs() { - final fixturesDef = []; - final leftEdge = EdgeShape() ..set( 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), ); - final topEdgeFixtureDef = FixtureDef(topEdge); - fixturesDef.add(topEdgeFixtureDef); - final rightEdge = EdgeShape() ..set( Vector2(-10.7, -50.6), Vector2(-9, -47.2), ); - final rightEdgeFixtureDef = FixtureDef(rightEdge); - fixturesDef.add(rightEdgeFixtureDef); - return fixturesDef; + return [ + FixtureDef(leftEdge), + FixtureDef(topEdge), + FixtureDef(rightEdge), + ]; } @override Body createBody() { - final bodyDef = BodyDef( - position: initialPosition, - userData: this, - ); - + final bodyDef = BodyDef(position: initialPosition); final body = world.createBody(bodyDef); _createFixtureDefs().forEach(body.createFixture); return body; } - - @override - Future onLoad() async { - await super.onLoad(); - renderBody = false; - - await add(_ComputerBaseSpriteComponent()); - } } class _ComputerBaseSpriteComponent extends SpriteComponent with HasGameRef { @@ -115,3 +104,24 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef { size = sprite.originalSize / 10; } } + +/// {@template sparky_computer_sensor} +/// Small sensor body used to detect when a ball has entered the +/// [SparkyComputer]. +/// {@endtemplate} +class SparkyComputerSensor extends BodyComponent with InitialPosition { + /// {@macro sparky_computer_sensor} + SparkyComputerSensor() { + renderBody = false; + } + + @override + Body createBody() { + final shape = CircleShape()..radius = 0.1; + 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/pubspec.yaml b/packages/pinball_components/pubspec.yaml index a14c5296..7df3429a 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -16,6 +16,8 @@ dependencies: intl: ^0.17.0 pinball_flame: path: ../pinball_flame + pinball_theme: + path: ../pinball_theme dev_dependencies: diff --git a/packages/pinball_components/sandbox/lib/stories/backboard/backboard_game_over_game.dart b/packages/pinball_components/sandbox/lib/stories/backboard/backboard_game_over_game.dart new file mode 100644 index 00000000..691b1656 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/backboard/backboard_game_over_game.dart @@ -0,0 +1,51 @@ +import 'package:flame/components.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; +import 'package:pinball_theme/pinball_theme.dart'; +import 'package:sandbox/common/common.dart'; + +class BackboardGameOverGame extends BasicKeyboardGame { + BackboardGameOverGame(this.score, this.character); + + static const info = ''' + Simple example showing the game over mode of the backboard. + + - Select a character to update the character icon. + '''; + + final int score; + final String character; + + static final characterIconPaths = { + 'Dash': Assets.images.dash.leaderboardIcon.keyName, + 'Sparky': Assets.images.sparky.leaderboardIcon.keyName, + 'Android': Assets.images.android.leaderboardIcon.keyName, + 'Dino': Assets.images.dino.leaderboardIcon.keyName, + }; + + @override + Future onLoad() async { + camera + ..followVector2(Vector2.zero()) + ..zoom = 5; + + await images.loadAll(characterIconPaths.values.toList()); + + await add( + Backboard.gameOver( + position: Vector2(0, 20), + score: score, + characterIconPath: characterIconPaths[character]!, + 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/waiting.dart b/packages/pinball_components/sandbox/lib/stories/backboard/backboard_waiting_game.dart similarity index 100% rename from packages/pinball_components/sandbox/lib/stories/backboard/waiting.dart rename to packages/pinball_components/sandbox/lib/stories/backboard/backboard_waiting_game.dart diff --git a/packages/pinball_components/sandbox/lib/stories/backboard/game_over.dart b/packages/pinball_components/sandbox/lib/stories/backboard/game_over.dart deleted file mode 100644 index a513276f..00000000 --- a/packages/pinball_components/sandbox/lib/stories/backboard/game_over.dart +++ /dev/null @@ -1,37 +0,0 @@ -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 index f85e6685..cc05ba4f 100644 --- a/packages/pinball_components/sandbox/lib/stories/backboard/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/backboard/stories.dart @@ -1,8 +1,8 @@ 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'; +import 'package:sandbox/stories/backboard/backboard_game_over_game.dart'; +import 'package:sandbox/stories/backboard/backboard_waiting_game.dart'; void addBackboardStories(Dashbook dashbook) { dashbook.storiesOf('Backboard') @@ -18,7 +18,12 @@ void addBackboardStories(Dashbook dashbook) { 'Game over', (context) => GameWidget( game: BackboardGameOverGame( - context.numberProperty('score', 9000000000).toInt(), + context.numberProperty('Score', 9000000000).toInt(), + context.listProperty( + 'Character', + BackboardGameOverGame.characterIconPaths.keys.first, + BackboardGameOverGame.characterIconPaths.keys.toList(), + ), ), ), codeLink: buildSourceLink('backboard/game_over.dart'), diff --git a/packages/pinball_components/sandbox/lib/stories/baseboard/baseboard_game.dart b/packages/pinball_components/sandbox/lib/stories/baseboard/baseboard_game.dart new file mode 100644 index 00000000..3dab5c6f --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/baseboard/baseboard_game.dart @@ -0,0 +1,38 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class BaseboardGame extends BasicBallGame with Traceable { + static const info = ''' + Shows how the Baseboards are rendered. + + - Activate the "trace" parameter to overlay the body. + - Tap anywhere on the screen to spawn a ball into the game. +'''; + + @override + Future onLoad() async { + await super.onLoad(); + + await images.loadAll([ + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + ]); + + final center = screenToWorld(camera.viewport.canvasSize! / 2); + final leftBaseboard = Baseboard(side: BoardSide.left) + ..initialPosition = center - Vector2(25, 0) + ..priority = 1; + final rightBaseboard = Baseboard(side: BoardSide.right) + ..initialPosition = center + Vector2(25, 0) + ..priority = 1; + + await addAll([ + leftBaseboard, + rightBaseboard, + ]); + + await traceAllBodies(); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart b/packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart deleted file mode 100644 index 0650fa13..00000000 --- a/packages/pinball_components/sandbox/lib/stories/baseboard/basic_baseboard_game.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:pinball_components/pinball_components.dart'; -import 'package:sandbox/common/common.dart'; - -class BasicBaseboardGame extends BasicGame { - static const info = 'Shows how a Baseboard works.'; - - @override - Future onLoad() async { - await super.onLoad(); - - final center = screenToWorld(camera.viewport.canvasSize! / 2); - - final leftBaseboard = Baseboard(side: BoardSide.left) - ..initialPosition = center - Vector2(25, 0); - final rightBaseboard = Baseboard(side: BoardSide.right) - ..initialPosition = center + Vector2(25, 0); - - await addAll([ - leftBaseboard, - rightBaseboard, - ]); - } -} diff --git a/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart b/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart index b3982af4..2e4d3090 100644 --- a/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/baseboard/stories.dart @@ -1,15 +1,15 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/baseboard/basic_baseboard_game.dart'; +import 'package:sandbox/stories/baseboard/baseboard_game.dart'; void addBaseboardStories(Dashbook dashbook) { dashbook.storiesOf('Baseboard').add( 'Basic', (context) => GameWidget( - game: BasicBaseboardGame(), + game: BaseboardGame()..trace = context.boolProperty('Trace', true), ), - codeLink: buildSourceLink('baseboard/basic.dart'), - info: BasicBaseboardGame.info, + codeLink: buildSourceLink('baseboard_game/basic.dart'), + info: BaseboardGame.info, ); } 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 ab984045..ca7c8cc8 100644 --- a/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/boundaries/boundaries_game.dart @@ -16,6 +16,11 @@ class BoundariesGame extends BasicBallGame with Traceable { Future onLoad() async { await super.onLoad(); + await images.loadAll([ + Assets.images.boundary.outer.keyName, + Assets.images.boundary.bottom.keyName, + ]); + await addFromBlueprint(Boundaries()); await ready(); diff --git a/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart b/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart index c0dfee24..40a638b9 100644 --- a/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/flipper/flipper_game.dart @@ -33,6 +33,11 @@ class FlipperGame extends BasicBallGame with KeyboardEvents, Traceable { Future onLoad() async { await super.onLoad(); + await images.loadAll([ + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + ]); + final center = screenToWorld(camera.viewport.canvasSize! / 2); leftFlipper = Flipper(side: BoardSide.left) 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 7d941099..a0f54ea4 100644 --- a/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/slingshot/slingshot_game.dart @@ -15,6 +15,12 @@ class SlingshotGame extends BasicBallGame with Traceable { @override Future onLoad() async { await super.onLoad(); + + await images.loadAll([ + Assets.images.slingshot.upper.keyName, + Assets.images.slingshot.lower.keyName, + ]); + await addFromBlueprint(Slingshots()); camera.followVector2(Vector2.zero()); await traceAllBodies(); diff --git a/packages/pinball_components/sandbox/pubspec.lock b/packages/pinball_components/sandbox/pubspec.lock index 7f78e365..d7ab8901 100644 --- a/packages/pinball_components/sandbox/pubspec.lock +++ b/packages/pinball_components/sandbox/pubspec.lock @@ -64,6 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + equatable: + dependency: transitive + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" fake_async: dependency: transitive description: @@ -247,6 +254,13 @@ packages: relative: true source: path version: "1.0.0+1" + pinball_theme: + dependency: "direct main" + description: + path: "../../pinball_theme" + 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 03a46ee0..dd9f8259 100644 --- a/packages/pinball_components/sandbox/pubspec.yaml +++ b/packages/pinball_components/sandbox/pubspec.yaml @@ -16,6 +16,8 @@ dependencies: path: ../ pinball_flame: path: ../../pinball_flame + pinball_theme: + path: ../../pinball_theme 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 1faf75e8..1f8b9ee6 100644 --- a/packages/pinball_components/test/helpers/test_game.dart +++ b/packages/pinball_components/test/helpers/test_game.dart @@ -17,4 +17,6 @@ class TestGame extends Forge2DGame { } } -class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents {} +class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents { + KeyboardTestGame([List? assets]) : super(assets); +} diff --git a/packages/pinball_components/test/src/components/backboard_test.dart b/packages/pinball_components/test/src/components/backboard_test.dart index 5e868bfc..aee2481a 100644 --- a/packages/pinball_components/test/src/components/backboard_test.dart +++ b/packages/pinball_components/test/src/components/backboard_test.dart @@ -5,13 +5,15 @@ 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'; +import 'package:pinball_components/pinball_components.dart' hide Assets; +import 'package:pinball_theme/pinball_theme.dart'; import '../../helpers/helpers.dart'; void main() { group('Backboard', () { - final tester = FlameTester(KeyboardTestGame.new); + final characterIconPath = Assets.images.dash.leaderboardIcon.keyName; + final tester = FlameTester(() => KeyboardTestGame([characterIconPath])); group('on waitingMode', () { tester.testGameWidget( @@ -20,6 +22,7 @@ void main() { game.camera.zoom = 2; game.camera.followVector2(Vector2.zero()); await game.ensureAdd(Backboard.waiting(position: Vector2(0, 15))); + await tester.pump(); }, verify: (game, tester) async { await expectLater( @@ -39,6 +42,7 @@ void main() { final backboard = Backboard.gameOver( position: Vector2(0, 15), score: 1000, + characterIconPath: characterIconPath, onSubmit: (_) {}, ); await game.ensureAdd(backboard); @@ -52,7 +56,6 @@ void main() { (component) => component is TextComponent && component.text == '1,000', ); - expect(score, isNotNull); }, ); @@ -63,6 +66,7 @@ void main() { final backboard = Backboard.gameOver( position: Vector2(0, 15), score: 1000, + characterIconPath: characterIconPath, onSubmit: (_) {}, ); await game.ensureAdd(backboard); @@ -106,6 +110,7 @@ void main() { final backboard = Backboard.gameOver( position: Vector2(0, 15), score: 1000, + characterIconPath: characterIconPath, onSubmit: (value) { submitedInitials = value; }, diff --git a/packages/pinball_components/test/src/components/baseboard_test.dart b/packages/pinball_components/test/src/components/baseboard_test.dart index 82a6475e..101e3e21 100644 --- a/packages/pinball_components/test/src/components/baseboard_test.dart +++ b/packages/pinball_components/test/src/components/baseboard_test.dart @@ -10,11 +10,16 @@ import '../../helpers/helpers.dart'; void main() { group('Baseboard', () { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final assets = [ + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); final leftBaseboard = Baseboard( side: BoardSide.left, )..initialPosition = Vector2(-20, 0); @@ -24,6 +29,7 @@ void main() { await game.ensureAddAll([leftBaseboard, rightBaseboard]); game.camera.followVector2(Vector2.zero()); + await tester.pump(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/boundaries_test.dart b/packages/pinball_components/test/src/components/boundaries_test.dart index e62d63e3..9affe0b7 100644 --- a/packages/pinball_components/test/src/components/boundaries_test.dart +++ b/packages/pinball_components/test/src/components/boundaries_test.dart @@ -10,15 +10,20 @@ import '../../helpers/helpers.dart'; void main() { group('Boundaries', () { + final assets = [ + Assets.images.boundary.outer.keyName, + Assets.images.boundary.bottom.keyName, + ]; final tester = FlameTester(TestGame.new); tester.testGameWidget( 'render correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); await game.addFromBlueprint(Boundaries()); game.camera.followVector2(Vector2.zero()); - game.camera.zoom = 3.9; - await game.ready(); + game.camera.zoom = 3.2; + await tester.pump(); }, verify: (game, tester) async { await expectLater( 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 e4b31ca6..8b5d0e30 100644 --- a/packages/pinball_components/test/src/components/dash_animatronic_test.dart +++ b/packages/pinball_components/test/src/components/dash_animatronic_test.dart @@ -9,29 +9,33 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final asset = Assets.images.dash.animatronic.keyName; + final flameTester = FlameTester(() => TestGame([asset])); group('DashAnimatronic', () { flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.load(asset); await game.ensureAdd(DashAnimatronic()..playing = true); game.camera.followVector2(Vector2.zero()); + await tester.pump(); }, verify: (game, tester) async { + const animationDuration = 3.25; await expectLater( find.byGame(), matchesGoldenFile('golden/dash_animatronic/start.png'), ); - game.update(1); + game.update(animationDuration * 0.25); await tester.pump(); await expectLater( find.byGame(), matchesGoldenFile('golden/dash_animatronic/middle.png'), ); - game.update(4); + game.update(animationDuration * 0.75); await tester.pump(); await expectLater( find.byGame(), diff --git a/packages/pinball_components/test/src/components/flipper_test.dart b/packages/pinball_components/test/src/components/flipper_test.dart index f413fade..c34d0d1c 100644 --- a/packages/pinball_components/test/src/components/flipper_test.dart +++ b/packages/pinball_components/test/src/components/flipper_test.dart @@ -10,7 +10,11 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final assets = [ + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); group('Flipper', () { // TODO(alestiago): Consider testing always both left and right Flipper. @@ -18,6 +22,7 @@ void main() { flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); final leftFlipper = Flipper( side: BoardSide.left, )..initialPosition = Vector2(-10, 0); @@ -27,6 +32,7 @@ void main() { await game.ensureAddAll([leftFlipper, rightFlipper]); game.camera.followVector2(Vector2.zero()); + await tester.pump(); }, verify: (game, tester) async { await expectLater( diff --git a/packages/pinball_components/test/src/components/golden/baseboard.png b/packages/pinball_components/test/src/components/golden/baseboard.png index df83fa4c..01141551 100644 Binary files a/packages/pinball_components/test/src/components/golden/baseboard.png and b/packages/pinball_components/test/src/components/golden/baseboard.png differ diff --git a/packages/pinball_components/test/src/components/golden/boundaries.png b/packages/pinball_components/test/src/components/golden/boundaries.png index 4ac8f5eb..ecb396c4 100644 Binary files a/packages/pinball_components/test/src/components/golden/boundaries.png and b/packages/pinball_components/test/src/components/golden/boundaries.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 index 8c99f674..3e5e91f5 100644 Binary files a/packages/pinball_components/test/src/components/golden/dash_animatronic/end.png 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 index b2bf16a3..9e79695a 100644 Binary files a/packages/pinball_components/test/src/components/golden/dash_animatronic/middle.png 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 index 8c99f674..3e5e91f5 100644 Binary files a/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png and b/packages/pinball_components/test/src/components/golden/dash_animatronic/start.png differ diff --git a/packages/pinball_components/test/src/components/golden/flipper.png b/packages/pinball_components/test/src/components/golden/flipper.png index 79b7ca5b..07fe81ed 100644 Binary files a/packages/pinball_components/test/src/components/golden/flipper.png and b/packages/pinball_components/test/src/components/golden/flipper.png differ diff --git a/packages/pinball_components/test/src/components/golden/slingshots.png b/packages/pinball_components/test/src/components/golden/slingshots.png index 2cbb1f5c..d0b01f76 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/slingshot_test.dart b/packages/pinball_components/test/src/components/slingshot_test.dart index a5535750..69296f78 100644 --- a/packages/pinball_components/test/src/components/slingshot_test.dart +++ b/packages/pinball_components/test/src/components/slingshot_test.dart @@ -10,17 +10,22 @@ import '../../helpers/helpers.dart'; void main() { group('Slingshot', () { - final flameTester = FlameTester(TestGame.new); + final assets = [ + Assets.images.slingshot.upper.keyName, + Assets.images.slingshot.lower.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); const length = 2.0; const angle = 0.0; - final spritePath = Assets.images.slingshot.upper.keyName; flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); await game.addFromBlueprint(Slingshots()); game.camera.followVector2(Vector2.zero()); await game.ready(); + await tester.pump(); }, verify: (game, tester) async { await expectLater( @@ -36,7 +41,7 @@ void main() { final slingshot = Slingshot( length: length, angle: angle, - spritePath: spritePath, + spritePath: assets.first, ); await game.ensureAdd(slingshot); @@ -50,7 +55,7 @@ void main() { final slingshot = Slingshot( length: length, angle: angle, - spritePath: spritePath, + spritePath: assets.first, ); await game.ensureAdd(slingshot); @@ -64,7 +69,7 @@ void main() { final slingshot = Slingshot( length: length, angle: angle, - spritePath: spritePath, + spritePath: assets.first, ); await game.ensureAdd(slingshot); @@ -82,7 +87,7 @@ void main() { final slingshot = Slingshot( length: length, angle: angle, - spritePath: spritePath, + spritePath: assets.first, ); await game.ensureAdd(slingshot); 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 c73c8dee..d3242ff6 100644 --- a/packages/pinball_components/test/src/components/spaceship_rail_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_rail_test.dart @@ -17,6 +17,8 @@ void main() { 'renders correctly', setUp: (game, tester) async { await game.addFromBlueprint(SpaceshipRail()); + await game.ready(); + game.camera.followVector2(Vector2.zero()); game.camera.zoom = 8; }, 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 4e5d149b..98a6e95f 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp_test.dart @@ -1,7 +1,6 @@ // ignore_for_file: cascade_invocations import 'package:flame/components.dart'; -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'; @@ -37,6 +36,7 @@ void main() { ); group('renders correctly', () { + const goldenFilePath = 'golden/spaceship_ramp/'; final centerForSpaceshipRamp = Vector2(-13, -55); flameTester.testGameWidget( @@ -49,7 +49,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.inactive, ); @@ -58,7 +61,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/inactive.png'), + matchesGoldenFile('${goldenFilePath}inactive.png'), ); }, ); @@ -74,7 +77,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active1, ); @@ -83,7 +89,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active1.png'), + matchesGoldenFile('${goldenFilePath}active1.png'), ); }, ); @@ -101,7 +107,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active2, ); @@ -110,7 +119,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active2.png'), + matchesGoldenFile('${goldenFilePath}active2.png'), ); }, ); @@ -129,7 +138,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active3, ); @@ -138,7 +150,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active3.png'), + matchesGoldenFile('${goldenFilePath}active3.png'), ); }, ); @@ -158,7 +170,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active4, ); @@ -167,7 +182,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active4.png'), + matchesGoldenFile('${goldenFilePath}active4.png'), ); }, ); @@ -188,7 +203,10 @@ void main() { await tester.pump(); expect( - spaceshipRamp.spaceshipRampArrow.current, + spaceshipRamp.components + .whereType() + .first + .current, SpaceshipRampArrowSpriteState.active5, ); @@ -197,7 +215,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship_ramp/active5.png'), + matchesGoldenFile('${goldenFilePath}active5.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 3d67106d..6b19481e 100644 --- a/packages/pinball_components/test/src/components/sparky_computer_test.dart +++ b/packages/pinball_components/test/src/components/sparky_computer_test.dart @@ -16,6 +16,8 @@ void main() { 'renders correctly', setUp: (game, tester) async { await game.addFromBlueprint(SparkyComputer()); + await game.ready(); + game.camera.followVector2(Vector2(-15, -50)); }, verify: (game, tester) async { diff --git a/packages/pinball_flame/lib/src/blueprint.dart b/packages/pinball_flame/lib/src/blueprint.dart index 5c2df683..de9c8b2c 100644 --- a/packages/pinball_flame/lib/src/blueprint.dart +++ b/packages/pinball_flame/lib/src/blueprint.dart @@ -1,101 +1,49 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/foundation.dart'; - -const _attachedErrorMessage = "Can't add to attached Blueprints"; // TODO(erickzanardo): Keeping this inside our code base // so we can experiment with the idea, but this is a // potential upstream change on Flame. -/// A [Blueprint] is a virtual way of grouping [Component]s -/// that are related, but they need to be added directly on -/// the [FlameGame] level. +/// {@template blueprint} +/// A [Blueprint] is a virtual way of grouping [Component]s that are related, +/// but they need to be added directly on the [FlameGame] level. +/// {@endtemplate blueprint} // TODO(alestiago): refactor with feat/make-blueprint-extend-component. -abstract class Blueprint extends Component { - final List _components = []; - final List _blueprints = []; - - bool _isAttached = false; - - /// Called before the the [Component]s managed - /// by this blueprint is added to the [FlameGame] - void build(T gameRef); - - /// Attach the [Component]s built on [build] to the [game] - /// instance - @mustCallSuper - Future attach(T game) async { - build(game); - await Future.wait([ - game.addAll(_components), - ..._blueprints.map(game.addFromBlueprint).toList(), - ]); - _isAttached = true; +class Blueprint extends Component { + /// {@macro blueprint} + Blueprint({ + Iterable? components, + Iterable? blueprints, + }) { + if (components != null) _components.addAll(components); + if (blueprints != null) { + _blueprints.addAll(blueprints); + for (final blueprint in blueprints) { + _components.addAll(blueprint.components); + } + } } - /// Adds a single [Component] to this blueprint. - @override - Future add(Component component) async { - assert(!_isAttached, _attachedErrorMessage); - _components.add(component); - } + final List _components = []; - /// Adds a list of [Blueprint]s to this blueprint. - void addAllBlueprints(List blueprints) { - assert(!_isAttached, _attachedErrorMessage); - _blueprints.addAll(blueprints); - } + final List _blueprints = []; - /// Adds a single [Blueprint] to this blueprint. - void addBlueprint(Blueprint blueprint) { - assert(!_isAttached, _attachedErrorMessage); - _blueprints.add(blueprint); + Future _addToParent(Component parent) async { + await parent.addAll(_components); } - /// Returns a copy of the components built by this blueprint + /// Returns a copy of the components built by this blueprint. List get components => List.unmodifiable(_components); - /// Returns a copy of the children blueprints - List get blueprints => List.unmodifiable(_blueprints); -} - -/// A [Blueprint] that provides additional -/// structures specific to flame_forge2d -abstract class Forge2DBlueprint extends Blueprint { - final List _callbacks = []; - - /// Adds a single [ContactCallback] to this blueprint - void addContactCallback(ContactCallback callback) { - assert(!_isAttached, _attachedErrorMessage); - _callbacks.add(callback); - } - - /// Adds a collection of [ContactCallback]s to this blueprint - void addAllContactCallback(List callbacks) { - assert(!_isAttached, _attachedErrorMessage); - _callbacks.addAll(callbacks); - } - - @override - Future attach(Forge2DGame game) async { - await super.attach(game); - - for (final callback in _callbacks) { - game.addContactCallback(callback); - } - } - - /// Returns a copy of the callbacks built by this blueprint - List get callbacks => List.unmodifiable(_callbacks); + /// Returns a copy of the blueprints built by this blueprint. + List get blueprints => List.unmodifiable(_blueprints); } -/// Adds helper methods regardin [Blueprint]s to [FlameGame] -extension FlameGameBlueprint on FlameGame { - /// Shortcut to attach a [Blueprint] instance to this game - /// equivalent to `MyBluepinrt().attach(game)` +/// Adds helper methods regarding [Blueprint]s to [FlameGame]. +extension FlameGameBlueprint on Component { + /// Shortcut to add a [Blueprint]s components to its parent. Future addFromBlueprint(Blueprint blueprint) async { - await blueprint.attach(this); + await blueprint._addToParent(this); } } diff --git a/packages/pinball_flame/test/src/blueprint_test.dart b/packages/pinball_flame/test/src/blueprint_test.dart index f1c58dc9..402d5059 100644 --- a/packages/pinball_flame/test/src/blueprint_test.dart +++ b/packages/pinball_flame/test/src/blueprint_test.dart @@ -1,138 +1,86 @@ +// ignore_for_file: cascade_invocations + import 'package:flame/components.dart'; -import 'package:flame_forge2d/contact_callbacks.dart'; +import 'package:flame/game.dart'; +import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; import 'package:pinball_flame/pinball_flame.dart'; -import '../helpers/helpers.dart'; - -class TestContactCallback extends ContactCallback {} - -class MyBlueprint extends Blueprint { - @override - void build(_) { - add(Component()); - addAll([Component(), Component()]); - } -} - -class MyOtherBlueprint extends Blueprint { - @override - void build(_) { - add(Component()); - } -} - -class YetMyOtherBlueprint extends Blueprint { - @override - void build(_) { - add(Component()); - } -} - -class MyComposedBlueprint extends Blueprint { - @override - void build(_) { - addBlueprint(MyBlueprint()); - addAllBlueprints([MyOtherBlueprint(), YetMyOtherBlueprint()]); - } -} - -class MyForge2dBlueprint extends Forge2DBlueprint { - @override - void build(_) { - addContactCallback(MockContactCallback()); - addAllContactCallback([MockContactCallback(), MockContactCallback()]); - } -} - void main() { - group('Blueprint', () { - setUpAll(() { - registerFallbackValue(MyBlueprint()); - registerFallbackValue(Component()); - }); - - test('components can be added to it', () { - final blueprint = MyBlueprint()..build(MockForge2DGame()); + TestWidgetsFlutterBinding.ensureInitialized(); - expect(blueprint.components.length, equals(3)); - }); - - test('blueprints can be added to it', () { - final blueprint = MyComposedBlueprint()..build(MockForge2DGame()); - - expect(blueprint.blueprints.length, equals(3)); - }); - - test('adds the components to a game on attach', () { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - MyBlueprint().attach(mockGame); - - verify(() => mockGame.addAll(any())).called(1); - }); - - test('adds components from a child Blueprint the to a game on attach', () { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - MyComposedBlueprint().attach(mockGame); - - verify(() => mockGame.addAll(any())).called(4); + group('Blueprint', () { + final flameTester = FlameTester(FlameGame.new); + + test('correctly sets and gets components', () { + final component1 = Component(); + final component2 = Component(); + final blueprint = Blueprint( + components: [ + component1, + component2, + ], + ); + + expect(blueprint.components.length, 2); + expect(blueprint.components, contains(component1)); + expect(blueprint.components, contains(component2)); }); - test( - 'throws assertion error when adding to an already attached blueprint', - () async { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - final blueprint = MyBlueprint(); - await blueprint.attach(mockGame); - - expect(() => blueprint.add(Component()), throwsAssertionError); - expect(() => blueprint.addAll([Component()]), throwsAssertionError); - }, - ); - }); + test('correctly sets and gets blueprints', () { + final blueprint2 = Blueprint( + components: [Component()], + ); + final blueprint1 = Blueprint( + components: [Component()], + blueprints: [blueprint2], + ); - group('Forge2DBlueprint', () { - setUpAll(() { - registerFallbackValue(TestContactCallback()); + expect(blueprint1.blueprints, contains(blueprint2)); }); - test('callbacks can be added to it', () { - final blueprint = MyForge2dBlueprint()..build(MockForge2DGame()); - - expect(blueprint.callbacks.length, equals(3)); + flameTester.test('adds the components to parent on attach', (game) async { + final blueprint = Blueprint( + components: [ + Component(), + Component(), + ], + ); + await game.addFromBlueprint(blueprint); + await game.ready(); + + for (final component in blueprint.components) { + expect(game.children.contains(component), isTrue); + } }); - test('adds the callbacks to a game on attach', () async { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {}); - await MyForge2dBlueprint().attach(mockGame); - - verify(() => mockGame.addContactCallback(any())).called(3); + flameTester.test('adds components from a child Blueprint', (game) async { + final childBlueprint = Blueprint( + components: [ + Component(), + Component(), + ], + ); + final parentBlueprint = Blueprint( + components: [ + Component(), + Component(), + ], + blueprints: [ + childBlueprint, + ], + ); + + await game.addFromBlueprint(parentBlueprint); + await game.ready(); + + for (final component in childBlueprint.components) { + expect(game.children, contains(component)); + expect(parentBlueprint.components, contains(component)); + } + for (final component in parentBlueprint.components) { + expect(game.children, contains(component)); + } }); - - test( - 'throws assertion error when adding to an already attached blueprint', - () async { - final mockGame = MockForge2DGame(); - when(() => mockGame.addAll(any())).thenAnswer((_) async {}); - when(() => mockGame.addContactCallback(any())).thenAnswer((_) async {}); - final blueprint = MyForge2dBlueprint(); - await blueprint.attach(mockGame); - - expect( - () => blueprint.addContactCallback(MockContactCallback()), - throwsAssertionError, - ); - expect( - () => blueprint.addAllContactCallback([MockContactCallback()]), - throwsAssertionError, - ); - }, - ); }); } diff --git a/packages/pinball_theme/assets/images/android/leaderboard_icon.png b/packages/pinball_theme/assets/images/android/leaderboard_icon.png new file mode 100644 index 00000000..238e29ef Binary files /dev/null and b/packages/pinball_theme/assets/images/android/leaderboard_icon.png differ diff --git a/packages/pinball_theme/assets/images/dash/leaderboard_icon.png b/packages/pinball_theme/assets/images/dash/leaderboard_icon.png new file mode 100644 index 00000000..5c172d47 Binary files /dev/null and b/packages/pinball_theme/assets/images/dash/leaderboard_icon.png differ diff --git a/packages/pinball_theme/assets/images/dino/leaderboard_icon.png b/packages/pinball_theme/assets/images/dino/leaderboard_icon.png new file mode 100644 index 00000000..b1033371 Binary files /dev/null and b/packages/pinball_theme/assets/images/dino/leaderboard_icon.png differ diff --git a/packages/pinball_theme/assets/images/sparky/leaderboard_icon.png b/packages/pinball_theme/assets/images/sparky/leaderboard_icon.png new file mode 100644 index 00000000..76001516 Binary files /dev/null and b/packages/pinball_theme/assets/images/sparky/leaderboard_icon.png differ diff --git a/packages/pinball_theme/lib/src/generated/assets.gen.dart b/packages/pinball_theme/lib/src/generated/assets.gen.dart index 22b6b520..f050c621 100644 --- a/packages/pinball_theme/lib/src/generated/assets.gen.dart +++ b/packages/pinball_theme/lib/src/generated/assets.gen.dart @@ -39,6 +39,10 @@ class $AssetsImagesAndroidGen { /// File path: assets/images/android/icon.png AssetGenImage get icon => const AssetGenImage('assets/images/android/icon.png'); + + /// File path: assets/images/android/leaderboard_icon.png + AssetGenImage get leaderboardIcon => + const AssetGenImage('assets/images/android/leaderboard_icon.png'); } class $AssetsImagesDashGen { @@ -54,6 +58,10 @@ class $AssetsImagesDashGen { /// File path: assets/images/dash/icon.png AssetGenImage get icon => const AssetGenImage('assets/images/dash/icon.png'); + + /// File path: assets/images/dash/leaderboard_icon.png + AssetGenImage get leaderboardIcon => + const AssetGenImage('assets/images/dash/leaderboard_icon.png'); } class $AssetsImagesDinoGen { @@ -69,6 +77,10 @@ class $AssetsImagesDinoGen { /// File path: assets/images/dino/icon.png AssetGenImage get icon => const AssetGenImage('assets/images/dino/icon.png'); + + /// File path: assets/images/dino/leaderboard_icon.png + AssetGenImage get leaderboardIcon => + const AssetGenImage('assets/images/dino/leaderboard_icon.png'); } class $AssetsImagesSparkyGen { @@ -85,6 +97,10 @@ class $AssetsImagesSparkyGen { /// File path: assets/images/sparky/icon.png AssetGenImage get icon => const AssetGenImage('assets/images/sparky/icon.png'); + + /// File path: assets/images/sparky/leaderboard_icon.png + AssetGenImage get leaderboardIcon => + const AssetGenImage('assets/images/sparky/leaderboard_icon.png'); } class Assets { diff --git a/packages/pinball_theme/lib/src/themes/android_theme.dart b/packages/pinball_theme/lib/src/themes/android_theme.dart index 2a59cb61..1001ca5f 100644 --- a/packages/pinball_theme/lib/src/themes/android_theme.dart +++ b/packages/pinball_theme/lib/src/themes/android_theme.dart @@ -22,4 +22,7 @@ class AndroidTheme extends CharacterTheme { @override AssetGenImage get icon => Assets.images.android.icon; + + @override + AssetGenImage get leaderboardIcon => Assets.images.android.leaderboardIcon; } diff --git a/packages/pinball_theme/lib/src/themes/character_theme.dart b/packages/pinball_theme/lib/src/themes/character_theme.dart index 0a81fae8..9c5f6ba6 100644 --- a/packages/pinball_theme/lib/src/themes/character_theme.dart +++ b/packages/pinball_theme/lib/src/themes/character_theme.dart @@ -27,6 +27,9 @@ abstract class CharacterTheme extends Equatable { /// Icon asset. AssetGenImage get icon; + /// Icon asset for the leaderboard. + AssetGenImage get leaderboardIcon; + @override List get props => [ name, @@ -34,5 +37,6 @@ abstract class CharacterTheme extends Equatable { character, background, icon, + leaderboardIcon, ]; } diff --git a/packages/pinball_theme/lib/src/themes/dash_theme.dart b/packages/pinball_theme/lib/src/themes/dash_theme.dart index df6cf363..9038f2fc 100644 --- a/packages/pinball_theme/lib/src/themes/dash_theme.dart +++ b/packages/pinball_theme/lib/src/themes/dash_theme.dart @@ -22,4 +22,7 @@ class DashTheme extends CharacterTheme { @override AssetGenImage get icon => Assets.images.dash.icon; + + @override + AssetGenImage get leaderboardIcon => Assets.images.dash.leaderboardIcon; } diff --git a/packages/pinball_theme/lib/src/themes/dino_theme.dart b/packages/pinball_theme/lib/src/themes/dino_theme.dart index b1a5a32f..b434463e 100644 --- a/packages/pinball_theme/lib/src/themes/dino_theme.dart +++ b/packages/pinball_theme/lib/src/themes/dino_theme.dart @@ -22,4 +22,7 @@ class DinoTheme extends CharacterTheme { @override AssetGenImage get icon => Assets.images.dino.icon; + + @override + AssetGenImage get leaderboardIcon => Assets.images.dino.leaderboardIcon; } diff --git a/packages/pinball_theme/lib/src/themes/sparky_theme.dart b/packages/pinball_theme/lib/src/themes/sparky_theme.dart index 7990e2c3..9e71bef8 100644 --- a/packages/pinball_theme/lib/src/themes/sparky_theme.dart +++ b/packages/pinball_theme/lib/src/themes/sparky_theme.dart @@ -22,4 +22,7 @@ class SparkyTheme extends CharacterTheme { @override AssetGenImage get icon => Assets.images.sparky.icon; + + @override + AssetGenImage get leaderboardIcon => Assets.images.sparky.leaderboardIcon; } diff --git a/packages/pinball_theme/pubspec.yaml b/packages/pinball_theme/pubspec.yaml index 93084cca..57522939 100644 --- a/packages/pinball_theme/pubspec.yaml +++ b/packages/pinball_theme/pubspec.yaml @@ -17,7 +17,6 @@ dev_dependencies: very_good_analysis: ^2.4.0 flutter: - uses-material-design: true generate: true assets: - assets/images/ diff --git a/packages/pinball_theme/test/src/themes/android_theme_test.dart b/packages/pinball_theme/test/src/themes/android_theme_test.dart index 5dd10283..27a06922 100644 --- a/packages/pinball_theme/test/src/themes/android_theme_test.dart +++ b/packages/pinball_theme/test/src/themes/android_theme_test.dart @@ -1,6 +1,5 @@ // ignore_for_file: prefer_const_constructors -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_theme/pinball_theme.dart'; @@ -13,27 +12,5 @@ void main() { test('supports value equality', () { expect(AndroidTheme(), equals(AndroidTheme())); }); - - test('ballColor is correct', () { - expect(AndroidTheme().ballColor, equals(Colors.green)); - }); - - test('character asset is correct', () { - expect( - AndroidTheme().character, - equals(Assets.images.android.character), - ); - }); - - test('background asset is correct', () { - expect( - AndroidTheme().background, - equals(Assets.images.android.background), - ); - }); - - test('icon asset is correct', () { - expect(AndroidTheme().icon, equals(Assets.images.android.icon)); - }); }); } diff --git a/packages/pinball_theme/test/src/themes/dash_theme_test.dart b/packages/pinball_theme/test/src/themes/dash_theme_test.dart index 1180eeb5..16605fb3 100644 --- a/packages/pinball_theme/test/src/themes/dash_theme_test.dart +++ b/packages/pinball_theme/test/src/themes/dash_theme_test.dart @@ -1,6 +1,5 @@ // ignore_for_file: prefer_const_constructors -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_theme/pinball_theme.dart'; @@ -13,27 +12,5 @@ void main() { test('supports value equality', () { expect(DashTheme(), equals(DashTheme())); }); - - test('ballColor is correct', () { - expect(DashTheme().ballColor, equals(Colors.blue)); - }); - - test('character asset is correct', () { - expect( - DashTheme().character, - equals(Assets.images.dash.character), - ); - }); - - test('background asset is correct', () { - expect( - DashTheme().background, - equals(Assets.images.dash.background), - ); - }); - - test('icon asset is correct', () { - expect(DashTheme().icon, equals(Assets.images.dash.icon)); - }); }); } diff --git a/packages/pinball_theme/test/src/themes/dino_theme_test.dart b/packages/pinball_theme/test/src/themes/dino_theme_test.dart index 061ebf8d..45ea0813 100644 --- a/packages/pinball_theme/test/src/themes/dino_theme_test.dart +++ b/packages/pinball_theme/test/src/themes/dino_theme_test.dart @@ -1,6 +1,5 @@ // ignore_for_file: prefer_const_constructors -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_theme/pinball_theme.dart'; @@ -13,27 +12,5 @@ void main() { test('supports value equality', () { expect(DinoTheme(), equals(DinoTheme())); }); - - test('ballColor is correct', () { - expect(DinoTheme().ballColor, equals(Colors.grey)); - }); - - test('character asset is correct', () { - expect( - DinoTheme().character, - equals(Assets.images.dino.character), - ); - }); - - test('background asset is correct', () { - expect( - DinoTheme().background, - equals(Assets.images.dino.background), - ); - }); - - test('icon asset is correct', () { - expect(DinoTheme().icon, equals(Assets.images.dino.icon)); - }); }); } diff --git a/packages/pinball_theme/test/src/themes/sparky_theme_test.dart b/packages/pinball_theme/test/src/themes/sparky_theme_test.dart index bf18211d..ce276938 100644 --- a/packages/pinball_theme/test/src/themes/sparky_theme_test.dart +++ b/packages/pinball_theme/test/src/themes/sparky_theme_test.dart @@ -1,6 +1,5 @@ // ignore_for_file: prefer_const_constructors -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_theme/pinball_theme.dart'; @@ -13,27 +12,5 @@ void main() { test('supports value equality', () { expect(SparkyTheme(), equals(SparkyTheme())); }); - - test('ballColor is correct', () { - expect(SparkyTheme().ballColor, equals(Colors.orange)); - }); - - test('character asset is correct', () { - expect( - SparkyTheme().character, - equals(Assets.images.sparky.character), - ); - }); - - test('background asset is correct', () { - expect( - SparkyTheme().background, - equals(Assets.images.sparky.background), - ); - }); - - test('icon asset is correct', () { - expect(SparkyTheme().icon, equals(Assets.images.sparky.icon)); - }); }); } diff --git a/pubspec.lock b/pubspec.lock index 1a502f37..31aed6af 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "31.0.0" + version: "39.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "2.8.0" + version: "4.0.0" args: dependency: transitive description: @@ -71,13 +71,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" - cli_util: - dependency: transitive - description: - name: cli_util - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.5" clock: dependency: transitive description: @@ -321,7 +314,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" json_annotation: dependency: transitive description: @@ -356,7 +349,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" meta: dependency: transitive description: @@ -419,7 +412,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" path_provider: dependency: transitive description: @@ -592,7 +585,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -634,21 +627,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.19.5" + version: "1.21.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.9" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.13" typed_data: dependency: transitive description: @@ -669,7 +662,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" very_good_analysis: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index f7676247..f17ea07a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,6 +47,7 @@ flutter: assets: - assets/images/components/ + - assets/images/bonus_animation/ flutter_gen: line_length: 80 diff --git a/test/game/components/board_test.dart b/test/game/components/board_test.dart index 3ecbe6e7..bc1c5c39 100644 --- a/test/game/components/board_test.dart +++ b/test/game/components/board_test.dart @@ -16,10 +16,15 @@ void main() { Assets.images.dash.bumper.a.inactive.keyName, Assets.images.dash.bumper.b.active.keyName, Assets.images.dash.bumper.b.inactive.keyName, + Assets.images.dash.animatronic.keyName, Assets.images.signpost.inactive.keyName, Assets.images.signpost.active1.keyName, Assets.images.signpost.active2.keyName, Assets.images.signpost.active3.keyName, + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, ]; final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); diff --git a/test/game/components/controlled_flipper_test.dart b/test/game/components/controlled_flipper_test.dart index 01982129..1b2a7e43 100644 --- a/test/game/components/controlled_flipper_test.dart +++ b/test/game/components/controlled_flipper_test.dart @@ -11,7 +11,11 @@ import '../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(EmptyPinballTestGame.new); + final assets = [ + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + ]; + final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); final flameBlocTester = FlameBlocTester( gameBuilder: EmptyPinballTestGame.new, @@ -25,6 +29,7 @@ void main() { whenListen(bloc, Stream.value(state), initialState: state); return bloc; }, + assets: assets, ); group('FlipperController', () { diff --git a/test/game/components/controlled_sparky_computer_test.dart b/test/game/components/controlled_sparky_computer_test.dart index 944afca3..6ba21a44 100644 --- a/test/game/components/controlled_sparky_computer_test.dart +++ b/test/game/components/controlled_sparky_computer_test.dart @@ -8,28 +8,21 @@ import 'package:pinball/game/game.dart'; import '../../helpers/helpers.dart'; void main() { - group('SparkyComputerController', () { + group('ControlledSparkyComputer', () { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(EmptyPinballTestGame.new); - late ControlledSparkyComputer controlledSparkyComputer; - - setUp(() { - controlledSparkyComputer = ControlledSparkyComputer(); - }); - - test('can be instantiated', () { - expect( - SparkyComputerController(controlledSparkyComputer), - isA(), - ); + flameTester.test('loads correctly', (game) async { + final sparkyComputer = ControlledSparkyComputer(); + await game.ensureAdd(sparkyComputer); + expect(game.children, contains(sparkyComputer)); }); flameTester.testGameWidget( 'SparkyTurboChargeSensorBallContactCallback turbo charges the ball', setUp: (game, tester) async { - final contackCallback = SparkyTurboChargeSensorBallContactCallback(); - final sparkyTurboChargeSensor = MockSparkyTurboChargeSensor(); + final contackCallback = SparkyComputerSensorBallContactCallback(); + final sparkyTurboChargeSensor = MockSparkyComputerSensor(); final ball = MockControlledBall(); final controller = MockBallController(); diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index 73259afd..388410d5 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -19,6 +19,7 @@ void main() { Assets.images.dash.bumper.a.inactive.keyName, Assets.images.dash.bumper.b.active.keyName, Assets.images.dash.bumper.b.inactive.keyName, + Assets.images.dash.animatronic.keyName, Assets.images.signpost.inactive.keyName, Assets.images.signpost.active1.keyName, Assets.images.signpost.active2.keyName, diff --git a/test/game/components/game_flow_controller_test.dart b/test/game/components/game_flow_controller_test.dart index b9a6181a..4efc7174 100644 --- a/test/game/components/game_flow_controller_test.dart +++ b/test/game/components/game_flow_controller_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart'; import '../../helpers/helpers.dart'; @@ -43,6 +44,7 @@ void main() { when( () => backboard.gameOverMode( score: any(named: 'score'), + characterIconPath: any(named: 'characterIconPath'), onSubmit: any(named: 'onSubmit'), ), ).thenAnswer((_) async {}); @@ -55,6 +57,8 @@ void main() { when(game.firstChild).thenReturn(backboard); when(game.firstChild).thenReturn(cameraController); when(() => game.overlays).thenReturn(overlays); + when(() => game.theme) + .thenReturn(PinballTheme(characterTheme: DashTheme())); }); test( @@ -71,6 +75,7 @@ void main() { verify( () => backboard.gameOverMode( score: 0, + characterIconPath: any(named: 'characterIconPath'), onSubmit: any(named: 'onSubmit'), ), ).called(1); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index 1775a42f..4c3dbbfb 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -19,6 +19,7 @@ void main() { Assets.images.dash.bumper.a.inactive.keyName, Assets.images.dash.bumper.b.active.keyName, Assets.images.dash.bumper.b.inactive.keyName, + Assets.images.dash.animatronic.keyName, Assets.images.signpost.inactive.keyName, Assets.images.signpost.active1.keyName, Assets.images.signpost.active2.keyName, @@ -43,6 +44,14 @@ void main() { Assets.images.spaceship.ramp.arrow.active3.keyName, Assets.images.spaceship.ramp.arrow.active4.keyName, Assets.images.spaceship.ramp.arrow.active5.keyName, + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + Assets.images.boundary.outer.keyName, + Assets.images.boundary.bottom.keyName, + Assets.images.slingshot.upper.keyName, + Assets.images.slingshot.lower.keyName, ]; final flameTester = FlameTester(() => PinballTestGame(assets)); final debugModeFlameTester = FlameTester(() => DebugPinballTestGame(assets)); @@ -52,17 +61,6 @@ void main() { // [BallScorePointsCallback] once the following issue is resolved: // https://github.com/flame-engine/flame/issues/1416 group('components', () { - flameTester.test( - 'has three Walls', - (game) async { - await game.ready(); - final walls = game.children.where( - (component) => component is Wall && component is! BottomWall, - ); - expect(walls.length, 3); - }, - ); - flameTester.test( 'has only one BottomWall', (game) async { diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index 3c54197a..bbed2963 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -160,6 +160,10 @@ void main() { }); group('PinballGameView', () { + setUp(() async { + await Future.wait(game.preLoadAssets()); + }); + testWidgets('renders game and a hud', (tester) async { final gameBloc = MockGameBloc(); whenListen( diff --git a/test/game/view/widgets/bonus_animation_test.dart b/test/game/view/widgets/bonus_animation_test.dart new file mode 100644 index 00000000..9c23ae0d --- /dev/null +++ b/test/game/view/widgets/bonus_animation_test.dart @@ -0,0 +1,99 @@ +import 'dart:async'; + +import 'dart:ui' as ui; + +import 'package:flame/assets.dart'; +import 'package:flame/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/view/widgets/bonus_animation.dart'; + +import '../../../helpers/helpers.dart'; + +class MockImages extends Mock implements Images {} + +class MockImage extends Mock implements ui.Image {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() async { + await BonusAnimation.loadAssets(); + }); + + group('loads SpriteAnimationWidget correctly for', () { + testWidgets('dashNest', (tester) async { + await tester.pumpApp( + BonusAnimation.dashNest(), + ); + await tester.pump(); + + expect(find.byType(SpriteAnimationWidget), findsOneWidget); + }); + + testWidgets('dino', (tester) async { + await tester.pumpApp( + BonusAnimation.dino(), + ); + await tester.pump(); + + expect(find.byType(SpriteAnimationWidget), findsOneWidget); + }); + + testWidgets('sparkyTurboCharge', (tester) async { + await tester.pumpApp( + BonusAnimation.sparkyTurboCharge(), + ); + await tester.pump(); + + expect(find.byType(SpriteAnimationWidget), findsOneWidget); + }); + + testWidgets('google', (tester) async { + await tester.pumpApp( + BonusAnimation.google(), + ); + await tester.pump(); + + expect(find.byType(SpriteAnimationWidget), findsOneWidget); + }); + + testWidgets('android', (tester) async { + await tester.pumpApp( + BonusAnimation.android(), + ); + await tester.pump(); + + expect(find.byType(SpriteAnimationWidget), findsOneWidget); + }); + }); + + // TODO(arturplaczek): refactor this test when there is a new version of the + // flame with an onComplete callback in SpriteAnimationWidget + // https://github.com/flame-engine/flame/issues/1543 + testWidgets('called onCompleted callback at the end of animation ', + (tester) async { + final completer = Completer(); + + await tester.runAsync(() async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: BonusAnimation.dashNest( + onCompleted: completer.complete, + ), + ), + ), + ); + + await tester.pump(); + + await Future.delayed(const Duration(seconds: 4)); + + await tester.pump(); + + expect(completer.isCompleted, isTrue); + }); + }); +} diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index e9af4408..9b0f67c9 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -69,8 +69,7 @@ class MockDashNestBumper extends Mock implements DashNestBumper {} class MockPinballAudio extends Mock implements PinballAudio {} -class MockSparkyTurboChargeSensor extends Mock - implements SparkyTurboChargeSensor {} +class MockSparkyComputerSensor extends Mock implements SparkyComputerSensor {} class MockAssetsManagerCubit extends Mock implements AssetsManagerCubit {}