diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart index 966b0a6b..9731b7ae 100644 --- a/lib/game/components/flutter_forest.dart +++ b/lib/game/components/flutter_forest.dart @@ -37,12 +37,14 @@ class FlutterForest extends Component with Controls<_FlutterForestController> { final smallRightNest = _ControlledSmallDashNestBumper.b( id: 'small_nest_bumper_b', )..initialPosition = Vector2(23.3, 46.75); + final dashAnimatronic = _DashAnimatronic(); await addAll([ signPost, smallLeftNest, smallRightNest, bigNest, + dashAnimatronic, ]); } } @@ -68,13 +70,65 @@ class _FlutterForestController extends ComponentController void onNewState(GameState state) { super.onNewState(state); - gameRef.add( + _startDashAnimatronic(); + _addBonusBall(); + } + + void _startDashAnimatronic() { + component.descendants().whereType<_DashAnimatronic>().single.playing = true; + } + + Future _addBonusBall() async { + await Future.delayed(const Duration(milliseconds: 700)); + await gameRef.add( ControlledBall.bonus(theme: gameRef.theme) ..initialPosition = Vector2(17.2, 52.7), ); } } +class _DashAnimatronic extends SpriteAnimationComponent with HasGameRef { + _DashAnimatronic() + : super( + size: Vector2(15, 15), + anchor: Anchor.center, + position: Vector2(20, -66), + playing: false, + ); + + late final SpriteAnimation _animation; + + @override + Future? onLoad() async { + await super.onLoad(); + + final spriteSheet = await gameRef.images.load( + Assets.images.dash.animatronic.keyName, + ); + + _animation = SpriteAnimation.fromFrameData( + spriteSheet, + SpriteAnimationData.sequenced( + amount: 96, + amountPerRow: 12, + stepTime: 1 / 24, + textureSize: Vector2(150, 150), + loop: false, + ), + ); + animation = _animation; + } + + @override + void update(double dt) { + super.update(dt); + if (_animation.isLastFrame) { + _animation.reset(); + playing = false; + } + } +} + class _ControlledBigDashNestBumper extends BigDashNestBumper with Controls, ScorePoints { _ControlledBigDashNestBumper({required String id}) : super() { diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 050b2cd3..b6975f2b 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -25,12 +25,13 @@ extension PinballGameAssetsX on PinballGame { ), images.load(components.Assets.images.dino.dinoLandTop.keyName), images.load(components.Assets.images.dino.dinoLandBottom.keyName), - images.load(components.Assets.images.dashBumper.a.active.keyName), - images.load(components.Assets.images.dashBumper.a.inactive.keyName), - images.load(components.Assets.images.dashBumper.b.active.keyName), - images.load(components.Assets.images.dashBumper.b.inactive.keyName), - images.load(components.Assets.images.dashBumper.main.active.keyName), - images.load(components.Assets.images.dashBumper.main.inactive.keyName), + images.load(components.Assets.images.dash.animatronic.keyName), + images.load(components.Assets.images.dash.bumper.a.active.keyName), + images.load(components.Assets.images.dash.bumper.a.inactive.keyName), + images.load(components.Assets.images.dash.bumper.b.active.keyName), + images.load(components.Assets.images.dash.bumper.b.inactive.keyName), + images.load(components.Assets.images.dash.bumper.main.active.keyName), + images.load(components.Assets.images.dash.bumper.main.inactive.keyName), images.load(components.Assets.images.boundary.bottom.keyName), images.load(components.Assets.images.boundary.outer.keyName), images.load(components.Assets.images.spaceship.saucer.keyName), diff --git a/packages/pinball_components/assets/images/dash/animatronic.png b/packages/pinball_components/assets/images/dash/animatronic.png new file mode 100644 index 00000000..e67cafef Binary files /dev/null and b/packages/pinball_components/assets/images/dash/animatronic.png differ diff --git a/packages/pinball_components/assets/images/dash_bumper/a/active.png b/packages/pinball_components/assets/images/dash/bumper/a/active.png similarity index 100% rename from packages/pinball_components/assets/images/dash_bumper/a/active.png rename to packages/pinball_components/assets/images/dash/bumper/a/active.png diff --git a/packages/pinball_components/assets/images/dash_bumper/a/inactive.png b/packages/pinball_components/assets/images/dash/bumper/a/inactive.png similarity index 100% rename from packages/pinball_components/assets/images/dash_bumper/a/inactive.png rename to packages/pinball_components/assets/images/dash/bumper/a/inactive.png diff --git a/packages/pinball_components/assets/images/dash_bumper/b/active.png b/packages/pinball_components/assets/images/dash/bumper/b/active.png similarity index 100% rename from packages/pinball_components/assets/images/dash_bumper/b/active.png rename to packages/pinball_components/assets/images/dash/bumper/b/active.png diff --git a/packages/pinball_components/assets/images/dash_bumper/b/inactive.png b/packages/pinball_components/assets/images/dash/bumper/b/inactive.png similarity index 100% rename from packages/pinball_components/assets/images/dash_bumper/b/inactive.png rename to packages/pinball_components/assets/images/dash/bumper/b/inactive.png diff --git a/packages/pinball_components/assets/images/dash_bumper/main/active.png b/packages/pinball_components/assets/images/dash/bumper/main/active.png similarity index 100% rename from packages/pinball_components/assets/images/dash_bumper/main/active.png rename to packages/pinball_components/assets/images/dash/bumper/main/active.png diff --git a/packages/pinball_components/assets/images/dash_bumper/main/inactive.png b/packages/pinball_components/assets/images/dash/bumper/main/inactive.png similarity index 100% rename from packages/pinball_components/assets/images/dash_bumper/main/inactive.png rename to packages/pinball_components/assets/images/dash/bumper/main/inactive.png diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 518d3237..577533a0 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -17,8 +17,7 @@ class $AssetsImagesGen { $AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen(); $AssetsImagesChromeDinoGen get chromeDino => const $AssetsImagesChromeDinoGen(); - $AssetsImagesDashBumperGen get dashBumper => - const $AssetsImagesDashBumperGen(); + $AssetsImagesDashGen get dash => const $AssetsImagesDashGen(); $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); $AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen(); @@ -71,13 +70,14 @@ class $AssetsImagesChromeDinoGen { const AssetGenImage('assets/images/chrome_dino/mouth.png'); } -class $AssetsImagesDashBumperGen { - const $AssetsImagesDashBumperGen(); +class $AssetsImagesDashGen { + const $AssetsImagesDashGen(); - $AssetsImagesDashBumperAGen get a => const $AssetsImagesDashBumperAGen(); - $AssetsImagesDashBumperBGen get b => const $AssetsImagesDashBumperBGen(); - $AssetsImagesDashBumperMainGen get main => - const $AssetsImagesDashBumperMainGen(); + /// File path: assets/images/dash/animatronic.png + AssetGenImage get animatronic => + const AssetGenImage('assets/images/dash/animatronic.png'); + + $AssetsImagesDashBumperGen get bumper => const $AssetsImagesDashBumperGen(); } class $AssetsImagesDinoGen { @@ -173,40 +173,13 @@ class $AssetsImagesSparkyBumperGen { $AssetsImagesSparkyBumperCGen get c => const $AssetsImagesSparkyBumperCGen(); } -class $AssetsImagesDashBumperAGen { - const $AssetsImagesDashBumperAGen(); - - /// File path: assets/images/dash_bumper/a/active.png - AssetGenImage get active => - const AssetGenImage('assets/images/dash_bumper/a/active.png'); - - /// File path: assets/images/dash_bumper/a/inactive.png - AssetGenImage get inactive => - const AssetGenImage('assets/images/dash_bumper/a/inactive.png'); -} - -class $AssetsImagesDashBumperBGen { - const $AssetsImagesDashBumperBGen(); - - /// File path: assets/images/dash_bumper/b/active.png - AssetGenImage get active => - const AssetGenImage('assets/images/dash_bumper/b/active.png'); - - /// File path: assets/images/dash_bumper/b/inactive.png - AssetGenImage get inactive => - const AssetGenImage('assets/images/dash_bumper/b/inactive.png'); -} - -class $AssetsImagesDashBumperMainGen { - const $AssetsImagesDashBumperMainGen(); - - /// File path: assets/images/dash_bumper/main/active.png - AssetGenImage get active => - const AssetGenImage('assets/images/dash_bumper/main/active.png'); +class $AssetsImagesDashBumperGen { + const $AssetsImagesDashBumperGen(); - /// File path: assets/images/dash_bumper/main/inactive.png - AssetGenImage get inactive => - const AssetGenImage('assets/images/dash_bumper/main/inactive.png'); + $AssetsImagesDashBumperAGen get a => const $AssetsImagesDashBumperAGen(); + $AssetsImagesDashBumperBGen get b => const $AssetsImagesDashBumperBGen(); + $AssetsImagesDashBumperMainGen get main => + const $AssetsImagesDashBumperMainGen(); } class $AssetsImagesSpaceshipRailGen { @@ -273,6 +246,42 @@ class $AssetsImagesSparkyBumperCGen { const AssetGenImage('assets/images/sparky_bumper/c/inactive.png'); } +class $AssetsImagesDashBumperAGen { + const $AssetsImagesDashBumperAGen(); + + /// File path: assets/images/dash/bumper/a/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/dash/bumper/a/active.png'); + + /// File path: assets/images/dash/bumper/a/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/dash/bumper/a/inactive.png'); +} + +class $AssetsImagesDashBumperBGen { + const $AssetsImagesDashBumperBGen(); + + /// File path: assets/images/dash/bumper/b/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/dash/bumper/b/active.png'); + + /// File path: assets/images/dash/bumper/b/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/dash/bumper/b/inactive.png'); +} + +class $AssetsImagesDashBumperMainGen { + const $AssetsImagesDashBumperMainGen(); + + /// File path: assets/images/dash/bumper/main/active.png + AssetGenImage get active => + const AssetGenImage('assets/images/dash/bumper/main/active.png'); + + /// File path: assets/images/dash/bumper/main/inactive.png + AssetGenImage get inactive => + const AssetGenImage('assets/images/dash/bumper/main/inactive.png'); +} + class Assets { Assets._(); diff --git a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart index 447b4156..cec5b42a 100644 --- a/packages/pinball_components/lib/src/components/dash_nest_bumper.dart +++ b/packages/pinball_components/lib/src/components/dash_nest_bumper.dart @@ -63,8 +63,8 @@ class BigDashNestBumper extends DashNestBumper { /// {@macro dash_nest_bumper} BigDashNestBumper() : super._( - activeAssetPath: Assets.images.dashBumper.main.active.keyName, - inactiveAssetPath: Assets.images.dashBumper.main.inactive.keyName, + activeAssetPath: Assets.images.dash.bumper.main.active.keyName, + inactiveAssetPath: Assets.images.dash.bumper.main.inactive.keyName, spriteComponent: SpriteComponent( anchor: Anchor.center, position: Vector2(0, -0.3), @@ -104,8 +104,8 @@ class SmallDashNestBumper extends DashNestBumper { /// {@macro dash_nest_bumper} SmallDashNestBumper.a() : this._( - activeAssetPath: Assets.images.dashBumper.a.active.keyName, - inactiveAssetPath: Assets.images.dashBumper.a.inactive.keyName, + activeAssetPath: Assets.images.dash.bumper.a.active.keyName, + inactiveAssetPath: Assets.images.dash.bumper.a.inactive.keyName, spriteComponent: SpriteComponent( anchor: Anchor.center, position: Vector2(0.35, -1.2), @@ -115,8 +115,8 @@ class SmallDashNestBumper extends DashNestBumper { /// {@macro dash_nest_bumper} SmallDashNestBumper.b() : this._( - activeAssetPath: Assets.images.dashBumper.b.active.keyName, - inactiveAssetPath: Assets.images.dashBumper.b.inactive.keyName, + activeAssetPath: Assets.images.dash.bumper.b.active.keyName, + inactiveAssetPath: Assets.images.dash.bumper.b.inactive.keyName, spriteComponent: SpriteComponent( anchor: Anchor.center, position: Vector2(0.35, -1.2), diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index b6f71b8b..db14f8a5 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -31,9 +31,10 @@ flutter: - assets/images/dino/ - assets/images/flipper/ - assets/images/launch_ramp/ - - assets/images/dash_bumper/a/ - - assets/images/dash_bumper/b/ - - assets/images/dash_bumper/main/ + - assets/images/dash/ + - assets/images/dash/bumper/a/ + - assets/images/dash/bumper/b/ + - assets/images/dash/bumper/main/ - assets/images/spaceship/ - assets/images/spaceship/rail/ - assets/images/spaceship/ramp/ diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index d85fe54f..b01018d7 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -1,6 +1,7 @@ // ignore_for_file: cascade_invocations import 'package:bloc_test/bloc_test.dart'; +import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -102,7 +103,7 @@ void main() { }); flameTester.test( - 'onNewState adds a new ball', + 'onNewState adds a new ball after a duration', (game) async { final flutterForest = FlutterForest(); await game.ready(); @@ -111,6 +112,8 @@ void main() { final previousBalls = game.descendants().whereType().length; flutterForest.controller.onNewState(MockGameState()); await game.ready(); + await Future.delayed(const Duration(milliseconds: 700)); + await game.ready(); expect( game.descendants().whereType().length, @@ -119,6 +122,42 @@ void main() { }, ); + flameTester.test( + 'onNewState starts Dash animatronic', + (game) async { + final flutterForest = FlutterForest(); + await game.ready(); + await game.ensureAdd(flutterForest); + + flutterForest.controller.onNewState(MockGameState()); + await game.ready(); + + final dashAnimatronic = + game.descendants().whereType().single; + expect(dashAnimatronic.playing, isTrue); + }, + ); + + flameTester.test( + 'Dash animatronic stops animating after animation completes', + (game) async { + final flutterForest = FlutterForest(); + await game.ready(); + await game.ensureAdd(flutterForest); + + flutterForest.controller.onNewState(MockGameState()); + await game.ready(); + + final dashAnimatronic = + game.descendants().whereType().single; + expect(dashAnimatronic.playing, isTrue); + + dashAnimatronic.animation?.setToLast(); + game.update(1); + expect(dashAnimatronic.playing, isFalse); + }, + ); + group('bumpers', () { late Ball ball; late GameBloc gameBloc;