diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index b96b6a65..63321974 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -12,4 +12,4 @@ export 'google_word/google_word.dart'; export 'launcher.dart'; export 'multiballs/multiballs.dart'; export 'multipliers/multipliers.dart'; -export 'sparky_scorch.dart'; +export 'sparky_scorch/sparky_scorch.dart'; diff --git a/lib/game/components/sparky_scorch.dart b/lib/game/components/sparky_scorch/sparky_scorch.dart similarity index 93% rename from lib/game/components/sparky_scorch.dart rename to lib/game/components/sparky_scorch/sparky_scorch.dart index b820e89d..0e55a940 100644 --- a/lib/game/components/sparky_scorch.dart +++ b/lib/game/components/sparky_scorch/sparky_scorch.dart @@ -4,8 +4,11 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/components/components.dart'; +import 'package:pinball/game/components/sparky_scorch/sparky_scorch_noise_behavior.dart'; import 'package:pinball_components/pinball_components.dart'; +export 'sparky_scorch_noise_behavior.dart'; + /// {@template sparky_scorch} /// Area positioned at the top left of the board containing the /// [SparkyComputer], [SparkyAnimatronic], and [SparkyBumper]s. @@ -52,6 +55,7 @@ class SparkyComputerSensor extends BodyComponent renderBody: false, children: [ ScoringContactBehavior(points: Points.twentyThousand), + SparkyScorchNoiseBehavior(), ], ); diff --git a/lib/game/components/sparky_scorch/sparky_scorch_noise_behavior.dart b/lib/game/components/sparky_scorch/sparky_scorch_noise_behavior.dart new file mode 100644 index 00000000..5ac61747 --- /dev/null +++ b/lib/game/components/sparky_scorch/sparky_scorch_noise_behavior.dart @@ -0,0 +1,15 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_audio/pinball_audio.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +/// Plays the sparky animation sound when in contact with a [Ball] +class SparkyScorchNoiseBehavior extends ContactBehavior { + @override + void beginContact(Object other, Contact contact) { + super.beginContact(other, contact); + if (other is Ball) { + readProvider().play(PinballAudio.sparky); + } + } +} diff --git a/packages/pinball_audio/assets/sfx/sparky.mp3 b/packages/pinball_audio/assets/sfx/sparky.mp3 new file mode 100644 index 00000000..14bf0ec6 Binary files /dev/null and b/packages/pinball_audio/assets/sfx/sparky.mp3 differ diff --git a/packages/pinball_audio/lib/gen/assets.gen.dart b/packages/pinball_audio/lib/gen/assets.gen.dart index 916906c4..08e83d87 100644 --- a/packages/pinball_audio/lib/gen/assets.gen.dart +++ b/packages/pinball_audio/lib/gen/assets.gen.dart @@ -14,13 +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'; + String get sparky => 'assets/sfx/sparky.mp3'; } class Assets { diff --git a/packages/pinball_audio/lib/src/pinball_audio.dart b/packages/pinball_audio/lib/src/pinball_audio.dart index 56289417..e7b38b35 100644 --- a/packages/pinball_audio/lib/src/pinball_audio.dart +++ b/packages/pinball_audio/lib/src/pinball_audio.dart @@ -25,6 +25,9 @@ enum PinballAudio { /// Launcher launcher, + + /// Google + sparky, } /// Defines the contract of the creation of an [AudioPool]. @@ -161,6 +164,11 @@ class PinballPlayer { playSingleAudio: _playSingleAudio, path: Assets.sfx.google, ), + PinballAudio.sparky: _SimplePlayAudio( + preCacheSingleAudio: _preCacheSingleAudio, + playSingleAudio: _playSingleAudio, + path: Assets.sfx.sparky, + ), PinballAudio.launcher: _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 fdcd661b..39060eb2 100644 --- a/packages/pinball_audio/test/src/pinball_audio_test.dart +++ b/packages/pinball_audio/test/src/pinball_audio_test.dart @@ -141,6 +141,10 @@ void main() { () => preCacheSingleAudio .onCall('packages/pinball_audio/assets/sfx/google.mp3'), ).called(1); + verify( + () => preCacheSingleAudio + .onCall('packages/pinball_audio/assets/sfx/sparky.mp3'), + ).called(1); verify( () => preCacheSingleAudio.onCall( 'packages/pinball_audio/assets/sfx/io_pinball_voice_over.mp3', @@ -211,7 +215,7 @@ void main() { }); }); - group('googleBonus', () { + group('google', () { test('plays the correct file', () async { await Future.wait(player.load()); player.play(PinballAudio.google); @@ -223,6 +227,18 @@ void main() { }); }); + group('sparky', () { + test('plays the correct file', () async { + await Future.wait(player.load()); + player.play(PinballAudio.sparky); + + verify( + () => playSingleAudio + .onCall('packages/pinball_audio/${Assets.sfx.sparky}'), + ).called(1); + }); + }); + group('launcher', () { test('plays the correct file', () async { await Future.wait(player.load()); diff --git a/test/game/components/sparky_scorch/sparky_scorch_noise_behavior_test.dart b/test/game/components/sparky_scorch/sparky_scorch_noise_behavior_test.dart new file mode 100644 index 00000000..14cb39e8 --- /dev/null +++ b/test/game/components/sparky_scorch/sparky_scorch_noise_behavior_test.dart @@ -0,0 +1,75 @@ +// 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:mocktail/mocktail.dart'; +import 'package:pinball/game/components/components.dart'; +import 'package:pinball_audio/pinball_audio.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +class _TestGame extends Forge2DGame { + Future pump(_TestBodyComponent child, {required PinballPlayer player}) { + return ensureAdd( + FlameProvider.value( + player, + children: [ + child, + ], + ), + ); + } +} + +class _TestBodyComponent extends BodyComponent { + @override + Body createBody() => world.createBody(BodyDef()); +} + +class _MockPinballPlayer extends Mock implements PinballPlayer {} + +class _MockContact extends Mock implements Contact {} + +class _MockBall extends Mock implements Ball {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('SparkyScorchNoiseBehavior', () {}); + + late PinballPlayer player; + final flameTester = FlameTester(_TestGame.new); + + setUp(() { + player = _MockPinballPlayer(); + }); + + flameTester.testGameWidget( + 'plays sparky sound', + setUp: (game, _) async { + final behavior = SparkyScorchNoiseBehavior(); + final parent = _TestBodyComponent(); + await game.pump(parent, player: player); + await parent.ensureAdd(behavior); + behavior.beginContact(_MockBall(), _MockContact()); + }, + verify: (_, __) async { + verify(() => player.play(PinballAudio.sparky)).called(1); + }, + ); + + flameTester.testGameWidget( + 'plays nothing when it is not a ball', + setUp: (game, _) async { + final behavior = SparkyScorchNoiseBehavior(); + final parent = _TestBodyComponent(); + await game.pump(parent, player: player); + await parent.ensureAdd(behavior); + behavior.beginContact(Object(), _MockContact()); + }, + verify: (_, __) async { + verifyNever(() => player.play(PinballAudio.sparky)); + }, + ); +} diff --git a/test/game/components/sparky_scorch_test.dart b/test/game/components/sparky_scorch/sparky_scorch_test.dart similarity index 100% rename from test/game/components/sparky_scorch_test.dart rename to test/game/components/sparky_scorch/sparky_scorch_test.dart