diff --git a/lib/game/components/controlled_plunger.dart b/lib/game/components/controlled_plunger.dart index 999fae5e..ac9cc19d 100644 --- a/lib/game/components/controlled_plunger.dart +++ b/lib/game/components/controlled_plunger.dart @@ -2,6 +2,7 @@ import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/services.dart'; import 'package:pinball/game/game.dart'; +import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; @@ -14,6 +15,29 @@ class ControlledPlunger extends Plunger with Controls { : super(compressionDistance: compressionDistance) { controller = PlungerController(this); } + + @override + void release() { + super.release(); + + add(PlungerNoisyBehavior()); + } +} + +/// A behavior attached to the plunger when it launches the ball +/// which plays the related sound effects. +class PlungerNoisyBehavior extends Component with HasGameRef { + @override + Future onLoad() async { + await super.onLoad(); + gameRef.player.play(PinballAudio.launcher); + } + + @override + void update(double dt) { + super.update(dt); + removeFromParent(); + } } /// {@template plunger_controller} diff --git a/packages/pinball_audio/assets/sfx/launcher.mp3 b/packages/pinball_audio/assets/sfx/launcher.mp3 new file mode 100644 index 00000000..fde95720 Binary files /dev/null and b/packages/pinball_audio/assets/sfx/launcher.mp3 differ diff --git a/packages/pinball_audio/lib/gen/assets.gen.dart b/packages/pinball_audio/lib/gen/assets.gen.dart index 2bace523..916906c4 100644 --- a/packages/pinball_audio/lib/gen/assets.gen.dart +++ b/packages/pinball_audio/lib/gen/assets.gen.dart @@ -14,11 +14,13 @@ class $AssetsMusicGen { class $AssetsSfxGen { const $AssetsSfxGen(); + String get afterLaunch => 'assets/sfx/after_launch.mp3'; String get bumperA => 'assets/sfx/bumper_a.mp3'; String get bumperB => 'assets/sfx/bumper_b.mp3'; String get gameOverVoiceOver => 'assets/sfx/game_over_voice_over.mp3'; String get google => 'assets/sfx/google.mp3'; String get ioPinballVoiceOver => 'assets/sfx/io_pinball_voice_over.mp3'; + String get launcher => 'assets/sfx/launcher.mp3'; } class Assets { diff --git a/packages/pinball_audio/lib/src/pinball_audio.dart b/packages/pinball_audio/lib/src/pinball_audio.dart index dd3e8242..56289417 100644 --- a/packages/pinball_audio/lib/src/pinball_audio.dart +++ b/packages/pinball_audio/lib/src/pinball_audio.dart @@ -22,6 +22,9 @@ enum PinballAudio { /// Game over gameOverVoiceOver, + + /// Launcher + launcher, } /// Defines the contract of the creation of an [AudioPool]. @@ -158,6 +161,11 @@ class PinballPlayer { playSingleAudio: _playSingleAudio, path: Assets.sfx.google, ), + PinballAudio.launcher: _SimplePlayAudio( + preCacheSingleAudio: _preCacheSingleAudio, + playSingleAudio: _playSingleAudio, + path: Assets.sfx.launcher, + ), PinballAudio.ioPinballVoiceOver: _SimplePlayAudio( preCacheSingleAudio: _preCacheSingleAudio, playSingleAudio: _playSingleAudio, diff --git a/packages/pinball_audio/test/src/pinball_audio_test.dart b/packages/pinball_audio/test/src/pinball_audio_test.dart index b7760aa5..fdcd661b 100644 --- a/packages/pinball_audio/test/src/pinball_audio_test.dart +++ b/packages/pinball_audio/test/src/pinball_audio_test.dart @@ -151,6 +151,10 @@ void main() { 'packages/pinball_audio/assets/sfx/game_over_voice_over.mp3', ), ).called(1); + verify( + () => preCacheSingleAudio + .onCall('packages/pinball_audio/assets/sfx/launcher.mp3'), + ).called(1); verify( () => preCacheSingleAudio .onCall('packages/pinball_audio/assets/music/background.mp3'), @@ -219,6 +223,18 @@ void main() { }); }); + group('launcher', () { + test('plays the correct file', () async { + await Future.wait(player.load()); + player.play(PinballAudio.launcher); + + verify( + () => playSingleAudio + .onCall('packages/pinball_audio/${Assets.sfx.launcher}'), + ).called(1); + }); + }); + group('ioPinballVoiceOver', () { test('plays the correct file', () async { await Future.wait(player.load()); diff --git a/test/game/components/controlled_plunger_test.dart b/test/game/components/controlled_plunger_test.dart index f91b0c37..211cf82f 100644 --- a/test/game/components/controlled_plunger_test.dart +++ b/test/game/components/controlled_plunger_test.dart @@ -1,18 +1,25 @@ +// ignore_for_file: cascade_invocations + import 'dart:collection'; import 'package:bloc_test/bloc_test.dart'; -import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/game.dart'; +import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; import '../../helpers/helpers.dart'; class _MockGameBloc extends Mock implements GameBloc {} +class _MockPinballPlayer extends Mock implements PinballPlayer {} + +class _MockPinballGame extends Mock implements PinballGame {} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); final flameTester = FlameTester(EmptyPinballTestGame.new); @@ -20,15 +27,21 @@ void main() { group('PlungerController', () { late GameBloc gameBloc; - setUp(() { - gameBloc = _MockGameBloc(); - }); - final flameBlocTester = FlameBlocTester( gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, ); + late Plunger plunger; + late PlungerController controller; + + setUp(() { + gameBloc = _MockGameBloc(); + plunger = ControlledPlunger(compressionDistance: 10); + controller = PlungerController(plunger); + plunger.add(controller); + }); + group('onKeyEvent', () { final downKeys = UnmodifiableListView([ LogicalKeyboardKey.arrowDown, @@ -36,15 +49,6 @@ void main() { LogicalKeyboardKey.keyS, ]); - late Plunger plunger; - late PlungerController controller; - - setUp(() { - plunger = Plunger(compressionDistance: 10); - controller = PlungerController(plunger); - plunger.add(controller); - }); - testRawKeyDownEvents(downKeys, (event) { flameTester.test( 'moves down ' @@ -129,5 +133,50 @@ void main() { ); }); }); + + flameTester.test( + 'adds the PlungerNoisyBehavior plunger is released', + (game) async { + await game.ensureAdd(plunger); + plunger.body.setTransform(Vector2(0, 1), 0); + plunger.release(); + + await game.ready(); + final count = + game.descendants().whereType().length; + expect(count, equals(1)); + }, + ); + }); + + group('PlungerNoisyBehavior', () { + late PinballGame game; + late PinballPlayer player; + late PlungerNoisyBehavior behavior; + + setUp(() { + game = _MockPinballGame(); + player = _MockPinballPlayer(); + + when(() => game.player).thenReturn(player); + behavior = PlungerNoisyBehavior(); + behavior.mockGameRef(game); + }); + + test('plays the correct sound on load', () async { + await behavior.onLoad(); + + verify(() => player.play(PinballAudio.launcher)).called(1); + }); + + test('is removed on the first update', () { + final parent = Component(); + parent.add(behavior); + parent.update(0); // Run a tick to ensure it is added + + behavior.update(0); // Run its own update where the removal happens + + expect(behavior.shouldRemove, isTrue); + }); }); }