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 61d0f3ca..0f2d7730 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -2,6 +2,7 @@ export 'board.dart'; export 'bonus_word.dart'; export 'controlled_ball.dart'; export 'controlled_flipper.dart'; +export 'controlled_sparky_computer.dart'; export 'flutter_forest.dart'; export 'plunger.dart'; export 'score_points.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..4d396037 --- /dev/null +++ b/lib/game/components/controlled_sparky_computer.dart @@ -0,0 +1,70 @@ +import 'package:flame/components.dart'; +import 'package:flame_forge2d/flame_forge2d.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.4, 49.8); + add(sparkyTurboChargeSensor); + super.build(_); + } +} + +class _SparkyTurboChargeSensor extends BodyComponent with InitialPosition { + _SparkyTurboChargeSensor() { + renderBody = false; + } + + @override + Body createBody() { + final shape = CircleShape()..radius = .1; + + final fixtureDef = FixtureDef(shape)..isSensor = true; + + final bodyDef = BodyDef() + ..position = initialPosition + ..userData = this; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + +/// {@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); +} + +class _SparkyTurboChargeSensorBallContactCallback + extends ContactCallback<_SparkyTurboChargeSensor, ControlledBall> { + _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 47175c32..913cce5f 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -42,6 +42,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(Assets.images.components.background.path), ]); } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 7a0e6823..4e01c7ba 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -40,6 +40,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); @@ -147,7 +148,7 @@ class DebugPinballGame extends PinballGame with TapDetector { @override Future onLoad() async { await super.onLoad(); - await _loadBackground(); + // await _loadBackground(); } // TODO(alestiago): Move to PinballGame once we have the real background 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 00000000..2e8fe362 Binary files /dev/null and b/packages/pinball_components/assets/images/sparky/computer/base.png differ 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 00000000..d9f3bc6c Binary files /dev/null and b/packages/pinball_components/assets/images/sparky/computer/top.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index de59219e..04619566 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -30,8 +30,7 @@ class $AssetsImagesGen { $AssetsImagesLaunchRampGen get launchRamp => const $AssetsImagesLaunchRampGen(); $AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen(); - $AssetsImagesSparkyBumperGen get sparkyBumper => - const $AssetsImagesSparkyBumperGen(); + $AssetsImagesSparkyGen get sparky => const $AssetsImagesSparkyGen(); } class $AssetsImagesBaseboardGen { @@ -144,12 +143,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 { @@ -216,40 +216,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 + /// 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 + /// 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 + /// 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 + /// 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 + /// 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 + /// 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 892936f9..cd682b00 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball.dart @@ -44,6 +44,8 @@ class Ball extends BodyComponent anchor: Anchor.center, )..tint(tint), ); + + renderBody = false; } @override @@ -64,15 +66,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]ed, the previous ball's velocity is not kept. void resume() { - body.setType(BodyType.dynamic); + body.gravityScale = 1; } @override @@ -96,7 +102,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 6b0c2ef5..d25e8f5f 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -20,3 +20,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..0f02be71 --- /dev/null +++ b/packages/pinball_components/lib/src/components/sparky_computer.dart @@ -0,0 +1,109 @@ +// 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 [_ComputerTop]. +/// {@endtemplate} +class SparkyComputer extends Forge2DBlueprint { + @override + void build(_) { + final computerBase = _ComputerBase(); + final computerTop = _ComputerTop(); + + addAll([ + computerBase, + computerTop, + ]); + } +} + +class _ComputerBase extends BodyComponent with InitialPosition { + _ComputerBase(); + + List _createFixtureDefs() { + final fixturesDef = []; + + final leftEdge = EdgeShape() + ..set( + Vector2(-15.3, 46), + Vector2(-15.7, 49.6), + ); + final leftEdgeFixtureDef = FixtureDef(leftEdge); + fixturesDef.add(leftEdgeFixtureDef); + + final topEdge = EdgeShape() + ..set( + Vector2(-15.7, 49.6), + Vector2(-11.1, 50.6), + ); + final topEdgeFixtureDef = FixtureDef(topEdge); + fixturesDef.add(topEdgeFixtureDef); + + final rightEdge = EdgeShape() + ..set( + Vector2(-11.1, 50.6), + Vector2(-9.4, 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(); + await _loadSprite(); + renderBody = false; + } + + Future _loadSprite() async { + final sprite = await gameRef.loadSprite( + Assets.images.sparky.computer.base.keyName, + ); + + await add( + SpriteComponent( + sprite: sprite, + size: sprite.originalSize / 10, + anchor: Anchor.center, + position: Vector2(-12.35, -48.35), + ), + ); + } +} + +class _ComputerTop extends SpriteComponent with HasGameRef { + _ComputerTop() + : super( + anchor: Anchor.center, + position: Vector2(-12.85, -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 312e01f3..0790d101 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -39,9 +39,10 @@ flutter: - assets/images/spaceship/ramp/ - assets/images/chrome_dino/ - assets/images/kicker/ - - 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/ flutter_gen: line_length: 80