From 0bec9971786238cd89e2c428a79d92460c1987d3 Mon Sep 17 00:00:00 2001 From: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> Date: Fri, 8 Apr 2022 14:12:10 -0500 Subject: [PATCH] feat: add Sparky's computer (#155) * feat: add sparky computer and mechanics * test: sparky mechanics * refactor: reposition * test: contact callback test * refactor: new separated sprite components * fix: lint * refactor: improve ball turbo charge tests --- lib/game/bloc/game_bloc.dart | 15 +++ lib/game/bloc/game_event.dart | 7 ++ lib/game/bloc/game_state.dart | 3 + lib/game/components/components.dart | 1 + lib/game/components/controlled_ball.dart | 14 +++ .../controlled_sparky_computer.dart | 84 +++++++++++++ lib/game/game_assets.dart | 8 ++ lib/game/pinball_game.dart | 1 + .../bumper}/a/active.png | Bin .../bumper}/a/inactive.png | Bin .../bumper}/b/active.png | Bin .../bumper}/b/inactive.png | Bin .../bumper}/c/active.png | Bin .../bumper}/c/inactive.png | Bin .../assets/images/sparky/computer/base.png | Bin 0 -> 10424 bytes .../assets/images/sparky/computer/top.png | Bin 0 -> 4589 bytes .../lib/gen/assets.gen.dart | 55 ++++++-- .../lib/src/components/ball.dart | 12 +- .../lib/src/components/components.dart | 1 + .../lib/src/components/sparky_bumper.dart | 12 +- .../lib/src/components/sparky_computer.dart | 115 +++++++++++++++++ .../lib/src/flame/blueprint.dart | 12 +- packages/pinball_components/pubspec.yaml | 7 +- .../test/src/components/ball_test.dart | 21 ++-- .../src/components/golden/sparky-computer.png | Bin 0 -> 39588 bytes .../src/components/sparky_computer_test.dart | 30 +++++ test/game/bloc/game_bloc_test.dart | 17 +++ test/game/bloc/game_event_test.dart | 13 ++ .../game/components/controlled_ball_test.dart | 118 ++++++++++++++---- .../controlled_sparky_computer_test.dart | 45 +++++++ test/helpers/mocks.dart | 3 + 31 files changed, 531 insertions(+), 63 deletions(-) create mode 100644 lib/game/components/controlled_sparky_computer.dart rename packages/pinball_components/assets/images/{sparky_bumper => sparky/bumper}/a/active.png (100%) rename packages/pinball_components/assets/images/{sparky_bumper => sparky/bumper}/a/inactive.png (100%) rename packages/pinball_components/assets/images/{sparky_bumper => sparky/bumper}/b/active.png (100%) rename packages/pinball_components/assets/images/{sparky_bumper => sparky/bumper}/b/inactive.png (100%) rename packages/pinball_components/assets/images/{sparky_bumper => sparky/bumper}/c/active.png (100%) rename packages/pinball_components/assets/images/{sparky_bumper => sparky/bumper}/c/inactive.png (100%) create mode 100644 packages/pinball_components/assets/images/sparky/computer/base.png create mode 100644 packages/pinball_components/assets/images/sparky/computer/top.png create mode 100644 packages/pinball_components/lib/src/components/sparky_computer.dart create mode 100644 packages/pinball_components/test/src/components/golden/sparky-computer.png create mode 100644 packages/pinball_components/test/src/components/sparky_computer_test.dart create mode 100644 test/game/components/controlled_sparky_computer_test.dart diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 7c1b4f44..ce1a78b4 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -13,6 +13,7 @@ class GameBloc extends Bloc { on(_onScored); on(_onBonusLetterActivated); on(_onDashNestActivated); + on(_onSparkyTurboChargeActivated); } static const bonusWord = 'GOOGLE'; @@ -77,4 +78,18 @@ class GameBloc extends Bloc { ); } } + + Future _onSparkyTurboChargeActivated( + SparkyTurboChargeActivated event, + Emitter emit, + ) async { + emit( + state.copyWith( + bonusHistory: [ + ...state.bonusHistory, + GameBonus.sparkyTurboCharge, + ], + ), + ); + } } diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index b05c5336..ee5315ad 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -54,3 +54,10 @@ class DashNestActivated extends GameEvent { @override List get props => [nestId]; } + +class SparkyTurboChargeActivated extends GameEvent { + const SparkyTurboChargeActivated(); + + @override + List get props => []; +} diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index bbaa4cd8..0d9485e9 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -10,6 +10,9 @@ enum GameBonus { /// Bonus achieved when the user activates all dash nest bumpers. dashNest, + + /// Bonus achieved when a ball enters Sparky's computer. + sparkyTurboCharge, } /// {@template game_state} diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 6bc65a89..31d3b917 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -3,6 +3,7 @@ export 'bonus_word.dart'; export 'camera_controller.dart'; export 'controlled_ball.dart'; export 'controlled_flipper.dart'; +export 'controlled_sparky_computer.dart'; export 'flutter_forest.dart'; export 'game_flow_controller.dart'; export 'plunger.dart'; diff --git a/lib/game/components/controlled_ball.dart b/lib/game/components/controlled_ball.dart index cef076d8..d48ebe66 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -53,6 +53,20 @@ class BallController extends ComponentController component.shouldRemove = true; } + /// Stops the [Ball] inside of the [SparkyComputer] while the turbo charge + /// sequence runs, then boosts the ball out of the computer. + Future turboCharge() async { + gameRef.read().add(const SparkyTurboChargeActivated()); + + // TODO(allisonryan0002): adjust delay to match animation duration once + // given animations. + component.stop(); + await Future.delayed(const Duration(seconds: 1)); + component + ..resume() + ..boost(Vector2(200, -500)); + } + @override void onRemove() { super.onRemove(); diff --git a/lib/game/components/controlled_sparky_computer.dart b/lib/game/components/controlled_sparky_computer.dart new file mode 100644 index 00000000..699ebae2 --- /dev/null +++ b/lib/game/components/controlled_sparky_computer.dart @@ -0,0 +1,84 @@ +// ignore_for_file: avoid_renaming_method_parameters + +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball/flame/flame.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template controlled_sparky_computer} +/// [SparkyComputer] with a [SparkyComputerController] attached. +/// {@endtemplate} +class ControlledSparkyComputer extends SparkyComputer + with Controls, HasGameRef { + /// {@macro controlled_sparky_computer} + ControlledSparkyComputer() { + controller = SparkyComputerController(this); + } + + @override + void build(Forge2DGame _) { + addContactCallback(SparkyTurboChargeSensorBallContactCallback()); + final sparkyTurboChargeSensor = SparkyTurboChargeSensor() + ..initialPosition = Vector2(-13, 49.8); + add(sparkyTurboChargeSensor); + super.build(_); + } +} + +/// {@template sparky_computer_controller} +/// Controller attached to a [SparkyComputer] that handles its game related +/// logic. +/// {@endtemplate} +// TODO(allisonryan0002): listen for turbo charge game bonus and animate Sparky. +class SparkyComputerController + extends ComponentController { + /// {@macro sparky_computer_controller} + SparkyComputerController(ControlledSparkyComputer controlledComputer) + : super(controlledComputer); +} + +/// {@template sparky_turbo_charge_sensor} +/// Small sensor body used to detect when a ball has entered the +/// [SparkyComputer] with the [SparkyTurboChargeSensorBallContactCallback]. +/// {@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(); + + @override + void begin( + SparkyTurboChargeSensor sparkyTurboChargeSensor, + ControlledBall ball, + _, + ) { + ball.controller.turboCharge(); + } +} diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index a4eedeb4..22605904 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -46,6 +46,14 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.spaceship.rail.foreground.keyName), images.load(components.Assets.images.chromeDino.mouth.keyName), images.load(components.Assets.images.chromeDino.head.keyName), + images.load(components.Assets.images.sparky.computer.base.keyName), + images.load(components.Assets.images.sparky.computer.top.keyName), + images.load(components.Assets.images.sparky.bumper.a.active.keyName), + images.load(components.Assets.images.sparky.bumper.a.inactive.keyName), + images.load(components.Assets.images.sparky.bumper.b.active.keyName), + images.load(components.Assets.images.sparky.bumper.b.inactive.keyName), + images.load(components.Assets.images.sparky.bumper.c.active.keyName), + images.load(components.Assets.images.sparky.bumper.c.inactive.keyName), images.load(components.Assets.images.backboard.backboardScores.keyName), images.load(components.Assets.images.backboard.backboardGameOver.keyName), images.load(Assets.images.components.background.path), diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 4ccad9db..49992653 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -45,6 +45,7 @@ class PinballGame extends Forge2DGame await _addGameBoundaries(); unawaited(addFromBlueprint(Boundaries())); unawaited(addFromBlueprint(LaunchRamp())); + unawaited(addFromBlueprint(ControlledSparkyComputer())); final plunger = Plunger(compressionDistance: 29) ..initialPosition = Vector2(38, -19); diff --git a/packages/pinball_components/assets/images/sparky_bumper/a/active.png b/packages/pinball_components/assets/images/sparky/bumper/a/active.png similarity index 100% rename from packages/pinball_components/assets/images/sparky_bumper/a/active.png rename to packages/pinball_components/assets/images/sparky/bumper/a/active.png diff --git a/packages/pinball_components/assets/images/sparky_bumper/a/inactive.png b/packages/pinball_components/assets/images/sparky/bumper/a/inactive.png similarity index 100% rename from packages/pinball_components/assets/images/sparky_bumper/a/inactive.png rename to packages/pinball_components/assets/images/sparky/bumper/a/inactive.png diff --git a/packages/pinball_components/assets/images/sparky_bumper/b/active.png b/packages/pinball_components/assets/images/sparky/bumper/b/active.png similarity index 100% rename from packages/pinball_components/assets/images/sparky_bumper/b/active.png rename to packages/pinball_components/assets/images/sparky/bumper/b/active.png diff --git a/packages/pinball_components/assets/images/sparky_bumper/b/inactive.png b/packages/pinball_components/assets/images/sparky/bumper/b/inactive.png similarity index 100% rename from packages/pinball_components/assets/images/sparky_bumper/b/inactive.png rename to packages/pinball_components/assets/images/sparky/bumper/b/inactive.png diff --git a/packages/pinball_components/assets/images/sparky_bumper/c/active.png b/packages/pinball_components/assets/images/sparky/bumper/c/active.png similarity index 100% rename from packages/pinball_components/assets/images/sparky_bumper/c/active.png rename to packages/pinball_components/assets/images/sparky/bumper/c/active.png diff --git a/packages/pinball_components/assets/images/sparky_bumper/c/inactive.png b/packages/pinball_components/assets/images/sparky/bumper/c/inactive.png similarity index 100% rename from packages/pinball_components/assets/images/sparky_bumper/c/inactive.png rename to packages/pinball_components/assets/images/sparky/bumper/c/inactive.png diff --git a/packages/pinball_components/assets/images/sparky/computer/base.png b/packages/pinball_components/assets/images/sparky/computer/base.png new file mode 100644 index 0000000000000000000000000000000000000000..2e8fe362a08ee871cac7b58f5c8f29a20e048a88 GIT binary patch literal 10424 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_components/assets/images/sparky/computer/top.png b/packages/pinball_components/assets/images/sparky/computer/top.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f3bc6c18f8ea5770c492a8fedf1697af5aefda GIT binary patch literal 4589 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 const $AssetsImagesSlingshotGen(); $AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen(); - $AssetsImagesSparkyBumperGen get sparkyBumper => - const $AssetsImagesSparkyBumperGen(); + $AssetsImagesSparkyGen get sparky => const $AssetsImagesSparkyGen(); } class $AssetsImagesBackboardGen { @@ -136,12 +135,13 @@ class $AssetsImagesSpaceshipGen { const AssetGenImage('assets/images/spaceship/saucer.png'); } -class $AssetsImagesSparkyBumperGen { - const $AssetsImagesSparkyBumperGen(); +class $AssetsImagesSparkyGen { + const $AssetsImagesSparkyGen(); - $AssetsImagesSparkyBumperAGen get a => const $AssetsImagesSparkyBumperAGen(); - $AssetsImagesSparkyBumperBGen get b => const $AssetsImagesSparkyBumperBGen(); - $AssetsImagesSparkyBumperCGen get c => const $AssetsImagesSparkyBumperCGen(); + $AssetsImagesSparkyBumperGen get bumper => + const $AssetsImagesSparkyBumperGen(); + $AssetsImagesSparkyComputerGen get computer => + const $AssetsImagesSparkyComputerGen(); } class $AssetsImagesDashBumperAGen { @@ -191,31 +191,60 @@ class $AssetsImagesSpaceshipRampGen { 'assets/images/spaceship/ramp/railing-foreground.png'); } +class $AssetsImagesSparkyBumperGen { + const $AssetsImagesSparkyBumperGen(); + + $AssetsImagesSparkyBumperAGen get a => const $AssetsImagesSparkyBumperAGen(); + $AssetsImagesSparkyBumperBGen get b => const $AssetsImagesSparkyBumperBGen(); + $AssetsImagesSparkyBumperCGen get c => const $AssetsImagesSparkyBumperCGen(); +} + +class $AssetsImagesSparkyComputerGen { + const $AssetsImagesSparkyComputerGen(); + + /// File path: assets/images/sparky/computer/base.png + AssetGenImage get base => + const AssetGenImage('assets/images/sparky/computer/base.png'); + + /// File path: assets/images/sparky/computer/top.png + AssetGenImage get top => + const AssetGenImage('assets/images/sparky/computer/top.png'); +} + class $AssetsImagesSparkyBumperAGen { const $AssetsImagesSparkyBumperAGen(); + /// File path: assets/images/sparky/bumper/a/active.png AssetGenImage get active => - const AssetGenImage('assets/images/sparky_bumper/a/active.png'); + const AssetGenImage('assets/images/sparky/bumper/a/active.png'); + + /// File path: assets/images/sparky/bumper/a/inactive.png AssetGenImage get inactive => - const AssetGenImage('assets/images/sparky_bumper/a/inactive.png'); + const AssetGenImage('assets/images/sparky/bumper/a/inactive.png'); } class $AssetsImagesSparkyBumperBGen { const $AssetsImagesSparkyBumperBGen(); + /// File path: assets/images/sparky/bumper/b/active.png AssetGenImage get active => - const AssetGenImage('assets/images/sparky_bumper/b/active.png'); + const AssetGenImage('assets/images/sparky/bumper/b/active.png'); + + /// File path: assets/images/sparky/bumper/b/inactive.png AssetGenImage get inactive => - const AssetGenImage('assets/images/sparky_bumper/b/inactive.png'); + const AssetGenImage('assets/images/sparky/bumper/b/inactive.png'); } class $AssetsImagesSparkyBumperCGen { const $AssetsImagesSparkyBumperCGen(); + /// File path: assets/images/sparky/bumper/c/active.png AssetGenImage get active => - const AssetGenImage('assets/images/sparky_bumper/c/active.png'); + const AssetGenImage('assets/images/sparky/bumper/c/active.png'); + + /// File path: assets/images/sparky/bumper/c/inactive.png AssetGenImage get inactive => - const AssetGenImage('assets/images/sparky_bumper/c/inactive.png'); + const AssetGenImage('assets/images/sparky/bumper/c/inactive.png'); } class Assets { diff --git a/packages/pinball_components/lib/src/components/ball.dart b/packages/pinball_components/lib/src/components/ball.dart index 6aaf88de..c32b8b18 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball.dart @@ -41,6 +41,8 @@ class Ball extends BodyComponent await add( _spriteComponent..tint(baseColor.withOpacity(0.5)), ); + + renderBody = false; } @override @@ -59,15 +61,19 @@ class Ball extends BodyComponent /// /// The [Ball] will no longer be affected by any forces, including it's /// weight and those emitted from collisions. + // TODO(allisonryan0002): prevent motion from contact with other balls. void stop() { - body.setType(BodyType.static); + body + ..gravityScale = 0 + ..linearVelocity = Vector2.zero() + ..angularVelocity = 0; } /// Allows the [Ball] to be affected by forces. /// /// If previously [stop]ped, the previous ball's velocity is not kept. void resume() { - body.setType(BodyType.dynamic); + body.gravityScale = 1; } @override @@ -91,7 +97,7 @@ class Ball extends BodyComponent /// Applies a boost on this [Ball]. void boost(Vector2 impulse) { - body.applyLinearImpulse(impulse); + body.linearVelocity = impulse; _boostTimer = _boostDuration; } diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index b71f7c13..b4ba70e2 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -23,3 +23,4 @@ export 'spaceship.dart'; export 'spaceship_rail.dart'; export 'spaceship_ramp.dart'; export 'sparky_bumper.dart'; +export 'sparky_computer.dart'; diff --git a/packages/pinball_components/lib/src/components/sparky_bumper.dart b/packages/pinball_components/lib/src/components/sparky_bumper.dart index e6a5f237..c4798624 100644 --- a/packages/pinball_components/lib/src/components/sparky_bumper.dart +++ b/packages/pinball_components/lib/src/components/sparky_bumper.dart @@ -27,8 +27,8 @@ class SparkyBumper extends BodyComponent with InitialPosition { : this._( majorRadius: 2.9, minorRadius: 2.1, - activeAssetPath: Assets.images.sparkyBumper.a.active.keyName, - inactiveAssetPath: Assets.images.sparkyBumper.a.inactive.keyName, + activeAssetPath: Assets.images.sparky.bumper.a.active.keyName, + inactiveAssetPath: Assets.images.sparky.bumper.a.inactive.keyName, spriteComponent: SpriteComponent( anchor: Anchor.center, position: Vector2(0, -0.25), @@ -40,8 +40,8 @@ class SparkyBumper extends BodyComponent with InitialPosition { : this._( majorRadius: 2.85, minorRadius: 2, - activeAssetPath: Assets.images.sparkyBumper.b.active.keyName, - inactiveAssetPath: Assets.images.sparkyBumper.b.inactive.keyName, + activeAssetPath: Assets.images.sparky.bumper.b.active.keyName, + inactiveAssetPath: Assets.images.sparky.bumper.b.inactive.keyName, spriteComponent: SpriteComponent( anchor: Anchor.center, position: Vector2(0, -0.35), @@ -53,8 +53,8 @@ class SparkyBumper extends BodyComponent with InitialPosition { : this._( majorRadius: 3, minorRadius: 2.2, - activeAssetPath: Assets.images.sparkyBumper.c.active.keyName, - inactiveAssetPath: Assets.images.sparkyBumper.c.inactive.keyName, + activeAssetPath: Assets.images.sparky.bumper.c.active.keyName, + inactiveAssetPath: Assets.images.sparky.bumper.c.inactive.keyName, spriteComponent: SpriteComponent( anchor: Anchor.center, position: Vector2(0, -0.4), diff --git a/packages/pinball_components/lib/src/components/sparky_computer.dart b/packages/pinball_components/lib/src/components/sparky_computer.dart new file mode 100644 index 00000000..6933a9ca --- /dev/null +++ b/packages/pinball_components/lib/src/components/sparky_computer.dart @@ -0,0 +1,115 @@ +// 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'; + +/// {@template sparky_computer} +/// A [Blueprint] which creates the [_ComputerBase] and +/// [_ComputerTopSpriteComponent]. +/// {@endtemplate} +class SparkyComputer extends Forge2DBlueprint { + @override + void build(_) { + final computerBase = _ComputerBase(); + final computerTop = _ComputerTopSpriteComponent(); + + addAll([ + computerBase, + computerTop, + ]); + } +} + +class _ComputerBase extends BodyComponent with InitialPosition { + _ComputerBase(); + + 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; + } + + @override + Body createBody() { + final bodyDef = BodyDef() + ..userData = this + ..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 { + _ComputerBaseSpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(-11.95, -48.35), + ); + + @override + Future onLoad() async { + await super.onLoad(); + + final sprite = await gameRef.loadSprite( + Assets.images.sparky.computer.base.keyName, + ); + this.sprite = sprite; + size = sprite.originalSize / 10; + } +} + +class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef { + _ComputerTopSpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(-12.45, -49.75), + priority: 1, + ); + + @override + Future onLoad() async { + await super.onLoad(); + + final sprite = await gameRef.loadSprite( + Assets.images.sparky.computer.top.keyName, + ); + this.sprite = sprite; + size = sprite.originalSize / 10; + } +} diff --git a/packages/pinball_components/lib/src/flame/blueprint.dart b/packages/pinball_components/lib/src/flame/blueprint.dart index 57af7d6d..5c2df683 100644 --- a/packages/pinball_components/lib/src/flame/blueprint.dart +++ b/packages/pinball_components/lib/src/flame/blueprint.dart @@ -12,7 +12,8 @@ const _attachedErrorMessage = "Can't add to attached Blueprints"; /// A [Blueprint] is a virtual way of grouping [Component]s /// that are related, but they need to be added directly on /// the [FlameGame] level. -abstract class Blueprint { +// TODO(alestiago): refactor with feat/make-blueprint-extend-component. +abstract class Blueprint extends Component { final List _components = []; final List _blueprints = []; @@ -34,14 +35,9 @@ abstract class Blueprint { _isAttached = true; } - /// Adds a list of [Component]s to this blueprint. - void addAll(List components) { - assert(!_isAttached, _attachedErrorMessage); - _components.addAll(components); - } - /// Adds a single [Component] to this blueprint. - void add(Component component) { + @override + Future add(Component component) async { assert(!_isAttached, _attachedErrorMessage); _components.add(component); } diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index b93f98a2..64446faf 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -40,9 +40,10 @@ flutter: - assets/images/chrome_dino/ - assets/images/kicker/ - assets/images/slingshot/ - - assets/images/sparky_bumper/a/ - - assets/images/sparky_bumper/b/ - - assets/images/sparky_bumper/c/ + - assets/images/sparky/computer/ + - assets/images/sparky/bumper/a/ + - assets/images/sparky/bumper/b/ + - assets/images/sparky/bumper/c/ - assets/images/backboard/ flutter_gen: diff --git a/packages/pinball_components/test/src/components/ball_test.dart b/packages/pinball_components/test/src/components/ball_test.dart index 248216f4..4fb8b5ff 100644 --- a/packages/pinball_components/test/src/components/ball_test.dart +++ b/packages/pinball_components/test/src/components/ball_test.dart @@ -116,15 +116,18 @@ void main() { }); }); - flameTester.test('by applying velocity', (game) async { - final ball = Ball(baseColor: Colors.blue); - await game.ensureAdd(ball); - ball.stop(); - - ball.body.linearVelocity.setValues(10, 10); - game.update(1); - expect(ball.body.position, equals(ball.initialPosition)); - }); + // TODO(allisonryan0002): delete or retest this if/when solution is added + // to prevent forces on a ball while stopped. + + // flameTester.test('by applying velocity', (game) async { + // final ball = Ball(baseColor: Colors.blue); + // await game.ensureAdd(ball); + // ball.stop(); + + // ball.body.linearVelocity.setValues(10, 10); + // game.update(1); + // expect(ball.body.position, equals(ball.initialPosition)); + // }); }); group('resume', () { diff --git a/packages/pinball_components/test/src/components/golden/sparky-computer.png b/packages/pinball_components/test/src/components/golden/sparky-computer.png new file mode 100644 index 0000000000000000000000000000000000000000..2f7ff65bfb2fde83cbaad41d1038265c859c8881 GIT binary patch literal 39588 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 Z*w|03S}DF^_{H=5e!_vc`p=IQ{||+-mZSgx literal 0 HcmV?d00001 diff --git a/packages/pinball_components/test/src/components/sparky_computer_test.dart b/packages/pinball_components/test/src/components/sparky_computer_test.dart new file mode 100644 index 00000000..7e761b97 --- /dev/null +++ b/packages/pinball_components/test/src/components/sparky_computer_test.dart @@ -0,0 +1,30 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('SparkyComputer', () { + final tester = FlameTester(TestGame.new); + + tester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.addFromBlueprint(SparkyComputer()); + await game.ready(); + game.camera.followVector2(Vector2(-15, -50)); + }, + // TODO(allisonryan0002): enable test when workflows are fixed. + // verify: (game, tester) async { + // await expectLater( + // find.byGame(), + // matchesGoldenFile('golden/sparky-computer.png'), + // ); + // }, + ); + }); +} diff --git a/test/game/bloc/game_bloc_test.dart b/test/game/bloc/game_bloc_test.dart index 8ec53106..fb543814 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -221,5 +221,22 @@ void main() { ], ); }); + + group('SparkyTurboChargeActivated', () { + blocTest( + 'adds game bonus', + build: GameBloc.new, + act: (bloc) => bloc..add(const SparkyTurboChargeActivated()), + expect: () => const [ + GameState( + score: 0, + balls: 3, + activatedBonusLetters: [], + activatedDashNests: {}, + bonusHistory: [GameBonus.sparkyTurboCharge], + ), + ], + ); + }); }); } diff --git a/test/game/bloc/game_event_test.dart b/test/game/bloc/game_event_test.dart index af9f6148..68530aae 100644 --- a/test/game/bloc/game_event_test.dart +++ b/test/game/bloc/game_event_test.dart @@ -84,5 +84,18 @@ void main() { ); }); }); + + group('SparkyTurboChargeActivated', () { + test('can be instantiated', () { + expect(const SparkyTurboChargeActivated(), isNotNull); + }); + + test('supports value equality', () { + expect( + SparkyTurboChargeActivated(), + equals(SparkyTurboChargeActivated()), + ); + }); + }); }); } diff --git a/test/game/components/controlled_ball_test.dart b/test/game/components/controlled_ball_test.dart index 53847b3c..41a1cdca 100644 --- a/test/game/components/controlled_ball_test.dart +++ b/test/game/components/controlled_ball_test.dart @@ -1,8 +1,9 @@ // ignore_for_file: cascade_invocations import 'package:bloc_test/bloc_test.dart'; +import 'package:flame/extensions.dart'; +import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; -import 'package:flutter/painting.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/game.dart'; @@ -11,10 +12,39 @@ import 'package:pinball_components/pinball_components.dart'; import '../../helpers/helpers.dart'; +// TODO(allisonryan0002): remove once +// https://github.com/flame-engine/flame/pull/1520 is merged +class WrappedBallController extends BallController { + WrappedBallController(Ball ball, this._gameRef) : super(ball); + + final PinballGame _gameRef; + + @override + PinballGame get gameRef => _gameRef; +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('BallController', () { + late Ball ball; + late GameBloc gameBloc; + + setUp(() { + ball = Ball(baseColor: const Color(0xFF00FFFF)); + gameBloc = MockGameBloc(); + whenListen( + gameBloc, + const Stream.empty(), + initialState: const GameState.initial(), + ); + }); + + final flameBlocTester = FlameBlocTester( + gameBuilder: EmptyPinballGameTest.new, + blocBuilder: () => gameBloc, + ); + test('can be instantiated', () { expect( BallController(MockBall()), @@ -22,36 +52,82 @@ void main() { ); }); - group('description', () { - late Ball ball; - late GameBloc gameBloc; - - setUp(() { - ball = Ball(baseColor: const Color(0xFF00FFFF)); - gameBloc = MockGameBloc(); - whenListen( - gameBloc, - const Stream.empty(), - initialState: const GameState.initial(), - ); - }); + flameBlocTester.testGameWidget( + 'lost adds BallLost to GameBloc', + setUp: (game, tester) async { + final controller = BallController(ball); + await ball.add(controller); + await game.ensureAdd(ball); - final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballGameTest.new, - blocBuilder: () => gameBloc, - ); + controller.lost(); + }, + verify: (game, tester) async { + verify(() => gameBloc.add(const BallLost())).called(1); + }, + ); + + group('turboCharge', () { + setUpAll(() { + registerFallbackValue(Vector2.zero()); + }); flameBlocTester.testGameWidget( - 'lost adds BallLost to GameBloc', + 'adds TurboChargeActivated', setUp: (game, tester) async { final controller = BallController(ball); await ball.add(controller); await game.ensureAdd(ball); - controller.lost(); + await controller.turboCharge(); }, verify: (game, tester) async { - verify(() => gameBloc.add(const BallLost())).called(1); + verify(() => gameBloc.add(const SparkyTurboChargeActivated())) + .called(1); + }, + ); + + flameBlocTester.test( + 'initially stops the ball', + (game) async { + final gameRef = MockPinballGame(); + final ball = MockControlledBall(); + final controller = WrappedBallController(ball, gameRef); + when(() => gameRef.read()).thenReturn(gameBloc); + when(() => ball.controller).thenReturn(controller); + + await controller.turboCharge(); + + verify(ball.stop).called(1); + }, + ); + + flameBlocTester.test( + 'resumes the ball', + (game) async { + final gameRef = MockPinballGame(); + final ball = MockControlledBall(); + final controller = WrappedBallController(ball, gameRef); + when(() => gameRef.read()).thenReturn(gameBloc); + when(() => ball.controller).thenReturn(controller); + + await controller.turboCharge(); + + verify(ball.resume).called(1); + }, + ); + + flameBlocTester.test( + 'boosts the ball', + (game) async { + final gameRef = MockPinballGame(); + final ball = MockControlledBall(); + final controller = WrappedBallController(ball, gameRef); + when(() => gameRef.read()).thenReturn(gameBloc); + when(() => ball.controller).thenReturn(controller); + + await controller.turboCharge(); + + verify(() => ball.boost(any())).called(1); }, ); }); diff --git a/test/game/components/controlled_sparky_computer_test.dart b/test/game/components/controlled_sparky_computer_test.dart new file mode 100644 index 00000000..a3e13486 --- /dev/null +++ b/test/game/components/controlled_sparky_computer_test.dart @@ -0,0 +1,45 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/game.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('SparkyComputerController', () { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(EmptyPinballGameTest.new); + + late ControlledSparkyComputer controlledSparkyComputer; + + setUp(() { + controlledSparkyComputer = ControlledSparkyComputer(); + }); + + test('can be instantiated', () { + expect( + SparkyComputerController(controlledSparkyComputer), + isA(), + ); + }); + + flameTester.testGameWidget( + 'SparkyTurboChargeSensorBallContactCallback turbo charges the ball', + setUp: (game, tester) async { + final contackCallback = SparkyTurboChargeSensorBallContactCallback(); + final sparkyTurboChargeSensor = MockSparkyTurboChargeSensor(); + final ball = MockControlledBall(); + final controller = MockBallController(); + + when(() => ball.controller).thenReturn(controller); + when(controller.turboCharge).thenAnswer((_) async {}); + + contackCallback.begin(sparkyTurboChargeSensor, ball, MockContact()); + + verify(() => ball.controller.turboCharge()).called(1); + }, + ); + }); +} diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index 941da872..df6728cc 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -76,6 +76,9 @@ class MockDashNestBumper extends Mock implements DashNestBumper {} class MockPinballAudio extends Mock implements PinballAudio {} +class MockSparkyTurboChargeSensor extends Mock + implements SparkyTurboChargeSensor {} + class MockAssetsManagerCubit extends Mock implements AssetsManagerCubit {} class MockBackboard extends Mock implements Backboard {}