diff --git a/lib/game/behaviors/behaviors.dart b/lib/game/behaviors/behaviors.dart index 243fff82..44cce1df 100644 --- a/lib/game/behaviors/behaviors.dart +++ b/lib/game/behaviors/behaviors.dart @@ -1,4 +1,4 @@ export 'ball_spawning_behavior.dart'; -export 'bumper_noisy_behavior.dart'; +export 'bumper_noise_behavior.dart'; export 'camera_focusing_behavior.dart'; export 'scoring_behavior.dart'; diff --git a/lib/game/behaviors/bumper_noisy_behavior.dart b/lib/game/behaviors/bumper_noise_behavior.dart similarity index 87% rename from lib/game/behaviors/bumper_noisy_behavior.dart rename to lib/game/behaviors/bumper_noise_behavior.dart index 86c9f7b0..e89ec23a 100644 --- a/lib/game/behaviors/bumper_noisy_behavior.dart +++ b/lib/game/behaviors/bumper_noise_behavior.dart @@ -6,7 +6,7 @@ import 'package:pinball/game/pinball_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_flame/pinball_flame.dart'; -class BumperNoisyBehavior extends ContactBehavior with HasGameRef { +class BumperNoiseBehavior extends ContactBehavior with HasGameRef { @override void beginContact(Object other, Contact contact) { super.beginContact(other, contact); diff --git a/lib/game/components/android_acres/android_acres.dart b/lib/game/components/android_acres/android_acres.dart index 649ef196..af4ec451 100644 --- a/lib/game/components/android_acres/android_acres.dart +++ b/lib/game/components/android_acres/android_acres.dart @@ -35,19 +35,19 @@ class AndroidAcres extends Component { AndroidBumper.a( children: [ ScoringContactBehavior(points: Points.twentyThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(-25, 1.3), AndroidBumper.b( children: [ ScoringContactBehavior(points: Points.twentyThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(-32.8, -9.2), AndroidBumper.cow( children: [ ScoringContactBehavior(points: Points.twentyThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(-20.5, -13.8), AndroidSpaceshipBonusBehavior(), diff --git a/lib/game/components/controlled_plunger.dart b/lib/game/components/controlled_plunger.dart index 999fae5e..bcebbacc 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(PlungerNoiseBehavior()); + } +} + +/// A behavior attached to the plunger when it launches the ball +/// which plays the related sound effects. +class PlungerNoiseBehavior 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/lib/game/components/flutter_forest/flutter_forest.dart b/lib/game/components/flutter_forest/flutter_forest.dart index 259b6bb2..a9219ba0 100644 --- a/lib/game/components/flutter_forest/flutter_forest.dart +++ b/lib/game/components/flutter_forest/flutter_forest.dart @@ -19,25 +19,25 @@ class FlutterForest extends Component with ZIndex { Signpost( children: [ ScoringContactBehavior(points: Points.fiveThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(8.35, -58.3), DashNestBumper.main( children: [ ScoringContactBehavior(points: Points.twoHundredThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(18.55, -59.35), DashNestBumper.a( children: [ ScoringContactBehavior(points: Points.twentyThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(8.95, -51.95), DashNestBumper.b( children: [ ScoringContactBehavior(points: Points.twentyThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(22.3, -46.75), DashAnimatronic()..position = Vector2(20, -66), diff --git a/lib/game/components/sparky_scorch.dart b/lib/game/components/sparky_scorch.dart index 5a266b4e..e7e5004a 100644 --- a/lib/game/components/sparky_scorch.dart +++ b/lib/game/components/sparky_scorch.dart @@ -18,19 +18,19 @@ class SparkyScorch extends Component { SparkyBumper.a( children: [ ScoringContactBehavior(points: Points.twentyThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(-22.9, -41.65), SparkyBumper.b( children: [ ScoringContactBehavior(points: Points.twentyThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(-21.25, -57.9), SparkyBumper.c( children: [ ScoringContactBehavior(points: Points.twentyThousand), - BumperNoisyBehavior(), + BumperNoiseBehavior(), ], )..initialPosition = Vector2(-3.3, -52.55), SparkyComputerSensor()..initialPosition = Vector2(-13, -49.9), 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/behaviors/bumper_noisy_behavior_test.dart b/test/game/behaviors/bumper_noisy_behavior_test.dart index 18d90fbd..e860a094 100644 --- a/test/game/behaviors/bumper_noisy_behavior_test.dart +++ b/test/game/behaviors/bumper_noisy_behavior_test.dart @@ -23,7 +23,7 @@ class _MockContact extends Mock implements Contact {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('BumperNoisyBehavior', () {}); + group('BumperNoiseBehavior', () {}); late PinballPlayer player; final flameTester = FlameTester( @@ -37,7 +37,7 @@ void main() { flameTester.testGameWidget( 'plays bumper sound', setUp: (game, _) async { - final behavior = BumperNoisyBehavior(); + final behavior = BumperNoiseBehavior(); final parent = _TestBodyComponent(); await game.ensureAdd(parent); await parent.ensureAdd(behavior); diff --git a/test/game/components/android_acres/android_acres_test.dart b/test/game/components/android_acres/android_acres_test.dart index 8434d5f8..5de7576b 100644 --- a/test/game/components/android_acres/android_acres_test.dart +++ b/test/game/components/android_acres/android_acres_test.dart @@ -2,7 +2,7 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/game/behaviors/bumper_noisy_behavior.dart'; +import 'package:pinball/game/behaviors/bumper_noise_behavior.dart'; import 'package:pinball/game/components/android_acres/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -103,13 +103,13 @@ void main() { ); flameTester.test( - 'three AndroidBumpers with BumperNoisyBehavior', + 'three AndroidBumpers with BumperNoiseBehavior', (game) async { await game.ensureAdd(AndroidAcres()); final bumpers = game.descendants().whereType(); for (final bumper in bumpers) { expect( - bumper.firstChild(), + bumper.firstChild(), isNotNull, ); } diff --git a/test/game/components/controlled_plunger_test.dart b/test/game/components/controlled_plunger_test.dart index f91b0c37..f772b39a 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 PlungerNoiseBehavior 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('PlungerNoiseBehavior', () { + late PinballGame game; + late PinballPlayer player; + late PlungerNoiseBehavior behavior; + + setUp(() { + game = _MockPinballGame(); + player = _MockPinballPlayer(); + + when(() => game.player).thenReturn(player); + behavior = PlungerNoiseBehavior(); + 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); + }); }); } diff --git a/test/game/components/flutter_forest/flutter_forest_test.dart b/test/game/components/flutter_forest/flutter_forest_test.dart index 6dddcd7b..bc0e5ff4 100644 --- a/test/game/components/flutter_forest/flutter_forest_test.dart +++ b/test/game/components/flutter_forest/flutter_forest_test.dart @@ -76,14 +76,14 @@ void main() { ); flameTester.test( - 'three DashNestBumpers with BumperNoisyBehavior', + 'three DashNestBumpers with BumperNoiseBehavior', (game) async { final flutterForest = FlutterForest(); await game.ensureAdd(ZCanvasComponent(children: [flutterForest])); final bumpers = game.descendants().whereType(); for (final bumper in bumpers) { expect( - bumper.firstChild(), + bumper.firstChild(), isNotNull, ); } diff --git a/test/game/components/sparky_scorch_test.dart b/test/game/components/sparky_scorch_test.dart index 5df250dd..0eeb9b45 100644 --- a/test/game/components/sparky_scorch_test.dart +++ b/test/game/components/sparky_scorch_test.dart @@ -77,13 +77,13 @@ void main() { ); flameTester.test( - 'three SparkyBumpers with BumperNoisyBehavior', + 'three SparkyBumpers with BumperNoiseBehavior', (game) async { await game.ensureAdd(SparkyScorch()); final bumpers = game.descendants().whereType(); for (final bumper in bumpers) { expect( - bumper.firstChild(), + bumper.firstChild(), isNotNull, ); }