diff --git a/lib/game/components/google_gallery/behaviors/google_word_bonus_behavior.dart b/lib/game/components/google_gallery/behaviors/google_word_bonus_behavior.dart index 787fcefc..ed19f495 100644 --- a/lib/game/components/google_gallery/behaviors/google_word_bonus_behavior.dart +++ b/lib/game/components/google_gallery/behaviors/google_word_bonus_behavior.dart @@ -19,6 +19,7 @@ class GoogleWordBonusBehavior extends Component { .add(const BonusActivated(GameBonus.googleWord)); readBloc().onBonusAwarded(); add(BonusBallSpawningBehavior()); + add(GoogleWordAnimatingBehavior()); }, ), ); diff --git a/packages/pinball_components/lib/src/components/google_word/behaviors/behaviors.dart b/packages/pinball_components/lib/src/components/google_word/behaviors/behaviors.dart new file mode 100644 index 00000000..02dab3a8 --- /dev/null +++ b/packages/pinball_components/lib/src/components/google_word/behaviors/behaviors.dart @@ -0,0 +1 @@ +export 'google_word_animating_behavior.dart'; diff --git a/packages/pinball_components/lib/src/components/google_word/behaviors/google_word_animating_behavior.dart b/packages/pinball_components/lib/src/components/google_word/behaviors/google_word_animating_behavior.dart new file mode 100644 index 00000000..c16d4a2e --- /dev/null +++ b/packages/pinball_components/lib/src/components/google_word/behaviors/google_word_animating_behavior.dart @@ -0,0 +1,24 @@ +import 'package:flame/components.dart'; +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:pinball_components/pinball_components.dart'; + +class GoogleWordAnimatingBehavior extends TimerComponent + with FlameBlocReader { + GoogleWordAnimatingBehavior() : super(period: 0.35, repeat: true); + + final _maxBlinks = 7; + int _blinks = 0; + + @override + void onTick() { + super.onTick(); + if (_blinks != _maxBlinks * 2) { + bloc.switched(); + _blinks++; + } else { + timer.stop(); + bloc.onAnimationFinished(); + shouldRemove = true; + } + } +} diff --git a/packages/pinball_components/lib/src/components/google_word/cubit/google_word_cubit.dart b/packages/pinball_components/lib/src/components/google_word/cubit/google_word_cubit.dart index 197771d6..8a8b976d 100644 --- a/packages/pinball_components/lib/src/components/google_word/cubit/google_word_cubit.dart +++ b/packages/pinball_components/lib/src/components/google_word/cubit/google_word_cubit.dart @@ -23,7 +23,52 @@ class GoogleWordCubit extends Cubit { } } + void switched() { + switch (state.letterSpriteStates[0]!) { + case GoogleLetterSpriteState.lit: + emit( + GoogleWordState( + letterSpriteStates: { + for (int i = 0; i < _lettersInGoogle; i++) + if (i.isEven) + i: GoogleLetterSpriteState.dimmed + else + i: GoogleLetterSpriteState.lit + }, + ), + ); + break; + case GoogleLetterSpriteState.dimmed: + emit( + GoogleWordState( + letterSpriteStates: { + for (int i = 0; i < _lettersInGoogle; i++) + if (i.isEven) + i: GoogleLetterSpriteState.lit + else + i: GoogleLetterSpriteState.dimmed + }, + ), + ); + break; + } + } + void onBonusAwarded() { + emit( + GoogleWordState( + letterSpriteStates: { + for (int i = 0; i < _lettersInGoogle; i++) + if (i.isEven) + i: GoogleLetterSpriteState.lit + else + i: GoogleLetterSpriteState.dimmed + }, + ), + ); + } + + void onAnimationFinished() { emit(GoogleWordState.initial()); _lastLitLetter = 0; } diff --git a/packages/pinball_components/lib/src/components/google_word/google_word.dart b/packages/pinball_components/lib/src/components/google_word/google_word.dart index 72126d2c..f9c93e2c 100644 --- a/packages/pinball_components/lib/src/components/google_word/google_word.dart +++ b/packages/pinball_components/lib/src/components/google_word/google_word.dart @@ -1,6 +1,7 @@ import 'package:flame/components.dart'; import 'package:pinball_components/pinball_components.dart'; +export 'behaviors/behaviors.dart'; export 'cubit/google_word_cubit.dart'; /// {@template google_word} diff --git a/packages/pinball_components/test/src/components/google_word/behaviors/google_word_animating_behavior_test.dart b/packages/pinball_components/test/src/components/google_word/behaviors/google_word_animating_behavior_test.dart new file mode 100644 index 00000000..7224aeed --- /dev/null +++ b/packages/pinball_components/test/src/components/google_word/behaviors/google_word_animating_behavior_test.dart @@ -0,0 +1,64 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_bloc/flame_bloc.dart'; +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_components/pinball_components.dart'; + +class _TestGame extends Forge2DGame { + Future pump( + GoogleWordAnimatingBehavior child, { + required GoogleWordCubit bloc, + }) async { + await ensureAdd( + FlameBlocProvider.value( + value: bloc, + children: [child], + ), + ); + } +} + +class _MockGoogleWordCubit extends Mock implements GoogleWordCubit {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(_TestGame.new); + + group('GoogleWordAnimatingBehavior', () { + flameTester.testGameWidget( + 'calls switched after timer period reached', + setUp: (game, tester) async { + final behavior = GoogleWordAnimatingBehavior(); + final bloc = _MockGoogleWordCubit(); + await game.pump(behavior, bloc: bloc); + game.update(behavior.timer.limit); + + verify(bloc.switched).called(1); + }, + ); + + flameTester.testGameWidget( + 'calls onAnimationFinished and removes itself ' + 'after all blinks complete', + setUp: (game, tester) async { + final behavior = GoogleWordAnimatingBehavior(); + final bloc = _MockGoogleWordCubit(); + + await game.pump(behavior, bloc: bloc); + for (var i = 0; i <= 14; i++) { + game.update(behavior.timer.limit); + } + await game.ready(); + + verify(bloc.onAnimationFinished).called(1); + expect( + game.descendants().whereType().isEmpty, + isTrue, + ); + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/google_word/cubit/google_word_cubit_test.dart b/packages/pinball_components/test/src/components/google_word/cubit/google_word_cubit_test.dart index 08acfae8..b5000387 100644 --- a/packages/pinball_components/test/src/components/google_word/cubit/google_word_cubit_test.dart +++ b/packages/pinball_components/test/src/components/google_word/cubit/google_word_cubit_test.dart @@ -6,6 +6,21 @@ void main() { group( 'GoogleWordCubit', () { + final litEvens = { + for (int i = 0; i < 6; i++) + if (i.isEven) + i: GoogleLetterSpriteState.lit + else + i: GoogleLetterSpriteState.dimmed + }; + final litOdds = { + for (int i = 0; i < 6; i++) + if (i.isOdd) + i: GoogleLetterSpriteState.lit + else + i: GoogleLetterSpriteState.dimmed + }; + blocTest( 'onRolloverContacted emits first letter lit', build: GoogleWordCubit.new, @@ -25,9 +40,31 @@ void main() { ); blocTest( - 'onBonusAwarded emits initial state', + 'switched emits all even letters lit when first letter is dimmed', + build: GoogleWordCubit.new, + act: (bloc) => bloc.switched(), + expect: () => [GoogleWordState(letterSpriteStates: litEvens)], + ); + + blocTest( + 'switched emits all odd letters lit when first letter is lit', + build: GoogleWordCubit.new, + seed: () => GoogleWordState(letterSpriteStates: litEvens), + act: (bloc) => bloc.switched(), + expect: () => [GoogleWordState(letterSpriteStates: litOdds)], + ); + + blocTest( + 'onBonusAwarded emits all even letters lit', build: GoogleWordCubit.new, act: (bloc) => bloc.onBonusAwarded(), + expect: () => [GoogleWordState(letterSpriteStates: litEvens)], + ); + + blocTest( + 'onAnimationFinished emits initial state', + build: GoogleWordCubit.new, + act: (bloc) => bloc.onAnimationFinished(), expect: () => [GoogleWordState.initial()], ); }, diff --git a/test/game/components/google_gallery/behaviors/google_word_bonus_behavior_test.dart b/test/game/components/google_gallery/behaviors/google_word_bonus_behavior_test.dart index 4b3ec2bd..acabe4c7 100644 --- a/test/game/components/google_gallery/behaviors/google_word_bonus_behavior_test.dart +++ b/test/game/components/google_gallery/behaviors/google_word_bonus_behavior_test.dart @@ -119,8 +119,8 @@ void main() { ); flameTester.testGameWidget( - 'adds BonusBallSpawningBehavior to the game when all letters ' - 'in google word are activated', + 'adds BonusBallSpawningBehavior and GoogleWordAnimatingBehavior ' + 'to the game when all letters in google word are activated', setUp: (game, tester) async { final behavior = GoogleWordBonusBehavior(); final parent = GoogleGallery.test(); @@ -161,6 +161,10 @@ void main() { game.descendants().whereType().length, equals(1), ); + expect( + game.descendants().whereType().length, + equals(1), + ); }, ); });