diff --git a/lib/game/components/flutter_forest.dart b/lib/game/components/flutter_forest.dart index 966b0a6b..76699ec9 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()..position = Vector2(20, -66); await addAll([ signPost, smallLeftNest, smallRightNest, bigNest, + dashAnimatronic, ]); } } @@ -68,7 +70,13 @@ class _FlutterForestController extends ComponentController void onNewState(GameState state) { super.onNewState(state); - gameRef.add( + component.firstChild()?.playing = true; + _addBonusBall(); + } + + Future _addBonusBall() async { + await Future.delayed(const Duration(milliseconds: 700)); + await gameRef.add( ControlledBall.bonus(theme: gameRef.theme) ..initialPosition = Vector2(17.2, 52.7), ); diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 9abfa01c..c499f5a4 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 ab64439f..7b535ce8 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -19,8 +19,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(); @@ -87,13 +86,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 { @@ -226,40 +226,13 @@ class $AssetsImagesSparkyGen { const $AssetsImagesSparkyComputerGen(); } -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 { @@ -346,6 +319,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/ball.dart b/packages/pinball_components/lib/src/components/ball.dart index c32b8b18..1261791d 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball.dart @@ -10,7 +10,7 @@ import 'package:pinball_components/pinball_components.dart'; /// {@endtemplate} class Ball extends BodyComponent with Layered, InitialPosition { - /// {@macro ball_body} + /// {@macro ball} Ball({ required this.baseColor, }) { diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index b5e9392b..0348e4df 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -6,6 +6,7 @@ export 'board_side.dart'; export 'boundaries.dart'; export 'camera_zoom.dart'; export 'chrome_dino.dart'; +export 'dash_animatronic.dart'; export 'dash_nest_bumper.dart'; export 'dino_walls.dart'; export 'fire_effect.dart'; diff --git a/packages/pinball_components/lib/src/components/dash_animatronic.dart b/packages/pinball_components/lib/src/components/dash_animatronic.dart new file mode 100644 index 00000000..1ab7e76e --- /dev/null +++ b/packages/pinball_components/lib/src/components/dash_animatronic.dart @@ -0,0 +1,53 @@ +import 'package:flame/components.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// {@template dash_animatronic} +/// Animated Dash that sits on top of the [BigDashNestBumper]. +/// {@endtemplate} +class DashAnimatronic extends SpriteAnimationComponent with HasGameRef { + /// {@macro dash_animatronic} + DashAnimatronic() + : super( + anchor: Anchor.center, + playing: false, + ); + + @override + Future onLoad() async { + await super.onLoad(); + + final spriteSheet = await gameRef.images.load( + Assets.images.dash.animatronic.keyName, + ); + + const amountPerRow = 12; + const amountPerColumn = 8; + final textureSize = Vector2( + spriteSheet.width / amountPerRow, + spriteSheet.height / amountPerColumn, + ); + size = textureSize / 10; + + animation = SpriteAnimation.fromFrameData( + spriteSheet, + SpriteAnimationData.sequenced( + amount: amountPerRow * amountPerColumn, + amountPerRow: amountPerRow, + stepTime: 1 / 24, + textureSize: textureSize, + loop: false, + ), + ); + } + + @override + void update(double dt) { + super.update(dt); + if (animation != null) { + if (animation!.isLastFrame) { + animation!.reset(); + playing = false; + } + } + } +} 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 d63e2312..fc44a74a 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -41,9 +41,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/packages/pinball_components/test/src/components/dash_animatronic_test.dart b/packages/pinball_components/test/src/components/dash_animatronic_test.dart new file mode 100644 index 00000000..b268fee0 --- /dev/null +++ b/packages/pinball_components/test/src/components/dash_animatronic_test.dart @@ -0,0 +1,38 @@ +// ignore_for_file: cascade_invocations + +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() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + group('DashAnimatronic', () { + flameTester.test( + 'loads correctly', + (game) async { + final dashAnimatronic = DashAnimatronic(); + await game.ensureAdd(dashAnimatronic); + + expect(game.contains(dashAnimatronic), isTrue); + }, + ); + + flameTester.test( + 'stops animating after animation completes', + (game) async { + final dashAnimatronic = DashAnimatronic(); + await game.ensureAdd(dashAnimatronic); + + dashAnimatronic.playing = true; + dashAnimatronic.animation?.setToLast(); + game.update(1); + + expect(dashAnimatronic.playing, isFalse); + }, + ); + }); +} diff --git a/test/game/components/flutter_forest_test.dart b/test/game/components/flutter_forest_test.dart index d85fe54f..e9e58985 100644 --- a/test/game/components/flutter_forest_test.dart +++ b/test/game/components/flutter_forest_test.dart @@ -18,7 +18,6 @@ void main() { flameTester.test( 'loads correctly', (game) async { - await game.ready(); final flutterForest = FlutterForest(); await game.ensureAdd(flutterForest); @@ -30,7 +29,6 @@ void main() { flameTester.test( 'a FlutterSignPost', (game) async { - await game.ready(); final flutterForest = FlutterForest(); await game.ensureAdd(flutterForest); @@ -41,10 +39,22 @@ void main() { }, ); + flameTester.test( + 'a DashAnimatronic', + (game) async { + final flutterForest = FlutterForest(); + await game.ensureAdd(flutterForest); + + expect( + flutterForest.firstChild(), + isNotNull, + ); + }, + ); + flameTester.test( 'a BigDashNestBumper', (game) async { - await game.ready(); final flutterForest = FlutterForest(); await game.ensureAdd(flutterForest); @@ -58,7 +68,6 @@ void main() { flameTester.test( 'two SmallDashNestBumper', (game) async { - await game.ready(); final flutterForest = FlutterForest(); await game.ensureAdd(flutterForest); @@ -102,14 +111,15 @@ 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(); await game.ensureAdd(flutterForest); final previousBalls = game.descendants().whereType().length; flutterForest.controller.onNewState(MockGameState()); + + await Future.delayed(const Duration(milliseconds: 700)); await game.ready(); expect( @@ -119,6 +129,20 @@ void main() { }, ); + flameTester.test( + 'onNewState starts Dash animatronic', + (game) async { + final flutterForest = FlutterForest(); + await game.ensureAdd(flutterForest); + + flutterForest.controller.onNewState(MockGameState()); + final dashAnimatronic = + game.descendants().whereType().single; + + expect(dashAnimatronic.playing, isTrue); + }, + ); + group('bumpers', () { late Ball ball; late GameBloc gameBloc;