diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 89f03dfa..f621f1e2 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -9,20 +9,24 @@ import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_ui/pinball_ui.dart'; +import 'package:share_repository/share_repository.dart'; class App extends StatelessWidget { const App({ Key? key, required AuthenticationRepository authenticationRepository, required LeaderboardRepository leaderboardRepository, + required ShareRepository shareRepository, required PinballAudioPlayer pinballAudioPlayer, }) : _authenticationRepository = authenticationRepository, _leaderboardRepository = leaderboardRepository, + _shareRepository = shareRepository, _pinballAudioPlayer = pinballAudioPlayer, super(key: key); final AuthenticationRepository _authenticationRepository; final LeaderboardRepository _leaderboardRepository; + final ShareRepository _shareRepository; final PinballAudioPlayer _pinballAudioPlayer; @override @@ -31,6 +35,7 @@ class App extends StatelessWidget { providers: [ RepositoryProvider.value(value: _authenticationRepository), RepositoryProvider.value(value: _leaderboardRepository), + RepositoryProvider.value(value: _shareRepository), RepositoryProvider.value(value: _pinballAudioPlayer), ], child: MultiBlocProvider( diff --git a/lib/assets_manager/views/assets_loading_page.dart b/lib/assets_manager/views/assets_loading_page.dart index 4e75a3a5..72476064 100644 --- a/lib/assets_manager/views/assets_loading_page.dart +++ b/lib/assets_manager/views/assets_loading_page.dart @@ -17,29 +17,32 @@ class AssetsLoadingPage extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; final headline1 = Theme.of(context).textTheme.headline1; - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Assets.images.loadingGame.ioPinball.image(), - ), - const SizedBox(height: 40), - AnimatedEllipsisText( - l10n.loading, - style: headline1, - ), - const SizedBox(height: 40), - FractionallySizedBox( - widthFactor: 0.8, - child: BlocBuilder( - builder: (context, state) { - return PinballLoadingIndicator(value: state.progress); - }, + return Container( + decoration: const CrtBackground(), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Assets.images.loadingGame.ioPinball.image(), ), - ), - ], + const SizedBox(height: 40), + AnimatedEllipsisText( + l10n.loading, + style: headline1, + ), + const SizedBox(height: 40), + FractionallySizedBox( + widthFactor: 0.8, + child: BlocBuilder( + builder: (context, state) { + return PinballLoadingIndicator(value: state.progress); + }, + ), + ), + ], + ), ), ); } diff --git a/lib/game/behaviors/behaviors.dart b/lib/game/behaviors/behaviors.dart index 5900f2b3..8d5ee04e 100644 --- a/lib/game/behaviors/behaviors.dart +++ b/lib/game/behaviors/behaviors.dart @@ -1,7 +1,7 @@ export 'ball_spawning_behavior.dart'; -export 'ball_theming_behavior.dart'; export 'bonus_ball_spawning_behavior.dart'; export 'bonus_noise_behavior.dart'; export 'bumper_noise_behavior.dart'; export 'camera_focusing_behavior.dart'; +export 'character_selection_behavior.dart'; export 'scoring_behavior.dart'; diff --git a/lib/game/behaviors/ball_theming_behavior.dart b/lib/game/behaviors/character_selection_behavior.dart similarity index 57% rename from lib/game/behaviors/ball_theming_behavior.dart rename to lib/game/behaviors/character_selection_behavior.dart index 5e12a720..27003d75 100644 --- a/lib/game/behaviors/ball_theming_behavior.dart +++ b/lib/game/behaviors/character_selection_behavior.dart @@ -3,18 +3,25 @@ import 'package:flame_bloc/flame_bloc.dart'; import 'package:pinball/select_character/select_character.dart'; import 'package:pinball_components/pinball_components.dart'; -/// Updates the launch [Ball] to reflect character selections. -class BallThemingBehavior extends Component +/// Updates the [ArcadeBackground] and launch [Ball] to reflect character +/// selections. +class CharacterSelectionBehavior extends Component with FlameBlocListenable, HasGameRef { @override void onNewState(CharacterThemeState state) { + gameRef + .descendants() + .whereType() + .single + .bloc + .onCharacterSelected(state.characterTheme); gameRef .descendants() .whereType() .single .bloc - .onThemeChanged(state.characterTheme); + .onCharacterSelected(state.characterTheme); } } diff --git a/lib/game/components/backbox/backbox.dart b/lib/game/components/backbox/backbox.dart index e455e89f..dad2c118 100644 --- a/lib/game/components/backbox/backbox.dart +++ b/lib/game/components/backbox/backbox.dart @@ -6,10 +6,13 @@ import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; import 'package:pinball/game/components/backbox/displays/displays.dart'; import 'package:pinball/game/game.dart'; +import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' hide Assets; +import 'package:pinball_ui/pinball_ui.dart'; import 'package:platform_helper/platform_helper.dart'; +import 'package:share_repository/share_repository.dart'; /// {@template backbox} /// The [Backbox] of the pinball machine. @@ -18,21 +21,26 @@ class Backbox extends PositionComponent with ZIndex, HasGameRef { /// {@macro backbox} Backbox({ required LeaderboardRepository leaderboardRepository, + required ShareRepository shareRepository, required List? entries, }) : _bloc = BackboxBloc( leaderboardRepository: leaderboardRepository, initialEntries: entries, ), + _shareRepository = shareRepository, _platformHelper = PlatformHelper(); /// {@macro backbox} @visibleForTesting Backbox.test({ required BackboxBloc bloc, + required ShareRepository shareRepository, required PlatformHelper platformHelper, }) : _bloc = bloc, + _shareRepository = shareRepository, _platformHelper = platformHelper; + final ShareRepository _shareRepository; late final Component _display; final BackboxBloc _bloc; final PlatformHelper _platformHelper; @@ -87,6 +95,8 @@ class Backbox extends PositionComponent with ZIndex, HasGameRef { ), ); } else if (state is InitialsSuccessState) { + gameRef.overlays.remove(PinballGame.mobileControlsOverlay); + _display.add( GameOverInfoDisplay( onShare: () { @@ -94,6 +104,20 @@ class Backbox extends PositionComponent with ZIndex, HasGameRef { }, ), ); + } else if (state is ShareState) { + _display.add( + ShareDisplay( + onShare: (platform) { + final message = readProvider() + .iGotScoreAtPinball(state.score); + final url = _shareRepository.shareText( + value: message, + platform: platform, + ); + openLink(url); + }, + ), + ); } else if (state is InitialsFailureState) { _display.add( InitialsSubmissionFailureDisplay( diff --git a/lib/game/components/backbox/bloc/backbox_bloc.dart b/lib/game/components/backbox/bloc/backbox_bloc.dart index 8a89b1bd..cfac4c6f 100644 --- a/lib/game/components/backbox/bloc/backbox_bloc.dart +++ b/lib/game/components/backbox/bloc/backbox_bloc.dart @@ -75,9 +75,7 @@ class BackboxBloc extends Bloc { Emitter emit, ) async { emit( - ShareState( - score: event.score, - ), + ShareState(score: event.score), ); } diff --git a/lib/game/components/backbox/displays/displays.dart b/lib/game/components/backbox/displays/displays.dart index 2b8a38ae..f80c0b54 100644 --- a/lib/game/components/backbox/displays/displays.dart +++ b/lib/game/components/backbox/displays/displays.dart @@ -5,3 +5,4 @@ export 'initials_submission_success_display.dart'; export 'leaderboard_display.dart'; export 'leaderboard_failure_display.dart'; export 'loading_display.dart'; +export 'share_display.dart'; diff --git a/lib/game/components/backbox/displays/share_display.dart b/lib/game/components/backbox/displays/share_display.dart new file mode 100644 index 00000000..ebaaac7e --- /dev/null +++ b/lib/game/components/backbox/displays/share_display.dart @@ -0,0 +1,189 @@ +import 'dart:async'; + +import 'package:flame/components.dart'; +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball/l10n/l10n.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; +import 'package:pinball_ui/pinball_ui.dart'; +import 'package:share_repository/share_repository.dart'; + +/// Signature for the callback called when the user tries to share their score +/// on the [ShareDisplay]. +typedef OnSocialShareTap = void Function(SharePlatform); + +final _descriptionTextPaint = TextPaint( + style: const TextStyle( + fontSize: 1.6, + color: PinballColors.white, + fontFamily: PinballFonts.pixeloidSans, + ), +); + +/// {@template share_display} +/// Display that allows users to share their score to social networks. +/// {@endtemplate} +class ShareDisplay extends Component with HasGameRef { + /// {@macro share_display} + ShareDisplay({ + OnSocialShareTap? onShare, + }) : super( + children: [ + _ShareInstructionsComponent( + onShare: onShare, + ), + ], + ); +} + +class _ShareInstructionsComponent extends PositionComponent with HasGameRef { + _ShareInstructionsComponent({ + OnSocialShareTap? onShare, + }) : super( + anchor: Anchor.center, + position: Vector2(0, -25), + children: [ + _DescriptionComponent(), + _SocialNetworksComponent( + onShare: onShare, + ), + ], + ); +} + +class _DescriptionComponent extends PositionComponent with HasGameRef { + _DescriptionComponent() + : super( + anchor: Anchor.center, + position: Vector2.zero(), + children: [ + _LetEveryoneTextComponent(), + _SharingYourScoreTextComponent(), + _SocialMediaTextComponent(), + ], + ); +} + +class _LetEveryoneTextComponent extends TextComponent with HasGameRef { + _LetEveryoneTextComponent() + : super( + anchor: Anchor.center, + position: Vector2.zero(), + textRenderer: _descriptionTextPaint, + ); + + @override + Future onLoad() async { + await super.onLoad(); + text = readProvider().letEveryone; + } +} + +class _SharingYourScoreTextComponent extends TextComponent with HasGameRef { + _SharingYourScoreTextComponent() + : super( + anchor: Anchor.center, + position: Vector2(0, 2.5), + textRenderer: _descriptionTextPaint, + ); + + @override + Future onLoad() async { + await super.onLoad(); + text = readProvider().bySharingYourScore; + } +} + +class _SocialMediaTextComponent extends TextComponent with HasGameRef { + _SocialMediaTextComponent() + : super( + anchor: Anchor.center, + position: Vector2(0, 5), + textRenderer: _descriptionTextPaint, + ); + + @override + Future onLoad() async { + await super.onLoad(); + text = readProvider().socialMediaAccount; + } +} + +class _SocialNetworksComponent extends PositionComponent with HasGameRef { + _SocialNetworksComponent({ + OnSocialShareTap? onShare, + }) : super( + anchor: Anchor.center, + position: Vector2(0, 12), + children: [ + FacebookButtonComponent(onTap: onShare), + TwitterButtonComponent(onTap: onShare), + ], + ); +} + +/// {@template facebook_button_component} +/// Button for sharing on Facebook. +/// {@endtemplate} +class FacebookButtonComponent extends SpriteComponent + with HasGameRef, Tappable { + /// {@macro facebook_button_component} + FacebookButtonComponent({ + OnSocialShareTap? onTap, + }) : _onTap = onTap, + super( + anchor: Anchor.center, + position: Vector2(-5, 0), + ); + + final OnSocialShareTap? _onTap; + + @override + bool onTapDown(TapDownInfo info) { + _onTap?.call(SharePlatform.facebook); + return true; + } + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = Sprite( + gameRef.images.fromCache(Assets.images.backbox.button.facebook.keyName), + ); + this.sprite = sprite; + size = sprite.originalSize / 25; + } +} + +/// {@template twitter_button_component} +/// Button for sharing on Twitter. +/// {@endtemplate} +class TwitterButtonComponent extends SpriteComponent with HasGameRef, Tappable { + /// {@macro twitter_button_component} + TwitterButtonComponent({ + OnSocialShareTap? onTap, + }) : _onTap = onTap, + super( + anchor: Anchor.center, + position: Vector2(5, 0), + ); + + final OnSocialShareTap? _onTap; + + @override + bool onTapDown(TapDownInfo info) { + _onTap?.call(SharePlatform.twitter); + return true; + } + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = Sprite( + gameRef.images.fromCache(Assets.images.backbox.button.twitter.keyName), + ); + this.sprite = sprite; + size = sprite.originalSize / 25; + } +} diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 0f5c6626..39d0e133 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -101,6 +101,8 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.sparky.bumper.c.dimmed.keyName), images.load(components.Assets.images.backbox.marquee.keyName), images.load(components.Assets.images.backbox.displayDivider.keyName), + images.load(components.Assets.images.backbox.button.facebook.keyName), + images.load(components.Assets.images.backbox.button.twitter.keyName), images.load( components.Assets.images.backbox.displayTitleDecoration.keyName, ), @@ -143,13 +145,17 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.skillShot.pin.keyName), images.load(components.Assets.images.skillShot.lit.keyName), images.load(components.Assets.images.skillShot.dimmed.keyName), - images.load(dashTheme.leaderboardIcon.keyName), - images.load(sparkyTheme.leaderboardIcon.keyName), images.load(androidTheme.leaderboardIcon.keyName), - images.load(dinoTheme.leaderboardIcon.keyName), + images.load(androidTheme.background.keyName), images.load(androidTheme.ball.keyName), + images.load(dashTheme.leaderboardIcon.keyName), + images.load(dashTheme.background.keyName), images.load(dashTheme.ball.keyName), + images.load(dinoTheme.leaderboardIcon.keyName), + images.load(dinoTheme.background.keyName), images.load(dinoTheme.ball.keyName), + images.load(sparkyTheme.leaderboardIcon.keyName), + images.load(sparkyTheme.background.keyName), images.load(sparkyTheme.ball.keyName), ]; } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 2a85c2e2..751b8d8e 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -14,12 +14,14 @@ import 'package:pinball/select_character/select_character.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; +import 'package:share_repository/share_repository.dart'; class PinballGame extends PinballForge2DGame with HasKeyboardHandlerComponents, MultiTouchTapDetector, HasTappables { PinballGame({ required CharacterThemeCubit characterThemeBloc, required this.leaderboardRepository, + required this.shareRepository, required GameBloc gameBloc, required AppLocalizations l10n, required PinballAudioPlayer audioPlayer, @@ -51,6 +53,8 @@ class PinballGame extends PinballForge2DGame final LeaderboardRepository leaderboardRepository; + final ShareRepository shareRepository; + final AppLocalizations _l10n; final GameBloc _gameBloc; @@ -84,13 +88,14 @@ class PinballGame extends PinballForge2DGame providers: [ FlameProvider.value(_audioPlayer), FlameProvider.value(leaderboardRepository), + FlameProvider.value(shareRepository), FlameProvider.value(_l10n), ], children: [ BonusNoiseBehavior(), GameBlocStatusListener(), BallSpawningBehavior(), - BallThemingBehavior(), + CharacterSelectionBehavior(), CameraFocusingBehavior(), CanvasComponent( onSpritePainted: (paint) { @@ -101,10 +106,12 @@ class PinballGame extends PinballForge2DGame children: [ ZCanvasComponent( children: [ + ArcadeBackground(), BoardBackgroundSpriteComponent(), Boundaries(), Backbox( leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: _entries, ), GoogleGallery(), @@ -187,6 +194,7 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector { DebugPinballGame({ required CharacterThemeCubit characterThemeBloc, required LeaderboardRepository leaderboardRepository, + required ShareRepository shareRepository, required AppLocalizations l10n, required PinballAudioPlayer audioPlayer, required GameBloc gameBloc, @@ -194,6 +202,7 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector { characterThemeBloc: characterThemeBloc, audioPlayer: audioPlayer, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, l10n: l10n, gameBloc: gameBloc, ); diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index 5c21f257..a114ec0b 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -12,6 +12,7 @@ import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_ui/pinball_ui.dart'; +import 'package:share_repository/share_repository.dart'; class PinballGamePage extends StatelessWidget { const PinballGamePage({ @@ -26,12 +27,14 @@ class PinballGamePage extends StatelessWidget { final characterThemeBloc = context.read(); final audioPlayer = context.read(); final leaderboardRepository = context.read(); + final shareRepository = context.read(); final gameBloc = context.read(); final game = isDebugMode ? DebugPinballGame( characterThemeBloc: characterThemeBloc, audioPlayer: audioPlayer, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, l10n: context.l10n, gameBloc: gameBloc, ) @@ -39,18 +42,16 @@ class PinballGamePage extends StatelessWidget { characterThemeBloc: characterThemeBloc, audioPlayer: audioPlayer, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, l10n: context.l10n, gameBloc: gameBloc, ); - return Container( - decoration: const CrtBackground(), - child: Scaffold( - backgroundColor: PinballColors.transparent, - body: BlocProvider( - create: (_) => AssetsManagerCubit(game, audioPlayer)..load(), - child: PinballGameView(game), - ), + return Scaffold( + backgroundColor: PinballColors.black, + body: BlocProvider( + create: (_) => AssetsManagerCubit(game, audioPlayer)..load(), + child: PinballGameView(game), ), ); } diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 8573c585..a9b12291 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -189,7 +189,7 @@ "description": "Text shown on the mobile controls enter button" }, "initialsErrorTitle": "Uh-oh... well, that didn’t work", - "@enter": { + "@initialsErrorTitle": { "description": "Title shown when the initials submission fails" }, "initialsErrorMessage": "Please try a different combination of letters", @@ -200,4 +200,26 @@ "@leaderboardErrorMessage": { "description": "Text shown when the leaderboard had an error while loading" } + , + "letEveryone": "Let everyone know about I/O Pinball", + "@letEveryone": { + "description": "Text displayed on share screen for description" + }, + "bySharingYourScore": "by sharing your score to your preferred", + "@bySharingYourScore": { + "description": "Text displayed on share screen for description" + }, + "socialMediaAccount": "social media account!", + "@socialMediaAccount": { + "description": "Text displayed on share screen for description" + }, + "iGotScoreAtPinball": "I got {score} at the #IOPinball machine, can you beat my score? See you at #GoogleIO!", + "@iGotScoreAtPinball": { + "description": "Text to share score on Social Network", + "placeholders": { + "score": { + "type": "int" + } + } + } } diff --git a/lib/main.dart b/lib/main.dart index 158966c8..cb8c78da 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,10 +6,13 @@ import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/app/app.dart'; import 'package:pinball/bootstrap.dart'; import 'package:pinball_audio/pinball_audio.dart'; +import 'package:share_repository/share_repository.dart'; void main() { bootstrap((firestore, firebaseAuth) async { final leaderboardRepository = LeaderboardRepository(firestore); + const shareRepository = + ShareRepository(appUrl: ShareRepository.pinballGameUrl); final authenticationRepository = AuthenticationRepository(firebaseAuth); final pinballAudioPlayer = PinballAudioPlayer(); unawaited( @@ -20,6 +23,7 @@ void main() { return App( authenticationRepository: authenticationRepository, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, pinballAudioPlayer: pinballAudioPlayer, ); }); diff --git a/packages/pinball_components/assets/images/android/ramp/board-opening.png b/packages/pinball_components/assets/images/android/ramp/board_opening.png similarity index 100% rename from packages/pinball_components/assets/images/android/ramp/board-opening.png rename to packages/pinball_components/assets/images/android/ramp/board_opening.png diff --git a/packages/pinball_components/assets/images/android/ramp/railing-background.png b/packages/pinball_components/assets/images/android/ramp/railing_background.png similarity index 100% rename from packages/pinball_components/assets/images/android/ramp/railing-background.png rename to packages/pinball_components/assets/images/android/ramp/railing_background.png diff --git a/packages/pinball_components/assets/images/android/ramp/railing-foreground.png b/packages/pinball_components/assets/images/android/ramp/railing_foreground.png similarity index 100% rename from packages/pinball_components/assets/images/android/ramp/railing-foreground.png rename to packages/pinball_components/assets/images/android/ramp/railing_foreground.png diff --git a/packages/pinball_components/assets/images/android/spaceship/light-beam.png b/packages/pinball_components/assets/images/android/spaceship/light_beam.png similarity index 100% rename from packages/pinball_components/assets/images/android/spaceship/light-beam.png rename to packages/pinball_components/assets/images/android/spaceship/light_beam.png diff --git a/packages/pinball_components/assets/images/backbox/button/facebook.png b/packages/pinball_components/assets/images/backbox/button/facebook.png new file mode 100644 index 00000000..f6d29ab2 Binary files /dev/null and b/packages/pinball_components/assets/images/backbox/button/facebook.png differ diff --git a/packages/pinball_components/assets/images/backbox/button/twitter.png b/packages/pinball_components/assets/images/backbox/button/twitter.png new file mode 100644 index 00000000..f109a4b8 Binary files /dev/null and b/packages/pinball_components/assets/images/backbox/button/twitter.png differ diff --git a/packages/pinball_components/assets/images/backbox/display-divider.png b/packages/pinball_components/assets/images/backbox/display_divider.png similarity index 100% rename from packages/pinball_components/assets/images/backbox/display-divider.png rename to packages/pinball_components/assets/images/backbox/display_divider.png diff --git a/packages/pinball_components/assets/images/board-background.png b/packages/pinball_components/assets/images/board_background.png similarity index 100% rename from packages/pinball_components/assets/images/board-background.png rename to packages/pinball_components/assets/images/board_background.png diff --git a/packages/pinball_components/assets/images/boundary/outer-bottom.png b/packages/pinball_components/assets/images/boundary/outer_bottom.png similarity index 100% rename from packages/pinball_components/assets/images/boundary/outer-bottom.png rename to packages/pinball_components/assets/images/boundary/outer_bottom.png diff --git a/packages/pinball_components/assets/images/dino/bottom-wall.png b/packages/pinball_components/assets/images/dino/bottom_wall.png similarity index 100% rename from packages/pinball_components/assets/images/dino/bottom-wall.png rename to packages/pinball_components/assets/images/dino/bottom_wall.png diff --git a/packages/pinball_components/assets/images/dino/top-wall.png b/packages/pinball_components/assets/images/dino/top_wall.png similarity index 100% rename from packages/pinball_components/assets/images/dino/top-wall.png rename to packages/pinball_components/assets/images/dino/top_wall.png diff --git a/packages/pinball_components/assets/images/dino/top-wall-tunnel.png b/packages/pinball_components/assets/images/dino/top_wall_tunnel.png similarity index 100% rename from packages/pinball_components/assets/images/dino/top-wall-tunnel.png rename to packages/pinball_components/assets/images/dino/top_wall_tunnel.png diff --git a/packages/pinball_components/assets/images/flapper/back-support.png b/packages/pinball_components/assets/images/flapper/back_support.png similarity index 100% rename from packages/pinball_components/assets/images/flapper/back-support.png rename to packages/pinball_components/assets/images/flapper/back_support.png diff --git a/packages/pinball_components/assets/images/flapper/front-support.png b/packages/pinball_components/assets/images/flapper/front_support.png similarity index 100% rename from packages/pinball_components/assets/images/flapper/front-support.png rename to packages/pinball_components/assets/images/flapper/front_support.png diff --git a/packages/pinball_components/assets/images/launch_ramp/background-railing.png b/packages/pinball_components/assets/images/launch_ramp/background_railing.png similarity index 100% rename from packages/pinball_components/assets/images/launch_ramp/background-railing.png rename to packages/pinball_components/assets/images/launch_ramp/background_railing.png diff --git a/packages/pinball_components/assets/images/launch_ramp/foreground-railing.png b/packages/pinball_components/assets/images/launch_ramp/foreground_railing.png similarity index 100% rename from packages/pinball_components/assets/images/launch_ramp/foreground-railing.png rename to packages/pinball_components/assets/images/launch_ramp/foreground_railing.png diff --git a/packages/pinball_components/assets/images/score/five-thousand.png b/packages/pinball_components/assets/images/score/five_thousand.png similarity index 100% rename from packages/pinball_components/assets/images/score/five-thousand.png rename to packages/pinball_components/assets/images/score/five_thousand.png diff --git a/packages/pinball_components/assets/images/score/one-million.png b/packages/pinball_components/assets/images/score/one_million.png similarity index 100% rename from packages/pinball_components/assets/images/score/one-million.png rename to packages/pinball_components/assets/images/score/one_million.png diff --git a/packages/pinball_components/assets/images/score/twenty-thousand.png b/packages/pinball_components/assets/images/score/twenty_thousand.png similarity index 100% rename from packages/pinball_components/assets/images/score/twenty-thousand.png rename to packages/pinball_components/assets/images/score/twenty_thousand.png diff --git a/packages/pinball_components/assets/images/score/two-hundred-thousand.png b/packages/pinball_components/assets/images/score/two_hundred_thousand.png similarity index 100% rename from packages/pinball_components/assets/images/score/two-hundred-thousand.png rename to packages/pinball_components/assets/images/score/two_hundred_thousand.png diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 53ac5c72..4b031934 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -15,9 +15,9 @@ class $AssetsImagesGen { $AssetsImagesBallGen get ball => const $AssetsImagesBallGen(); $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); - /// File path: assets/images/board-background.png + /// File path: assets/images/board_background.png AssetGenImage get boardBackground => - const AssetGenImage('assets/images/board-background.png'); + const AssetGenImage('assets/images/board_background.png'); $AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen(); $AssetsImagesDashGen get dash => const $AssetsImagesDashGen(); @@ -61,9 +61,12 @@ class $AssetsImagesAndroidGen { class $AssetsImagesBackboxGen { const $AssetsImagesBackboxGen(); - /// File path: assets/images/backbox/display-divider.png + $AssetsImagesBackboxButtonGen get button => + const $AssetsImagesBackboxButtonGen(); + + /// File path: assets/images/backbox/display_divider.png AssetGenImage get displayDivider => - const AssetGenImage('assets/images/backbox/display-divider.png'); + const AssetGenImage('assets/images/backbox/display_divider.png'); /// File path: assets/images/backbox/display_title_decoration.png AssetGenImage get displayTitleDecoration => @@ -101,13 +104,13 @@ class $AssetsImagesBoundaryGen { AssetGenImage get bottom => const AssetGenImage('assets/images/boundary/bottom.png'); - /// File path: assets/images/boundary/outer-bottom.png - AssetGenImage get outerBottom => - const AssetGenImage('assets/images/boundary/outer-bottom.png'); - /// File path: assets/images/boundary/outer.png AssetGenImage get outer => const AssetGenImage('assets/images/boundary/outer.png'); + + /// File path: assets/images/boundary/outer_bottom.png + AssetGenImage get outerBottom => + const AssetGenImage('assets/images/boundary/outer_bottom.png'); } class $AssetsImagesDashGen { @@ -126,33 +129,33 @@ class $AssetsImagesDinoGen { $AssetsImagesDinoAnimatronicGen get animatronic => const $AssetsImagesDinoAnimatronicGen(); - /// File path: assets/images/dino/bottom-wall.png + /// File path: assets/images/dino/bottom_wall.png AssetGenImage get bottomWall => - const AssetGenImage('assets/images/dino/bottom-wall.png'); - - /// File path: assets/images/dino/top-wall-tunnel.png - AssetGenImage get topWallTunnel => - const AssetGenImage('assets/images/dino/top-wall-tunnel.png'); + const AssetGenImage('assets/images/dino/bottom_wall.png'); - /// File path: assets/images/dino/top-wall.png + /// File path: assets/images/dino/top_wall.png AssetGenImage get topWall => - const AssetGenImage('assets/images/dino/top-wall.png'); + const AssetGenImage('assets/images/dino/top_wall.png'); + + /// File path: assets/images/dino/top_wall_tunnel.png + AssetGenImage get topWallTunnel => + const AssetGenImage('assets/images/dino/top_wall_tunnel.png'); } class $AssetsImagesFlapperGen { const $AssetsImagesFlapperGen(); - /// File path: assets/images/flapper/back-support.png + /// File path: assets/images/flapper/back_support.png AssetGenImage get backSupport => - const AssetGenImage('assets/images/flapper/back-support.png'); + const AssetGenImage('assets/images/flapper/back_support.png'); /// File path: assets/images/flapper/flap.png AssetGenImage get flap => const AssetGenImage('assets/images/flapper/flap.png'); - /// File path: assets/images/flapper/front-support.png + /// File path: assets/images/flapper/front_support.png AssetGenImage get frontSupport => - const AssetGenImage('assets/images/flapper/front-support.png'); + const AssetGenImage('assets/images/flapper/front_support.png'); } class $AssetsImagesFlipperGen { @@ -203,13 +206,13 @@ class $AssetsImagesKickerGen { class $AssetsImagesLaunchRampGen { const $AssetsImagesLaunchRampGen(); - /// File path: assets/images/launch_ramp/background-railing.png + /// File path: assets/images/launch_ramp/background_railing.png AssetGenImage get backgroundRailing => - const AssetGenImage('assets/images/launch_ramp/background-railing.png'); + const AssetGenImage('assets/images/launch_ramp/background_railing.png'); - /// File path: assets/images/launch_ramp/foreground-railing.png + /// File path: assets/images/launch_ramp/foreground_railing.png AssetGenImage get foregroundRailing => - const AssetGenImage('assets/images/launch_ramp/foreground-railing.png'); + const AssetGenImage('assets/images/launch_ramp/foreground_railing.png'); /// File path: assets/images/launch_ramp/ramp.png AssetGenImage get ramp => @@ -253,21 +256,21 @@ class $AssetsImagesPlungerGen { class $AssetsImagesScoreGen { const $AssetsImagesScoreGen(); - /// File path: assets/images/score/five-thousand.png + /// File path: assets/images/score/five_thousand.png AssetGenImage get fiveThousand => - const AssetGenImage('assets/images/score/five-thousand.png'); + const AssetGenImage('assets/images/score/five_thousand.png'); - /// File path: assets/images/score/one-million.png + /// File path: assets/images/score/one_million.png AssetGenImage get oneMillion => - const AssetGenImage('assets/images/score/one-million.png'); + const AssetGenImage('assets/images/score/one_million.png'); - /// File path: assets/images/score/twenty-thousand.png + /// File path: assets/images/score/twenty_thousand.png AssetGenImage get twentyThousand => - const AssetGenImage('assets/images/score/twenty-thousand.png'); + const AssetGenImage('assets/images/score/twenty_thousand.png'); - /// File path: assets/images/score/two-hundred-thousand.png + /// File path: assets/images/score/two_hundred_thousand.png AssetGenImage get twoHundredThousand => - const AssetGenImage('assets/images/score/two-hundred-thousand.png'); + const AssetGenImage('assets/images/score/two_hundred_thousand.png'); } class $AssetsImagesSignpostGen { @@ -364,21 +367,21 @@ class $AssetsImagesAndroidRampGen { $AssetsImagesAndroidRampArrowGen get arrow => const $AssetsImagesAndroidRampArrowGen(); - /// File path: assets/images/android/ramp/board-opening.png + /// File path: assets/images/android/ramp/board_opening.png AssetGenImage get boardOpening => - const AssetGenImage('assets/images/android/ramp/board-opening.png'); + const AssetGenImage('assets/images/android/ramp/board_opening.png'); /// File path: assets/images/android/ramp/main.png AssetGenImage get main => const AssetGenImage('assets/images/android/ramp/main.png'); - /// File path: assets/images/android/ramp/railing-background.png + /// File path: assets/images/android/ramp/railing_background.png AssetGenImage get railingBackground => - const AssetGenImage('assets/images/android/ramp/railing-background.png'); + const AssetGenImage('assets/images/android/ramp/railing_background.png'); - /// File path: assets/images/android/ramp/railing-foreground.png + /// File path: assets/images/android/ramp/railing_foreground.png AssetGenImage get railingForeground => - const AssetGenImage('assets/images/android/ramp/railing-foreground.png'); + const AssetGenImage('assets/images/android/ramp/railing_foreground.png'); } class $AssetsImagesAndroidSpaceshipGen { @@ -388,15 +391,27 @@ class $AssetsImagesAndroidSpaceshipGen { AssetGenImage get animatronic => const AssetGenImage('assets/images/android/spaceship/animatronic.png'); - /// File path: assets/images/android/spaceship/light-beam.png + /// File path: assets/images/android/spaceship/light_beam.png AssetGenImage get lightBeam => - const AssetGenImage('assets/images/android/spaceship/light-beam.png'); + const AssetGenImage('assets/images/android/spaceship/light_beam.png'); /// File path: assets/images/android/spaceship/saucer.png AssetGenImage get saucer => const AssetGenImage('assets/images/android/spaceship/saucer.png'); } +class $AssetsImagesBackboxButtonGen { + const $AssetsImagesBackboxButtonGen(); + + /// File path: assets/images/backbox/button/facebook.png + AssetGenImage get facebook => + const AssetGenImage('assets/images/backbox/button/facebook.png'); + + /// File path: assets/images/backbox/button/twitter.png + AssetGenImage get twitter => + const AssetGenImage('assets/images/backbox/button/twitter.png'); +} + class $AssetsImagesDashBumperGen { const $AssetsImagesDashBumperGen(); diff --git a/packages/pinball_components/lib/src/components/arcade_background/arcade_background.dart b/packages/pinball_components/lib/src/components/arcade_background/arcade_background.dart new file mode 100644 index 00000000..e9936367 --- /dev/null +++ b/packages/pinball_components/lib/src/components/arcade_background/arcade_background.dart @@ -0,0 +1,92 @@ +import 'package:flame/components.dart'; +import 'package:flame_bloc/flame_bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; + +export 'cubit/arcade_background_cubit.dart'; + +/// {@template arcade_background} +/// Background of the arcade that the pinball machine lives in. +/// {@endtemplate} +class ArcadeBackground extends Component with ZIndex { + /// {@macro arcade_background} + ArcadeBackground({String? assetPath}) + : this._( + bloc: ArcadeBackgroundCubit(), + assetPath: assetPath, + ); + + ArcadeBackground._({required this.bloc, String? assetPath}) + : super( + children: [ + FlameBlocProvider.value( + value: bloc, + children: [ArcadeBackgroundSpriteComponent(assetPath: assetPath)], + ) + ], + ) { + zIndex = ZIndexes.arcadeBackground; + } + + /// Creates an [ArcadeBackground] without any behaviors. + /// + /// This can be used for testing [ArcadeBackground]'s behaviors in isolation. + @visibleForTesting + ArcadeBackground.test({ + ArcadeBackgroundCubit? bloc, + String? assetPath, + }) : bloc = bloc ?? ArcadeBackgroundCubit(), + super( + children: [ + FlameBlocProvider.value( + value: bloc ?? ArcadeBackgroundCubit(), + children: [ArcadeBackgroundSpriteComponent(assetPath: assetPath)], + ) + ], + ); + + /// Bloc to update the arcade background sprite when a new character is + /// selected. + final ArcadeBackgroundCubit bloc; +} + +/// {@template arcade_background_sprite_component} +/// [SpriteComponent] for the [ArcadeBackground]. +/// {@endtemplate} +@visibleForTesting +class ArcadeBackgroundSpriteComponent extends SpriteComponent + with + FlameBlocListenable, + HasGameRef { + /// {@macro arcade_background_sprite_component} + ArcadeBackgroundSpriteComponent({required String? assetPath}) + : _assetPath = assetPath, + super( + anchor: Anchor.bottomCenter, + position: Vector2(0, 72.3), + ); + + final String? _assetPath; + + @override + void onNewState(ArcadeBackgroundState state) { + sprite = Sprite( + gameRef.images.fromCache(state.characterTheme.background.keyName), + ); + } + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = Sprite( + gameRef.images + .fromCache(_assetPath ?? theme.Assets.images.dash.background.keyName), + ); + this.sprite = sprite; + size = sprite.originalSize / 10; + } +} diff --git a/packages/pinball_components/lib/src/components/arcade_background/cubit/arcade_background_cubit.dart b/packages/pinball_components/lib/src/components/arcade_background/cubit/arcade_background_cubit.dart new file mode 100644 index 00000000..5b2188bc --- /dev/null +++ b/packages/pinball_components/lib/src/components/arcade_background/cubit/arcade_background_cubit.dart @@ -0,0 +1,15 @@ +// ignore_for_file: public_member_api_docs + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +part 'arcade_background_state.dart'; + +class ArcadeBackgroundCubit extends Cubit { + ArcadeBackgroundCubit() : super(const ArcadeBackgroundState.initial()); + + void onCharacterSelected(CharacterTheme characterTheme) { + emit(ArcadeBackgroundState(characterTheme: characterTheme)); + } +} diff --git a/packages/pinball_components/lib/src/components/arcade_background/cubit/arcade_background_state.dart b/packages/pinball_components/lib/src/components/arcade_background/cubit/arcade_background_state.dart new file mode 100644 index 00000000..d2406105 --- /dev/null +++ b/packages/pinball_components/lib/src/components/arcade_background/cubit/arcade_background_state.dart @@ -0,0 +1,15 @@ +// ignore_for_file: public_member_api_docs + +part of 'arcade_background_cubit.dart'; + +class ArcadeBackgroundState extends Equatable { + const ArcadeBackgroundState({required this.characterTheme}); + + const ArcadeBackgroundState.initial() + : this(characterTheme: const DashTheme()); + + final CharacterTheme characterTheme; + + @override + List get props => [characterTheme]; +} diff --git a/packages/pinball_components/lib/src/components/ball/cubit/ball_cubit.dart b/packages/pinball_components/lib/src/components/ball/cubit/ball_cubit.dart index 7d5c7dda..248a0e4f 100644 --- a/packages/pinball_components/lib/src/components/ball/cubit/ball_cubit.dart +++ b/packages/pinball_components/lib/src/components/ball/cubit/ball_cubit.dart @@ -7,7 +7,7 @@ part 'ball_state.dart'; class BallCubit extends Cubit { BallCubit() : super(const BallState.initial()); - void onThemeChanged(CharacterTheme characterTheme) { + void onCharacterSelected(CharacterTheme characterTheme) { emit(BallState(characterTheme: characterTheme)); } } diff --git a/packages/pinball_components/lib/src/components/baseboard.dart b/packages/pinball_components/lib/src/components/baseboard.dart index 47ba4666..2965d17f 100644 --- a/packages/pinball_components/lib/src/components/baseboard.dart +++ b/packages/pinball_components/lib/src/components/baseboard.dart @@ -3,6 +3,7 @@ import 'dart:math' as math; import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; /// {@template baseboard} /// Wing-shaped board piece to corral the [Ball] towards the [Flipper]s. diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index bcd4aab6..e67a38fe 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -1,6 +1,7 @@ export 'android_animatronic.dart'; export 'android_bumper/android_bumper.dart'; export 'android_spaceship/android_spaceship.dart'; +export 'arcade_background/arcade_background.dart'; export 'ball/ball.dart'; export 'baseboard.dart'; export 'board_background_sprite_component.dart'; @@ -28,7 +29,6 @@ export 'multiplier/multiplier.dart'; export 'plunger.dart'; export 'rocket.dart'; export 'score_component/score_component.dart'; -export 'shapes/shapes.dart'; export 'signpost/signpost.dart'; export 'skill_shot/skill_shot.dart'; export 'slingshot.dart'; diff --git a/packages/pinball_components/lib/src/components/z_indexes.dart b/packages/pinball_components/lib/src/components/z_indexes.dart index 0d0fab98..543ad7a7 100644 --- a/packages/pinball_components/lib/src/components/z_indexes.dart +++ b/packages/pinball_components/lib/src/components/z_indexes.dart @@ -18,6 +18,8 @@ abstract class ZIndexes { // Background + static const arcadeBackground = _below + boardBackground; + static const boardBackground = 5 * _below + _base; static const decal = _above + boardBackground; diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index a0ee6ea1..9306fb00 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -17,8 +17,6 @@ dependencies: ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: sdk: flutter - geometry: - path: ../geometry intl: ^0.17.0 pinball_flame: path: ../pinball_flame @@ -94,6 +92,7 @@ flutter: - assets/images/multiplier/x6/ - assets/images/score/ - assets/images/backbox/ + - assets/images/backbox/button/ - assets/images/flapper/ - assets/images/skill_shot/ diff --git a/packages/pinball_components/test/src/components/arcade_background/arcade_background_test.dart b/packages/pinball_components/test/src/components/arcade_background/arcade_background_test.dart new file mode 100644 index 00000000..a5c336d5 --- /dev/null +++ b/packages/pinball_components/test/src/components/arcade_background/arcade_background_test.dart @@ -0,0 +1,79 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/components.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; + +import '../../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final assets = [ + theme.Assets.images.android.background.keyName, + theme.Assets.images.dash.background.keyName, + theme.Assets.images.dino.background.keyName, + theme.Assets.images.sparky.background.keyName, + ]; + + final flameTester = FlameTester(() => TestGame(assets)); + + group('ArcadeBackground', () { + test( + 'can be instantiated', + () { + expect(ArcadeBackground(), isA()); + expect(ArcadeBackground.test(), isA()); + }, + ); + + flameTester.test( + 'loads correctly', + (game) async { + final ball = ArcadeBackground(); + await game.ready(); + await game.ensureAdd(ball); + + expect(game.contains(ball), isTrue); + }, + ); + + flameTester.test( + 'has only one SpriteComponent', + (game) async { + final ball = ArcadeBackground(); + await game.ready(); + await game.ensureAdd(ball); + + expect( + ball.descendants().whereType().length, + equals(1), + ); + }, + ); + + flameTester.test( + 'ArcadeBackgroundSpriteComponent changes sprite onNewState', + (game) async { + final ball = ArcadeBackground(); + await game.ready(); + await game.ensureAdd(ball); + + final ballSprite = ball + .descendants() + .whereType() + .single; + final originalSprite = ballSprite.sprite; + + ballSprite.onNewState( + const ArcadeBackgroundState(characterTheme: theme.DinoTheme()), + ); + await game.ready(); + + final newSprite = ballSprite.sprite; + expect(newSprite != originalSprite, isTrue); + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/arcade_background/cubit/arcade_background_cubit_test.dart b/packages/pinball_components/test/src/components/arcade_background/cubit/arcade_background_cubit_test.dart new file mode 100644 index 00000000..f2e99247 --- /dev/null +++ b/packages/pinball_components/test/src/components/arcade_background/cubit/arcade_background_cubit_test.dart @@ -0,0 +1,22 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group( + 'ArcadeBackgroundCubit', + () { + blocTest( + 'onCharacterSelected emits new theme', + build: ArcadeBackgroundCubit.new, + act: (bloc) => bloc.onCharacterSelected(const DinoTheme()), + expect: () => [ + const ArcadeBackgroundState( + characterTheme: DinoTheme(), + ), + ], + ); + }, + ); +} diff --git a/packages/pinball_components/test/src/components/arcade_background/cubit/arcade_background_state_test.dart b/packages/pinball_components/test/src/components/arcade_background/cubit/arcade_background_state_test.dart new file mode 100644 index 00000000..97925fb6 --- /dev/null +++ b/packages/pinball_components/test/src/components/arcade_background/cubit/arcade_background_state_test.dart @@ -0,0 +1,32 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('ArcadeBackgroundState', () { + test('supports value equality', () { + expect( + ArcadeBackgroundState(characterTheme: DashTheme()), + equals(ArcadeBackgroundState(characterTheme: DashTheme())), + ); + }); + + group('constructor', () { + test('can be instantiated', () { + expect( + ArcadeBackgroundState(characterTheme: DashTheme()), + isNotNull, + ); + }); + + test('initial contains DashTheme', () { + expect( + ArcadeBackgroundState.initial().characterTheme, + DashTheme(), + ); + }); + }); + }); +} diff --git a/packages/pinball_components/test/src/components/ball/cubit/ball_cubit_test.dart b/packages/pinball_components/test/src/components/ball/cubit/ball_cubit_test.dart index c5a03213..c3a89120 100644 --- a/packages/pinball_components/test/src/components/ball/cubit/ball_cubit_test.dart +++ b/packages/pinball_components/test/src/components/ball/cubit/ball_cubit_test.dart @@ -8,9 +8,9 @@ void main() { 'BallCubit', () { blocTest( - 'onThemeChanged emits new theme', + 'onCharacterSelected emits new theme', build: BallCubit.new, - act: (bloc) => bloc.onThemeChanged(const DinoTheme()), + act: (bloc) => bloc.onCharacterSelected(const DinoTheme()), expect: () => [const BallState(characterTheme: DinoTheme())], ); }, diff --git a/packages/pinball_components/test/src/components/board_background_sprite_component_test.dart b/packages/pinball_components/test/src/components/board_background_sprite_component_test.dart index df35594f..79e8c56b 100644 --- a/packages/pinball_components/test/src/components/board_background_sprite_component_test.dart +++ b/packages/pinball_components/test/src/components/board_background_sprite_component_test.dart @@ -40,7 +40,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/board-background.png'), + matchesGoldenFile('golden/board_background.png'), ); }, ); diff --git a/packages/pinball_components/test/src/components/dino_walls_test.dart b/packages/pinball_components/test/src/components/dino_walls_test.dart index 5e4471e5..dd8172ac 100644 --- a/packages/pinball_components/test/src/components/dino_walls_test.dart +++ b/packages/pinball_components/test/src/components/dino_walls_test.dart @@ -37,7 +37,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/dino-walls.png'), + matchesGoldenFile('golden/dino_walls.png'), ); }, ); diff --git a/packages/pinball_components/test/src/components/golden/board-background.png b/packages/pinball_components/test/src/components/golden/board_background.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/board-background.png rename to packages/pinball_components/test/src/components/golden/board_background.png diff --git a/packages/pinball_components/test/src/components/golden/dino-walls.png b/packages/pinball_components/test/src/components/golden/dino_walls.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/dino-walls.png rename to packages/pinball_components/test/src/components/golden/dino_walls.png diff --git a/packages/pinball_components/test/src/components/golden/launch-ramp.png b/packages/pinball_components/test/src/components/golden/launch_ramp.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/launch-ramp.png rename to packages/pinball_components/test/src/components/golden/launch_ramp.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x2-dimmed.png b/packages/pinball_components/test/src/components/golden/multipliers/x2_dimmed.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x2-dimmed.png rename to packages/pinball_components/test/src/components/golden/multipliers/x2_dimmed.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x2-lit.png b/packages/pinball_components/test/src/components/golden/multipliers/x2_lit.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x2-lit.png rename to packages/pinball_components/test/src/components/golden/multipliers/x2_lit.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x3-dimmed.png b/packages/pinball_components/test/src/components/golden/multipliers/x3_dimmed.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x3-dimmed.png rename to packages/pinball_components/test/src/components/golden/multipliers/x3_dimmed.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x3-lit.png b/packages/pinball_components/test/src/components/golden/multipliers/x3_lit.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x3-lit.png rename to packages/pinball_components/test/src/components/golden/multipliers/x3_lit.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x4-dimmed.png b/packages/pinball_components/test/src/components/golden/multipliers/x4_dimmed.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x4-dimmed.png rename to packages/pinball_components/test/src/components/golden/multipliers/x4_dimmed.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x4-lit.png b/packages/pinball_components/test/src/components/golden/multipliers/x4_lit.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x4-lit.png rename to packages/pinball_components/test/src/components/golden/multipliers/x4_lit.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x5-dimmed.png b/packages/pinball_components/test/src/components/golden/multipliers/x5_dimmed.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x5-dimmed.png rename to packages/pinball_components/test/src/components/golden/multipliers/x5_dimmed.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x5-lit.png b/packages/pinball_components/test/src/components/golden/multipliers/x5_lit.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x5-lit.png rename to packages/pinball_components/test/src/components/golden/multipliers/x5_lit.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x6-dimmed.png b/packages/pinball_components/test/src/components/golden/multipliers/x6_dimmed.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x6-dimmed.png rename to packages/pinball_components/test/src/components/golden/multipliers/x6_dimmed.png diff --git a/packages/pinball_components/test/src/components/golden/multipliers/x6-lit.png b/packages/pinball_components/test/src/components/golden/multipliers/x6_lit.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/multipliers/x6-lit.png rename to packages/pinball_components/test/src/components/golden/multipliers/x6_lit.png diff --git a/packages/pinball_components/test/src/components/golden/spaceship-rail.png b/packages/pinball_components/test/src/components/golden/spaceship_rail.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/spaceship-rail.png rename to packages/pinball_components/test/src/components/golden/spaceship_rail.png diff --git a/packages/pinball_components/test/src/components/golden/sparky-computer.png b/packages/pinball_components/test/src/components/golden/sparky_computer.png similarity index 100% rename from packages/pinball_components/test/src/components/golden/sparky-computer.png rename to packages/pinball_components/test/src/components/golden/sparky_computer.png diff --git a/packages/pinball_components/test/src/components/launch_ramp_test.dart b/packages/pinball_components/test/src/components/launch_ramp_test.dart index 38c0920b..11033b5a 100644 --- a/packages/pinball_components/test/src/components/launch_ramp_test.dart +++ b/packages/pinball_components/test/src/components/launch_ramp_test.dart @@ -36,7 +36,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/launch-ramp.png'), + matchesGoldenFile('golden/launch_ramp.png'), ); }, ); diff --git a/packages/pinball_components/test/src/components/multiplier/multiplier_test.dart b/packages/pinball_components/test/src/components/multiplier/multiplier_test.dart index c612ecb9..0a28ae54 100644 --- a/packages/pinball_components/test/src/components/multiplier/multiplier_test.dart +++ b/packages/pinball_components/test/src/components/multiplier/multiplier_test.dart @@ -122,7 +122,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x2-lit.png'), + matchesGoldenFile('../golden/multipliers/x2_lit.png'), ); }, ); @@ -162,7 +162,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x2-dimmed.png'), + matchesGoldenFile('../golden/multipliers/x2_dimmed.png'), ); }, ); @@ -206,7 +206,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x3-lit.png'), + matchesGoldenFile('../golden/multipliers/x3_lit.png'), ); }, ); @@ -246,7 +246,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x3-dimmed.png'), + matchesGoldenFile('../golden/multipliers/x3_dimmed.png'), ); }, ); @@ -290,7 +290,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x4-lit.png'), + matchesGoldenFile('../golden/multipliers/x4_lit.png'), ); }, ); @@ -330,7 +330,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x4-dimmed.png'), + matchesGoldenFile('../golden/multipliers/x4_dimmed.png'), ); }, ); @@ -374,7 +374,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x5-lit.png'), + matchesGoldenFile('../golden/multipliers/x5_lit.png'), ); }, ); @@ -414,7 +414,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x5-dimmed.png'), + matchesGoldenFile('../golden/multipliers/x5_dimmed.png'), ); }, ); @@ -458,7 +458,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x6-lit.png'), + matchesGoldenFile('../golden/multipliers/x6_lit.png'), ); }, ); @@ -498,7 +498,7 @@ void main() { await expectLater( find.byGame<_TestGame>(), - matchesGoldenFile('../golden/multipliers/x6-dimmed.png'), + matchesGoldenFile('../golden/multipliers/x6_dimmed.png'), ); }, ); diff --git a/packages/pinball_components/test/src/components/shapes/arc_shape_test.dart b/packages/pinball_components/test/src/components/shapes/arc_shape_test.dart deleted file mode 100644 index fe778872..00000000 --- a/packages/pinball_components/test/src/components/shapes/arc_shape_test.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'dart:math' as math; -import 'package:flame/extensions.dart'; -import 'package:flame/game.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball_components/src/components/components.dart'; - -void main() { - group('ArcShape', () { - test('can be instantiated', () { - expect( - ArcShape( - center: Vector2.zero(), - arcRadius: 10, - angle: 2 * math.pi, - ), - isNotNull, - ); - }); - - group('copyWith', () { - test( - 'copies correctly ' - 'when no argument specified', () { - final arcShape = ArcShape( - center: Vector2.zero(), - arcRadius: 10, - angle: 2 * math.pi, - ); - final arcShapeCopied = arcShape.copyWith(); - - for (var index = 0; index < arcShape.vertices.length; index++) { - expect( - arcShape.vertices[index], - equals(arcShapeCopied.vertices[index]), - ); - } - }); - - test( - 'copies correctly ' - 'when all arguments specified', () { - final arcShapeExpected = ArcShape( - center: Vector2.all(10), - arcRadius: 15, - angle: 2 * math.pi, - ); - final arcShapeCopied = ArcShape( - center: Vector2.zero(), - arcRadius: 10, - angle: math.pi, - ).copyWith( - center: Vector2.all(10), - arcRadius: 15, - angle: 2 * math.pi, - ); - - for (var index = 0; index < arcShapeCopied.vertices.length; index++) { - expect( - arcShapeCopied.vertices[index], - equals(arcShapeExpected.vertices[index]), - ); - } - }); - }); - }); -} diff --git a/packages/pinball_components/test/src/components/shapes/bezier_curve_shape_test.dart b/packages/pinball_components/test/src/components/shapes/bezier_curve_shape_test.dart deleted file mode 100644 index 6a6adeb7..00000000 --- a/packages/pinball_components/test/src/components/shapes/bezier_curve_shape_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'dart:math' as math; -import 'package:flame/extensions.dart'; -import 'package:flame/game.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball_components/src/components/components.dart'; - -void main() { - group('BezierCurveShape', () { - final controlPoints = [ - Vector2(0, 0), - Vector2(10, 0), - Vector2(0, 10), - Vector2(10, 10), - ]; - - test('can be instantiated', () { - expect( - BezierCurveShape( - controlPoints: controlPoints, - ), - isNotNull, - ); - }); - - group('rotate', () { - test('returns vertices rotated', () { - const rotationAngle = 2 * math.pi; - final controlPoints = [ - Vector2(0, 0), - Vector2(10, 0), - Vector2(0, 10), - Vector2(10, 10), - ]; - - final bezierCurveShape = BezierCurveShape( - controlPoints: controlPoints, - ); - final bezierCurveShapeRotated = BezierCurveShape( - controlPoints: controlPoints, - )..rotate(rotationAngle); - - for (var index = 0; index < bezierCurveShape.vertices.length; index++) { - expect( - bezierCurveShape.vertices[index]..rotate(rotationAngle), - equals(bezierCurveShapeRotated.vertices[index]), - ); - } - }); - }); - }); -} diff --git a/packages/pinball_components/test/src/components/shapes/ellipse_shape_test.dart b/packages/pinball_components/test/src/components/shapes/ellipse_shape_test.dart deleted file mode 100644 index 31f45cc1..00000000 --- a/packages/pinball_components/test/src/components/shapes/ellipse_shape_test.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'dart:math' as math; -import 'package:flame/extensions.dart'; -import 'package:flame/game.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball_components/src/components/components.dart'; - -void main() { - group('EllipseShape', () { - test('can be instantiated', () { - expect( - EllipseShape( - center: Vector2.zero(), - majorRadius: 10, - minorRadius: 8, - ), - isNotNull, - ); - }); - - group('rotate', () { - test('returns vertices rotated', () { - const rotationAngle = 2 * math.pi; - final ellipseShape = EllipseShape( - center: Vector2.zero(), - majorRadius: 10, - minorRadius: 8, - ); - final ellipseShapeRotated = EllipseShape( - center: Vector2.zero(), - majorRadius: 10, - minorRadius: 8, - )..rotate(rotationAngle); - - for (var index = 0; index < ellipseShape.vertices.length; index++) { - expect( - ellipseShape.vertices[index]..rotate(rotationAngle), - equals(ellipseShapeRotated.vertices[index]), - ); - } - }); - }); - - group('copyWith', () { - test('returns same shape when no properties are passed', () { - final ellipseShape = EllipseShape( - center: Vector2.zero(), - majorRadius: 10, - minorRadius: 8, - ); - final ellipseShapeCopied = ellipseShape.copyWith(); - - for (var index = 0; index < ellipseShape.vertices.length; index++) { - expect( - ellipseShape.vertices[index], - equals(ellipseShapeCopied.vertices[index]), - ); - } - }); - - test('returns object with updated properties when are passed', () { - final ellipseShapeExpected = EllipseShape( - center: Vector2.all(10), - majorRadius: 10, - minorRadius: 8, - ); - final ellipseShapeCopied = EllipseShape( - center: Vector2.zero(), - majorRadius: 10, - minorRadius: 8, - ).copyWith(center: Vector2.all(10)); - - for (var index = 0; - index < ellipseShapeCopied.vertices.length; - index++) { - expect( - ellipseShapeCopied.vertices[index], - equals(ellipseShapeExpected.vertices[index]), - ); - } - }); - }); - }); -} diff --git a/packages/pinball_components/test/src/components/spaceship_rail_test.dart b/packages/pinball_components/test/src/components/spaceship_rail_test.dart index 65e9dbd7..78aa4aaa 100644 --- a/packages/pinball_components/test/src/components/spaceship_rail_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_rail_test.dart @@ -35,7 +35,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('golden/spaceship-rail.png'), + matchesGoldenFile('golden/spaceship_rail.png'), ); }, ); diff --git a/packages/pinball_components/test/src/components/sparky_computer/sparky_computer_test.dart b/packages/pinball_components/test/src/components/sparky_computer/sparky_computer_test.dart index d15f8056..0c045cb1 100644 --- a/packages/pinball_components/test/src/components/sparky_computer/sparky_computer_test.dart +++ b/packages/pinball_components/test/src/components/sparky_computer/sparky_computer_test.dart @@ -42,7 +42,7 @@ void main() { verify: (game, tester) async { await expectLater( find.byGame(), - matchesGoldenFile('../golden/sparky-computer.png'), + matchesGoldenFile('../golden/sparky_computer.png'), ); }, ); diff --git a/packages/pinball_flame/lib/pinball_flame.dart b/packages/pinball_flame/lib/pinball_flame.dart index bc3cae0e..c40405cb 100644 --- a/packages/pinball_flame/lib/pinball_flame.dart +++ b/packages/pinball_flame/lib/pinball_flame.dart @@ -8,4 +8,5 @@ export 'src/keyboard_input_controller.dart'; export 'src/layer.dart'; export 'src/parent_is_a.dart'; export 'src/pinball_forge2d_game.dart'; +export 'src/shapes/shapes.dart'; export 'src/sprite_animation.dart'; diff --git a/packages/pinball_components/lib/src/components/shapes/arc_shape.dart b/packages/pinball_flame/lib/src/shapes/arc_shape.dart similarity index 73% rename from packages/pinball_components/lib/src/components/shapes/arc_shape.dart rename to packages/pinball_flame/lib/src/shapes/arc_shape.dart index d58bdf1e..780fa2d3 100644 --- a/packages/pinball_components/lib/src/components/shapes/arc_shape.dart +++ b/packages/pinball_flame/lib/src/shapes/arc_shape.dart @@ -35,17 +35,4 @@ class ArcShape extends ChainShape { /// Angle in radians to rotate the arc around its [center]. final double rotation; - - ArcShape copyWith({ - Vector2? center, - double? arcRadius, - double? angle, - double? rotation, - }) => - ArcShape( - center: center ?? this.center, - arcRadius: arcRadius ?? this.arcRadius, - angle: angle ?? this.angle, - rotation: rotation ?? this.rotation, - ); } diff --git a/packages/pinball_components/lib/src/components/shapes/bezier_curve_shape.dart b/packages/pinball_flame/lib/src/shapes/bezier_curve_shape.dart similarity index 75% rename from packages/pinball_components/lib/src/components/shapes/bezier_curve_shape.dart rename to packages/pinball_flame/lib/src/shapes/bezier_curve_shape.dart index 5fcf9e08..00d1bafb 100644 --- a/packages/pinball_components/lib/src/components/shapes/bezier_curve_shape.dart +++ b/packages/pinball_flame/lib/src/shapes/bezier_curve_shape.dart @@ -1,4 +1,3 @@ -import 'package:flame/extensions.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:geometry/geometry.dart'; @@ -18,9 +17,4 @@ class BezierCurveShape extends ChainShape { /// First and last [controlPoints] set the beginning and end of the curve, /// inner points between them set its final shape. final List controlPoints; - - /// Rotates the bezier curve by a given [angle] in radians. - void rotate(double angle) { - vertices.map((vector) => vector..rotate(angle)).toList(); - } } diff --git a/packages/pinball_components/lib/src/components/shapes/ellipse_shape.dart b/packages/pinball_flame/lib/src/shapes/ellipse_shape.dart similarity index 72% rename from packages/pinball_components/lib/src/components/shapes/ellipse_shape.dart rename to packages/pinball_flame/lib/src/shapes/ellipse_shape.dart index 488d3d6f..5c523a3f 100644 --- a/packages/pinball_components/lib/src/components/shapes/ellipse_shape.dart +++ b/packages/pinball_flame/lib/src/shapes/ellipse_shape.dart @@ -34,17 +34,8 @@ class EllipseShape extends ChainShape { /// Rotates the ellipse by a given [angle] in radians. void rotate(double angle) { - vertices.map((vector) => vector..rotate(angle)).toList(); + for (final vector in vertices) { + vector.rotate(angle); + } } - - EllipseShape copyWith({ - Vector2? center, - double? majorRadius, - double? minorRadius, - }) => - EllipseShape( - center: center ?? this.center, - majorRadius: majorRadius ?? this.majorRadius, - minorRadius: minorRadius ?? this.minorRadius, - ); } diff --git a/packages/pinball_components/lib/src/components/shapes/shapes.dart b/packages/pinball_flame/lib/src/shapes/shapes.dart similarity index 100% rename from packages/pinball_components/lib/src/components/shapes/shapes.dart rename to packages/pinball_flame/lib/src/shapes/shapes.dart diff --git a/packages/pinball_flame/pubspec.yaml b/packages/pinball_flame/pubspec.yaml index 4639a080..327951a1 100644 --- a/packages/pinball_flame/pubspec.yaml +++ b/packages/pinball_flame/pubspec.yaml @@ -17,6 +17,8 @@ dependencies: ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: sdk: flutter + geometry: + path: ../geometry dev_dependencies: flame_test: ^1.3.0 diff --git a/packages/pinball_flame/test/src/shapes/arc_shape_test.dart b/packages/pinball_flame/test/src/shapes/arc_shape_test.dart new file mode 100644 index 00000000..0c9f0a0f --- /dev/null +++ b/packages/pinball_flame/test/src/shapes/arc_shape_test.dart @@ -0,0 +1,20 @@ +import 'dart:math' as math; +import 'package:flame/extensions.dart'; +import 'package:flame/game.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +void main() { + group('ArcShape', () { + test('can be instantiated', () { + expect( + ArcShape( + center: Vector2.zero(), + arcRadius: 10, + angle: 2 * math.pi, + ), + isA(), + ); + }); + }); +} diff --git a/packages/pinball_flame/test/src/shapes/bezier_curve_shape_test.dart b/packages/pinball_flame/test/src/shapes/bezier_curve_shape_test.dart new file mode 100644 index 00000000..c2328a2f --- /dev/null +++ b/packages/pinball_flame/test/src/shapes/bezier_curve_shape_test.dart @@ -0,0 +1,22 @@ +import 'package:flame/extensions.dart'; +import 'package:flame/game.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +void main() { + group('BezierCurveShape', () { + test('can be instantiated', () { + expect( + BezierCurveShape( + controlPoints: [ + Vector2(0, 0), + Vector2(10, 0), + Vector2(0, 10), + Vector2(10, 10), + ], + ), + isA(), + ); + }); + }); +} diff --git a/packages/pinball_flame/test/src/shapes/ellipse_shape_test.dart b/packages/pinball_flame/test/src/shapes/ellipse_shape_test.dart new file mode 100644 index 00000000..0bb760d9 --- /dev/null +++ b/packages/pinball_flame/test/src/shapes/ellipse_shape_test.dart @@ -0,0 +1,41 @@ +import 'dart:math' as math; +import 'package:flame/extensions.dart'; +import 'package:flame/game.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +void main() { + group('EllipseShape', () { + test('can be instantiated', () { + expect( + EllipseShape( + center: Vector2.zero(), + majorRadius: 10, + minorRadius: 8, + ), + isA(), + ); + }); + + test('rotate returns vertices rotated', () { + const rotationAngle = 2 * math.pi; + final ellipseShape = EllipseShape( + center: Vector2.zero(), + majorRadius: 10, + minorRadius: 8, + ); + final ellipseShapeRotated = EllipseShape( + center: Vector2.zero(), + majorRadius: 10, + minorRadius: 8, + )..rotate(rotationAngle); + + for (var index = 0; index < ellipseShape.vertices.length; index++) { + expect( + ellipseShape.vertices[index]..rotate(rotationAngle), + equals(ellipseShapeRotated.vertices[index]), + ); + } + }); + }); +} diff --git a/packages/pinball_theme/assets/images/android/background.jpg b/packages/pinball_theme/assets/images/android/background.jpg new file mode 100644 index 00000000..f446e140 Binary files /dev/null and b/packages/pinball_theme/assets/images/android/background.jpg differ diff --git a/packages/pinball_theme/assets/images/android/background.png b/packages/pinball_theme/assets/images/android/background.png deleted file mode 100644 index f751dcdc..00000000 Binary files a/packages/pinball_theme/assets/images/android/background.png and /dev/null differ diff --git a/packages/pinball_theme/assets/images/dash/background.jpg b/packages/pinball_theme/assets/images/dash/background.jpg new file mode 100644 index 00000000..dc698060 Binary files /dev/null and b/packages/pinball_theme/assets/images/dash/background.jpg differ diff --git a/packages/pinball_theme/assets/images/dash/background.png b/packages/pinball_theme/assets/images/dash/background.png deleted file mode 100644 index a36601c9..00000000 Binary files a/packages/pinball_theme/assets/images/dash/background.png and /dev/null differ diff --git a/packages/pinball_theme/assets/images/dino/background.jpg b/packages/pinball_theme/assets/images/dino/background.jpg new file mode 100644 index 00000000..45272247 Binary files /dev/null and b/packages/pinball_theme/assets/images/dino/background.jpg differ diff --git a/packages/pinball_theme/assets/images/dino/background.png b/packages/pinball_theme/assets/images/dino/background.png deleted file mode 100644 index e42f1705..00000000 Binary files a/packages/pinball_theme/assets/images/dino/background.png and /dev/null differ diff --git a/packages/pinball_theme/assets/images/sparky/background.jpg b/packages/pinball_theme/assets/images/sparky/background.jpg new file mode 100644 index 00000000..ad19a47a Binary files /dev/null and b/packages/pinball_theme/assets/images/sparky/background.jpg differ diff --git a/packages/pinball_theme/assets/images/sparky/background.png b/packages/pinball_theme/assets/images/sparky/background.png deleted file mode 100644 index 5044376c..00000000 Binary files a/packages/pinball_theme/assets/images/sparky/background.png and /dev/null differ diff --git a/packages/pinball_theme/lib/src/generated/assets.gen.dart b/packages/pinball_theme/lib/src/generated/assets.gen.dart index 545f514b..17ec4caf 100644 --- a/packages/pinball_theme/lib/src/generated/assets.gen.dart +++ b/packages/pinball_theme/lib/src/generated/assets.gen.dart @@ -32,9 +32,9 @@ class $AssetsImagesAndroidGen { AssetGenImage get animation => const AssetGenImage('assets/images/android/animation.png'); - /// File path: assets/images/android/background.png + /// File path: assets/images/android/background.jpg AssetGenImage get background => - const AssetGenImage('assets/images/android/background.png'); + const AssetGenImage('assets/images/android/background.jpg'); /// File path: assets/images/android/ball.png AssetGenImage get ball => @@ -56,9 +56,9 @@ class $AssetsImagesDashGen { AssetGenImage get animation => const AssetGenImage('assets/images/dash/animation.png'); - /// File path: assets/images/dash/background.png + /// File path: assets/images/dash/background.jpg AssetGenImage get background => - const AssetGenImage('assets/images/dash/background.png'); + const AssetGenImage('assets/images/dash/background.jpg'); /// File path: assets/images/dash/ball.png AssetGenImage get ball => const AssetGenImage('assets/images/dash/ball.png'); @@ -78,9 +78,9 @@ class $AssetsImagesDinoGen { AssetGenImage get animation => const AssetGenImage('assets/images/dino/animation.png'); - /// File path: assets/images/dino/background.png + /// File path: assets/images/dino/background.jpg AssetGenImage get background => - const AssetGenImage('assets/images/dino/background.png'); + const AssetGenImage('assets/images/dino/background.jpg'); /// File path: assets/images/dino/ball.png AssetGenImage get ball => const AssetGenImage('assets/images/dino/ball.png'); @@ -100,9 +100,9 @@ class $AssetsImagesSparkyGen { AssetGenImage get animation => const AssetGenImage('assets/images/sparky/animation.png'); - /// File path: assets/images/sparky/background.png + /// File path: assets/images/sparky/background.jpg AssetGenImage get background => - const AssetGenImage('assets/images/sparky/background.png'); + const AssetGenImage('assets/images/sparky/background.jpg'); /// File path: assets/images/sparky/ball.png AssetGenImage get ball => diff --git a/packages/pinball_ui/lib/src/theme/pinball_colors.dart b/packages/pinball_ui/lib/src/theme/pinball_colors.dart index eb920129..250db02c 100644 --- a/packages/pinball_ui/lib/src/theme/pinball_colors.dart +++ b/packages/pinball_ui/lib/src/theme/pinball_colors.dart @@ -5,6 +5,9 @@ abstract class PinballColors { /// Color: 0xFFFFFFFF static const Color white = Color(0xFFFFFFFF); + /// Color: 0xFF000000 + static const Color black = Color(0xFF000000); + /// Color: 0xFF0C32A4 static const Color darkBlue = Color(0xFF0C32A4); diff --git a/packages/pinball_ui/test/src/theme/pinball_colors_test.dart b/packages/pinball_ui/test/src/theme/pinball_colors_test.dart index 469ab142..ddc95eda 100644 --- a/packages/pinball_ui/test/src/theme/pinball_colors_test.dart +++ b/packages/pinball_ui/test/src/theme/pinball_colors_test.dart @@ -8,6 +8,10 @@ void main() { expect(PinballColors.white, const Color(0xFFFFFFFF)); }); + test('black is 0xFF000000', () { + expect(PinballColors.black, const Color(0xFF000000)); + }); + test('darkBlue is 0xFF0C32A4', () { expect(PinballColors.darkBlue, const Color(0xFF0C32A4)); }); diff --git a/packages/share_repository/lib/src/share_repository.dart b/packages/share_repository/lib/src/share_repository.dart index e01f5746..16c29aee 100644 --- a/packages/share_repository/lib/src/share_repository.dart +++ b/packages/share_repository/lib/src/share_repository.dart @@ -17,6 +17,9 @@ class ShareRepository { /// Url to the Google IO Event. static const googleIOEvent = 'https://events.google.com/io/'; + /// Url to the Pinball game. + static const pinballGameUrl = 'https://ashehwkdkdjruejdnensjsjdne.web.app/#/'; + /// Returns a url to share the [value] on the given [platform]. /// /// The returned url can be opened using the [url_launcher](https://pub.dev/packages/url_launcher) package. diff --git a/test/app/view/app_test.dart b/test/app/view/app_test.dart index 4612641d..54814f36 100644 --- a/test/app/view/app_test.dart +++ b/test/app/view/app_test.dart @@ -5,6 +5,7 @@ import 'package:mocktail/mocktail.dart'; import 'package:pinball/app/app.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_audio/pinball_audio.dart'; +import 'package:share_repository/share_repository.dart'; class _MockAuthenticationRepository extends Mock implements AuthenticationRepository {} @@ -14,15 +15,19 @@ class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + void main() { group('App', () { late AuthenticationRepository authenticationRepository; late LeaderboardRepository leaderboardRepository; + late ShareRepository shareRepository; late PinballAudioPlayer pinballAudioPlayer; setUp(() { authenticationRepository = _MockAuthenticationRepository(); leaderboardRepository = _MockLeaderboardRepository(); + shareRepository = _MockShareRepository(); pinballAudioPlayer = _MockPinballAudioPlayer(); when(pinballAudioPlayer.load).thenAnswer((_) => [Future.value()]); }); @@ -32,6 +37,7 @@ void main() { App( authenticationRepository: authenticationRepository, leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, pinballAudioPlayer: pinballAudioPlayer, ), ); diff --git a/test/game/behaviors/bumper_noise_behavior_test.dart b/test/game/behaviors/bumper_noise_behavior_test.dart index 636da6ad..58bda07d 100644 --- a/test/game/behaviors/bumper_noise_behavior_test.dart +++ b/test/game/behaviors/bumper_noise_behavior_test.dart @@ -36,26 +36,26 @@ class _MockContact extends Mock implements Contact {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('BumperNoiseBehavior', () {}); - - late PinballAudioPlayer audioPlayer; - final flameTester = FlameTester(_TestGame.new); - - setUp(() { - audioPlayer = _MockPinballAudioPlayer(); + group('BumperNoiseBehavior', () { + late PinballAudioPlayer audioPlayer; + final flameTester = FlameTester(_TestGame.new); + + setUp(() { + audioPlayer = _MockPinballAudioPlayer(); + }); + + flameTester.testGameWidget( + 'plays bumper sound', + setUp: (game, _) async { + final behavior = BumperNoiseBehavior(); + final parent = _TestBodyComponent(); + await game.pump(parent, audioPlayer: audioPlayer); + await parent.ensureAdd(behavior); + behavior.beginContact(Object(), _MockContact()); + }, + verify: (_, __) async { + verify(() => audioPlayer.play(PinballAudio.bumper)).called(1); + }, + ); }); - - flameTester.testGameWidget( - 'plays bumper sound', - setUp: (game, _) async { - final behavior = BumperNoiseBehavior(); - final parent = _TestBodyComponent(); - await game.pump(parent, audioPlayer: audioPlayer); - await parent.ensureAdd(behavior); - behavior.beginContact(Object(), _MockContact()); - }, - verify: (_, __) async { - verify(() => audioPlayer.play(PinballAudio.bumper)).called(1); - }, - ); } diff --git a/test/game/behaviors/ball_theming_behavior_test.dart b/test/game/behaviors/character_selection_behavior_test.dart similarity index 57% rename from test/game/behaviors/ball_theming_behavior_test.dart rename to test/game/behaviors/character_selection_behavior_test.dart index 0c1e0b36..67a8b0a5 100644 --- a/test/game/behaviors/ball_theming_behavior_test.dart +++ b/test/game/behaviors/character_selection_behavior_test.dart @@ -20,6 +20,8 @@ class _TestGame extends Forge2DGame { await images.loadAll([ theme.Assets.images.dash.ball.keyName, theme.Assets.images.dino.ball.keyName, + theme.Assets.images.dash.background.keyName, + theme.Assets.images.dino.background.keyName, ]); } @@ -38,32 +40,66 @@ class _TestGame extends Forge2DGame { class _MockBallCubit extends Mock implements BallCubit {} +class _MockArcadeBackgroundCubit extends Mock implements ArcadeBackgroundCubit { +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); group( - 'BallThemingBehavior', + 'CharacterSelectionBehavior', () { final flameTester = FlameTester(_TestGame.new); test('can be instantiated', () { expect( - BallThemingBehavior(), - isA(), + CharacterSelectionBehavior(), + isA(), ); }); flameTester.test( 'loads', (game) async { - final behavior = BallThemingBehavior(); + final behavior = CharacterSelectionBehavior(); await game.pump([behavior]); expect(game.descendants(), contains(behavior)); }, ); flameTester.test( - 'onNewState calls onThemeChanged on the ball bloc', + 'onNewState calls onCharacterSelected on the arcade background bloc', + (game) async { + final arcadeBackgroundBloc = _MockArcadeBackgroundCubit(); + whenListen( + arcadeBackgroundBloc, + const Stream.empty(), + initialState: const ArcadeBackgroundState.initial(), + ); + final arcadeBackground = + ArcadeBackground.test(bloc: arcadeBackgroundBloc); + final behavior = CharacterSelectionBehavior(); + await game.pump([ + arcadeBackground, + behavior, + ZCanvasComponent(), + Plunger.test(compressionDistance: 10), + Ball.test(), + ]); + + const dinoThemeState = CharacterThemeState(theme.DinoTheme()); + behavior.onNewState(dinoThemeState); + await game.ready(); + + verify( + () => arcadeBackgroundBloc + .onCharacterSelected(dinoThemeState.characterTheme), + ).called(1); + }, + ); + + flameTester.test( + 'onNewState calls onCharacterSelected on the ball bloc', (game) async { final ballBloc = _MockBallCubit(); whenListen( @@ -72,20 +108,22 @@ void main() { initialState: const BallState.initial(), ); final ball = Ball.test(bloc: ballBloc); - final behavior = BallThemingBehavior(); + final behavior = CharacterSelectionBehavior(); await game.pump([ ball, behavior, ZCanvasComponent(), Plunger.test(compressionDistance: 10), + ArcadeBackground.test(), ]); const dinoThemeState = CharacterThemeState(theme.DinoTheme()); behavior.onNewState(dinoThemeState); await game.ready(); - verify(() => ballBloc.onThemeChanged(dinoThemeState.characterTheme)) - .called(1); + verify( + () => ballBloc.onCharacterSelected(dinoThemeState.characterTheme), + ).called(1); }, ); }, diff --git a/test/game/components/backbox/backbox_test.dart b/test/game/components/backbox/backbox_test.dart index 65474cd2..b99b86ab 100644 --- a/test/game/components/backbox/backbox_test.dart +++ b/test/game/components/backbox/backbox_test.dart @@ -20,7 +20,10 @@ import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' as theme; +import 'package:pinball_ui/pinball_ui.dart'; import 'package:platform_helper/platform_helper.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:share_repository/share_repository.dart'; class _TestGame extends Forge2DGame with HasKeyboardHandlerComponents, HasTappables { @@ -36,6 +39,8 @@ class _TestGame extends Forge2DGame character.leaderboardIcon.keyName, Assets.images.backbox.marquee.keyName, Assets.images.backbox.displayDivider.keyName, + Assets.images.backbox.button.facebook.keyName, + Assets.images.backbox.button.twitter.keyName, Assets.images.backbox.displayTitleDecoration.keyName, ]); } @@ -75,8 +80,14 @@ class _MockBackboxBloc extends Mock implements BackboxBloc {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + class _MockTapDownInfo extends Mock implements TapDownInfo {} +class _MockUrlLauncher extends Mock + with MockPlatformInterfaceMixin + implements UrlLauncherPlatform {} + class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get score => ''; @@ -105,6 +116,15 @@ class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get loading => ''; + @override + String get letEveryone => ''; + + @override + String get bySharingYourScore => ''; + + @override + String get socialMediaAccount => ''; + @override String get shareYourScore => ''; @@ -134,6 +154,9 @@ class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get leaderboardErrorMessage => ''; + + @override + String iGotScoreAtPinball(int _) => ''; } void main() { @@ -143,6 +166,7 @@ void main() { late BackboxBloc bloc; late PlatformHelper platformHelper; + late UrlLauncherPlatform urlLauncher; setUp(() { bloc = _MockBackboxBloc(); @@ -161,6 +185,7 @@ void main() { (game) async { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -178,6 +203,7 @@ void main() { await game.pump( Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ), ); @@ -199,6 +225,7 @@ void main() { leaderboardRepository: _MockLeaderboardRepository(), initialEntries: [LeaderboardEntryData.empty], ), + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -230,6 +257,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -258,6 +286,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -270,7 +299,8 @@ void main() { ); flameTester.test( - 'adds the mobile controls overlay when platform is mobile', + 'adds the mobile controls overlay ' + 'when platform is mobile at InitialsFormState', (game) async { final bloc = _MockBackboxBloc(); final platformHelper = _MockPlatformHelper(); @@ -286,6 +316,7 @@ void main() { when(() => platformHelper.isMobile).thenReturn(true); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -297,6 +328,33 @@ void main() { }, ); + flameTester.test( + 'remove the mobile controls overlay ' + 'when InitialsSuccessState', + (game) async { + final bloc = _MockBackboxBloc(); + final platformHelper = _MockPlatformHelper(); + final state = InitialsSuccessState(score: 10); + whenListen( + bloc, + Stream.empty(), + initialState: state, + ); + when(() => platformHelper.isMobile).thenReturn(true); + final backbox = Backbox.test( + bloc: bloc, + shareRepository: _MockShareRepository(), + platformHelper: platformHelper, + ); + await game.pump(backbox); + + expect( + game.overlays.value, + isNot(contains(PinballGame.mobileControlsOverlay)), + ); + }, + ); + flameTester.test( 'adds InitialsSubmissionSuccessDisplay on InitialsSuccessState', (game) async { @@ -308,6 +366,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -330,6 +389,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -359,6 +419,7 @@ void main() { ); final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -373,6 +434,145 @@ void main() { }, ); + group('ShareDisplay', () { + setUp(() async { + urlLauncher = _MockUrlLauncher(); + UrlLauncherPlatform.instance = urlLauncher; + }); + + flameTester.test( + 'adds ShareDisplay on ShareState', + (game) async { + final state = ShareState(score: 100); + whenListen( + bloc, + const Stream.empty(), + initialState: state, + ); + final backbox = Backbox.test( + bloc: bloc, + shareRepository: _MockShareRepository(), + platformHelper: platformHelper, + ); + await game.pump(backbox); + + expect( + game.descendants().whereType().length, + equals(1), + ); + }, + ); + + flameTester.test( + 'opens Facebook link when sharing with Facebook', + (game) async { + when(() => urlLauncher.canLaunch(any())) + .thenAnswer((_) async => true); + when( + () => urlLauncher.launch( + any(), + useSafariVC: any(named: 'useSafariVC'), + useWebView: any(named: 'useWebView'), + enableJavaScript: any(named: 'enableJavaScript'), + enableDomStorage: any(named: 'enableDomStorage'), + universalLinksOnly: any(named: 'universalLinksOnly'), + headers: any(named: 'headers'), + ), + ).thenAnswer((_) async => true); + + final state = ShareState(score: 100); + whenListen( + bloc, + const Stream.empty(), + initialState: state, + ); + + final shareRepository = _MockShareRepository(); + const fakeUrl = 'http://fakeUrl'; + when( + () => shareRepository.shareText( + value: any(named: 'value'), + platform: SharePlatform.facebook, + ), + ).thenReturn(fakeUrl); + + final backbox = Backbox.test( + bloc: bloc, + shareRepository: shareRepository, + platformHelper: platformHelper, + ); + await game.pump(backbox); + + final facebookButton = + game.descendants().whereType().first; + facebookButton.onTapDown(_MockTapDownInfo()); + + await game.ready(); + + verify( + () => shareRepository.shareText( + value: any(named: 'value'), + platform: SharePlatform.facebook, + ), + ).called(1); + }, + ); + + flameTester.test( + 'opens Twitter link when sharing with Twitter', + (game) async { + final state = ShareState(score: 100); + whenListen( + bloc, + Stream.value(state), + initialState: state, + ); + + final shareRepository = _MockShareRepository(); + const fakeUrl = 'http://fakeUrl'; + when( + () => shareRepository.shareText( + value: any(named: 'value'), + platform: SharePlatform.twitter, + ), + ).thenReturn(fakeUrl); + when(() => urlLauncher.canLaunch(any())) + .thenAnswer((_) async => true); + when( + () => urlLauncher.launch( + any(), + useSafariVC: any(named: 'useSafariVC'), + useWebView: any(named: 'useWebView'), + enableJavaScript: any(named: 'enableJavaScript'), + enableDomStorage: any(named: 'enableDomStorage'), + universalLinksOnly: any(named: 'universalLinksOnly'), + headers: any(named: 'headers'), + ), + ).thenAnswer((_) async => true); + + final backbox = Backbox.test( + bloc: bloc, + shareRepository: shareRepository, + platformHelper: platformHelper, + ); + await game.pump(backbox); + + final facebookButton = + game.descendants().whereType().first; + facebookButton.onTapDown(_MockTapDownInfo()); + + await game.ready(); + + verify( + () => shareRepository.shareText( + value: any(named: 'value'), + platform: SharePlatform.twitter, + ), + ).called(1); + }, + ); + }); + flameTester.test( 'adds LeaderboardDisplay on LeaderboardSuccessState', (game) async { @@ -384,6 +584,7 @@ void main() { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -406,6 +607,7 @@ void main() { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -429,6 +631,7 @@ void main() { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); @@ -469,6 +672,7 @@ void main() { final backbox = Backbox.test( bloc: bloc, + shareRepository: _MockShareRepository(), platformHelper: platformHelper, ); await game.pump(backbox); diff --git a/test/game/components/backbox/bloc/backbox_bloc_test.dart b/test/game/components/backbox/bloc/backbox_bloc_test.dart index aec46186..050307dc 100644 --- a/test/game/components/backbox/bloc/backbox_bloc_test.dart +++ b/test/game/components/backbox/bloc/backbox_bloc_test.dart @@ -137,6 +137,25 @@ void main() { ); }); + group('ShareScoreRequested', () { + blocTest( + 'emits ShareState', + setUp: () { + leaderboardRepository = _MockLeaderboardRepository(); + }, + build: () => BackboxBloc( + leaderboardRepository: leaderboardRepository, + initialEntries: emptyEntries, + ), + act: (bloc) => bloc.add( + ShareScoreRequested(score: 100), + ), + expect: () => [ + ShareState(score: 100), + ], + ); + }); + group('LeaderboardRequested', () { blocTest( 'adds [LoadingState, LeaderboardSuccessState] when request succeeds', diff --git a/test/game/components/backbox/bloc/backbox_state_test.dart b/test/game/components/backbox/bloc/backbox_state_test.dart index c41562b0..ba6d1c08 100644 --- a/test/game/components/backbox/bloc/backbox_state_test.dart +++ b/test/game/components/backbox/bloc/backbox_state_test.dart @@ -203,5 +203,23 @@ void main() { }); }); }); + + group('ShareState', () { + test('can be instantiated', () { + expect( + ShareState(score: 0), + isNotNull, + ); + }); + + test('supports value comparison', () { + expect( + ShareState(score: 0), + equals( + ShareState(score: 0), + ), + ); + }); + }); }); } diff --git a/test/game/components/backbox/displays/share_display_test.dart b/test/game/components/backbox/displays/share_display_test.dart new file mode 100644 index 00000000..1f882223 --- /dev/null +++ b/test/game/components/backbox/displays/share_display_test.dart @@ -0,0 +1,112 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; +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/game/bloc/game_bloc.dart'; +import 'package:pinball/game/components/backbox/displays/share_display.dart'; +import 'package:pinball/l10n/l10n.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +class _TestGame extends Forge2DGame with HasTappables { + @override + Future onLoad() async { + await super.onLoad(); + images.prefix = ''; + await images.loadAll( + [ + Assets.images.backbox.button.facebook.keyName, + Assets.images.backbox.button.twitter.keyName, + ], + ); + } + + Future pump(ShareDisplay component) { + return ensureAdd( + FlameBlocProvider.value( + value: GameBloc(), + children: [ + FlameProvider.value( + _MockAppLocalizations(), + children: [component], + ), + ], + ), + ); + } +} + +class _MockAppLocalizations extends Mock implements AppLocalizations { + @override + String get letEveryone => ''; + + @override + String get bySharingYourScore => ''; + + @override + String get socialMediaAccount => ''; +} + +class _MockTapDownInfo extends Mock implements TapDownInfo {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final flameTester = FlameTester(_TestGame.new); + + group('ShareDisplay', () { + flameTester.test( + 'loads correctly', + (game) async { + final component = ShareDisplay(); + await game.pump(component); + expect(game.descendants(), contains(component)); + }, + ); + + flameTester.test( + 'calls onShare when Facebook button is tapped', + (game) async { + var tapped = false; + + final tapDownInfo = _MockTapDownInfo(); + final component = ShareDisplay( + onShare: (_) => tapped = true, + ); + await game.pump(component); + + final facebookButton = + component.descendants().whereType().first; + + facebookButton.onTapDown(tapDownInfo); + + expect(tapped, isTrue); + }, + ); + + flameTester.test( + 'calls onShare when Twitter button is tapped', + (game) async { + var tapped = false; + + final tapDownInfo = _MockTapDownInfo(); + final component = ShareDisplay( + onShare: (_) => tapped = true, + ); + await game.pump(component); + + final twitterButton = + component.descendants().whereType().first; + + twitterButton.onTapDown(tapDownInfo); + + expect(tapped, isTrue); + }, + ); + }); +} diff --git a/test/game/components/game_bloc_status_listener_test.dart b/test/game/components/game_bloc_status_listener_test.dart index 3151e70b..cc1729b8 100644 --- a/test/game/components/game_bloc_status_listener_test.dart +++ b/test/game/components/game_bloc_status_listener_test.dart @@ -14,6 +14,7 @@ import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' as theme; +import 'package:share_repository/share_repository.dart'; class _TestGame extends Forge2DGame { @override @@ -65,6 +66,8 @@ class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get score => ''; @@ -149,9 +152,11 @@ void main() { 'changes the backbox display', (game) async { final component = GameBlocStatusListener(); - final repository = _MockLeaderboardRepository(); + final leaderboardRepository = _MockLeaderboardRepository(); + final shareRepository = _MockShareRepository(); final backbox = Backbox( - leaderboardRepository: repository, + leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: const [], ); @@ -165,9 +170,11 @@ void main() { 'removes FlipperKeyControllingBehavior from Flipper', (game) async { final component = GameBlocStatusListener(); - final repository = _MockLeaderboardRepository(); + final leaderboardRepository = _MockLeaderboardRepository(); + final shareRepository = _MockShareRepository(); final backbox = Backbox( - leaderboardRepository: repository, + leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: const [], ); final flipper = Flipper.test(side: BoardSide.left); @@ -193,9 +200,11 @@ void main() { (game) async { final audioPlayer = _MockPinballAudioPlayer(); final component = GameBlocStatusListener(); - final repository = _MockLeaderboardRepository(); + final leaderboardRepository = _MockLeaderboardRepository(); + final shareRepository = _MockShareRepository(); final backbox = Backbox( - leaderboardRepository: repository, + leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: const [], ); await game.pump( @@ -245,9 +254,11 @@ void main() { 'adds key controlling behavior to Flippers when the game is started', (game) async { final component = GameBlocStatusListener(); - final repository = _MockLeaderboardRepository(); + final leaderboardRepository = _MockLeaderboardRepository(); + final shareRepository = _MockShareRepository(); final backbox = Backbox( - leaderboardRepository: repository, + leaderboardRepository: leaderboardRepository, + shareRepository: shareRepository, entries: const [], ); final flipper = Flipper.test(side: BoardSide.left); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index 5aee66fd..52e8c97c 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -16,12 +16,14 @@ import 'package:pinball/game/game.dart'; import 'package:pinball/select_character/select_character.dart'; import 'package:pinball_audio/src/pinball_audio.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:share_repository/share_repository.dart'; class _TestPinballGame extends PinballGame { _TestPinballGame() : super( characterThemeBloc: CharacterThemeCubit(), leaderboardRepository: _MockLeaderboardRepository(), + shareRepository: _MockShareRepository(), gameBloc: GameBloc(), l10n: _MockAppLocalizations(), audioPlayer: _MockPinballAudioPlayer(), @@ -41,6 +43,7 @@ class _TestDebugPinballGame extends DebugPinballGame { : super( characterThemeBloc: CharacterThemeCubit(), leaderboardRepository: _MockLeaderboardRepository(), + shareRepository: _MockShareRepository(), gameBloc: GameBloc(), l10n: _MockAppLocalizations(), audioPlayer: _MockPinballAudioPlayer(), @@ -81,6 +84,8 @@ class _MockDragEndInfo extends Mock implements DragEndInfo {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {} void main() { @@ -113,11 +118,11 @@ void main() { ); flameTester.test( - 'has only one BallThemingBehavior', + 'has only one CharacterSelectionBehavior', (game) async { await game.ready(); expect( - game.descendants().whereType().length, + game.descendants().whereType().length, equals(1), ); }, diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index b2cde26e..74ef4fbb 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -16,6 +16,7 @@ import 'package:pinball/more_information/more_information.dart'; import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; +import 'package:share_repository/share_repository.dart'; import '../../helpers/helpers.dart'; @@ -24,6 +25,7 @@ class _TestPinballGame extends PinballGame { : super( characterThemeBloc: CharacterThemeCubit(), leaderboardRepository: _MockLeaderboardRepository(), + shareRepository: _MockShareRepository(), gameBloc: GameBloc(), l10n: _MockAppLocalizations(), audioPlayer: _MockPinballAudioPlayer(), @@ -60,6 +62,8 @@ class _MockPinballAudioPlayer extends Mock implements PinballAudioPlayer {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + void main() { final game = _TestPinballGame(); diff --git a/test/helpers/pump_app.dart b/test/helpers/pump_app.dart index 8be03f03..df75efae 100644 --- a/test/helpers/pump_app.dart +++ b/test/helpers/pump_app.dart @@ -12,12 +12,15 @@ import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; import 'package:pinball_ui/pinball_ui.dart'; +import 'package:share_repository/share_repository.dart'; class _MockAssetsManagerCubit extends Mock implements AssetsManagerCubit {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { } +class _MockShareRepository extends Mock implements ShareRepository {} + class _MockCharacterThemeCubit extends Mock implements CharacterThemeCubit {} class _MockGameBloc extends Mock implements GameBloc {} @@ -55,6 +58,7 @@ extension PumpApp on WidgetTester { AssetsManagerCubit? assetsManagerCubit, CharacterThemeCubit? characterThemeCubit, LeaderboardRepository? leaderboardRepository, + ShareRepository? shareRepository, PinballAudioPlayer? pinballAudioPlayer, }) { return runAsync(() { @@ -64,6 +68,9 @@ extension PumpApp on WidgetTester { RepositoryProvider.value( value: leaderboardRepository ?? _MockLeaderboardRepository(), ), + RepositoryProvider.value( + value: shareRepository ?? _MockShareRepository(), + ), RepositoryProvider.value( value: pinballAudioPlayer ?? _buildDefaultPinballAudioPlayer(), ),