diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index 82d8fae0..f260a028 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -21,7 +21,7 @@ jobs:
- name: Build Flutter App
run: |
flutter packages get
- flutter build web --target lib/main_development.dart --web-renderer canvaskit --release
+ flutter build web --target lib/main.dart --web-renderer canvaskit --release
- name: Deploy to Firebase
uses: FirebaseExtended/action-hosting-deploy@v0
diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml
index 5bef442d..48ad112c 100644
--- a/.github/workflows/main.yaml
+++ b/.github/workflows/main.yaml
@@ -14,3 +14,4 @@ jobs:
flutter_version: 2.10.5
coverage_excludes: "lib/gen/*.dart"
test_optimization: false
+
diff --git a/.github/workflows/spell_check.yaml b/.github/workflows/spell_check.yaml
new file mode 100644
index 00000000..bfef58f8
--- /dev/null
+++ b/.github/workflows/spell_check.yaml
@@ -0,0 +1,21 @@
+name: spell_check
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+on: [pull_request]
+
+jobs:
+ build:
+ name: Spell Check
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Check spelling
+ uses: zwaldowski/cspell-action@v1
+ with:
+ paths: '**/*.{dart,arb,md}'
+ config: .vscode/cspell.json
\ No newline at end of file
diff --git a/.idea/runConfigurations/development.xml b/.idea/runConfigurations/development.xml
deleted file mode 100644
index 37d45fe3..00000000
--- a/.idea/runConfigurations/development.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/main.xml b/.idea/runConfigurations/main.xml
new file mode 100644
index 00000000..b5c260ef
--- /dev/null
+++ b/.idea/runConfigurations/main.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/production.xml b/.idea/runConfigurations/production.xml
deleted file mode 100644
index e55961d8..00000000
--- a/.idea/runConfigurations/production.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/staging.xml b/.idea/runConfigurations/staging.xml
deleted file mode 100644
index 3f7d6219..00000000
--- a/.idea/runConfigurations/staging.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.vscode/cspell.json b/.vscode/cspell.json
new file mode 100644
index 00000000..71c54903
--- /dev/null
+++ b/.vscode/cspell.json
@@ -0,0 +1,50 @@
+{
+ "version": "0.2",
+ "enabled": true,
+ "language": "en",
+ "words": [
+ "animatronic",
+ "argb",
+ "audioplayers",
+ "backbox",
+ "bezier",
+ "contador",
+ "cupertino",
+ "dashbook",
+ "deserialization",
+ "dpad",
+ "endtemplate",
+ "firestore",
+ "gapless",
+ "genhtml",
+ "goldens",
+ "lcov",
+ "leaderboard",
+ "loadables",
+ "localizable",
+ "mixins",
+ "mocktail",
+ "mostrado",
+ "multiball",
+ "multiballs",
+ "occluder",
+ "pรกgina",
+ "pixelated",
+ "pixeloid",
+ "rects",
+ "rrect",
+ "serializable",
+ "sparky's",
+ "tappable",
+ "tappables",
+ "texto",
+ "theming",
+ "unawaited",
+ "unfocus",
+ "unlayered",
+ "vsync"
+ ],
+ "ignorePaths": [
+ ".github/workflows/**"
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 1b855b10..e2ea6cee 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -5,30 +5,10 @@
"version": "0.2.0",
"configurations": [
{
- "name": "Launch development",
+ "name": "Launch pinball",
"request": "launch",
"type": "dart",
- "program": "lib/main_development.dart",
- "args": [
- "--flavor",
- "development",
- "--target",
- "lib/main_development.dart"
- ]
- },
- {
- "name": "Launch staging",
- "request": "launch",
- "type": "dart",
- "program": "lib/main_staging.dart",
- "args": ["--flavor", "staging", "--target", "lib/main_staging.dart"]
- },
- {
- "name": "Launch production",
- "request": "launch",
- "type": "dart",
- "program": "lib/main_production.dart",
- "args": ["--flavor", "production", "--target", "lib/main_production.dart"]
+ "program": "lib/main.dart"
},
{
"name": "Launch component sandbox",
diff --git a/README.md b/README.md
index b91a1c98..957cb05b 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,31 @@
-# Pinball
+# I/O Pinball
+[![Pinball Header][logo]][pinball_link]
+
+[![io_pinball][build_status_badge]][workflow_link]
![coverage][coverage_badge]
[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link]
[![License: MIT][license_badge]][license_link]
-Generated by the [Very Good CLI][very_good_cli_link] ๐ค
+A Pinball game built with [Flutter][flutter_link] and [Firebase][firebase_link] for [Google I/O 2022][google_io_link].
-Google I/O 2022 Pinball game built with Flutter and Firebase
+[Try it now][pinball_link] and [learn about how it's made][blog_link].
----
+_Built by [Very Good Ventures][very_good_ventures_link] in partnership with Google_
-## Getting Started ๐
+_Created using [Very Good CLI][very_good_cli_link] ๐ค_
-This project contains 3 flavors:
+---
-- development
-- staging
-- production
+## Getting Started ๐
-To run the desired flavor either use the launch configuration in VSCode/Android Studio or use the following commands:
+To run the desired project either use the launch configuration in VSCode/Android Studio or use the following commands:
```sh
-# Development
-$ flutter run --flavor development --target lib/main_development.dart
-
-# Staging
-$ flutter run --flavor staging --target lib/main_staging.dart
-
-# Production
-$ flutter run --flavor production --target lib/main_production.dart
+$ flutter run -d chrome
```
-_\*Pinball works on iOS, Android, Web, and Windows._
+_\*I/O Pinball works on Web for desktop and mobile._
---
@@ -48,7 +42,6 @@ To view the generated coverage report you can use [lcov](https://github.com/linu
```sh
# Generate Coverage Report
$ genhtml coverage/lcov.info -o coverage/
-
# Open Coverage Report
$ open coverage/index.html
```
@@ -101,22 +94,6 @@ Widget build(BuildContext context) {
}
```
-### Adding Supported Locales
-
-Update the `CFBundleLocalizations` array in the `Info.plist` at `ios/Runner/Info.plist` to include the new locale.
-
-```xml
- ...
-
- CFBundleLocalizations
-
- en
- es
-
-
- ...
-```
-
### Adding Translations
1. For each supported locale, add a new ARB file in `lib/l10n/arb`.
@@ -154,38 +131,20 @@ Update the `CFBundleLocalizations` array in the `Info.plist` at `ios/Runner/Info
}
```
-### Deploy application to Firebase hosting
-
-Follow the following steps to deploy the application.
-
-## Firebase CLI
-
-Install and authenticate with [Firebase CLI tools](https://firebase.google.com/docs/cli)
-
-## Build the project using the desired environment
-
-```bash
-# Development
-$ flutter build web --release --target lib/main_development.dart
-
-# Staging
-$ flutter build web --release --target lib/main_staging.dart
-
-# Production
-$ flutter build web --release --target lib/main_production.dart
-```
-
-## Deploy
-
-```bash
-$ firebase deploy
-```
-
+[build_status_badge]: https://github.com/flutter/pinball/actions/workflows/main.yaml/badge.svg
[coverage_badge]: coverage_badge.svg
+[firebase_link]: https://firebase.google.com/
+[flutter_link]: https://flutter.dev
[flutter_localizations_link]: https://api.flutter.dev/flutter/flutter_localizations/flutter_localizations-library.html
+[google_io_link]: https://events.google.com/io/
+[blog_link]: https://medium.com/flutter/i-o-pinball-powered-by-flutter-and-firebase-d22423f3f5d
[internationalization_link]: https://flutter.dev/docs/development/accessibility-and-localization/internationalization
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
[license_link]: https://opensource.org/licenses/MIT
+[logo]: art/readme_header.png
+[pinball_link]: https://pinball.flutter.dev
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg
[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis
[very_good_cli_link]: https://github.com/VeryGoodOpenSource/very_good_cli
+[very_good_ventures_link]: https://verygood.ventures/
+[workflow_link]: https://github.com/flutter/pinball/actions/workflows/main.yaml
diff --git a/analysis_options.yaml b/analysis_options.yaml
index f8155aa6..eb141d0e 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -2,3 +2,6 @@ include: package:very_good_analysis/analysis_options.2.4.0.yaml
analyzer:
exclude:
- lib/**/*.gen.dart
+linter:
+ rules:
+ public_member_api_docs: false
\ No newline at end of file
diff --git a/art/readme_header.png b/art/readme_header.png
new file mode 100644
index 00000000..43e7fb53
Binary files /dev/null and b/art/readme_header.png differ
diff --git a/assets/images/bonus_animation/android_spaceship.png b/assets/images/bonus_animation/android_spaceship.png
index 200e1c6c..66cc8b6a 100644
Binary files a/assets/images/bonus_animation/android_spaceship.png and b/assets/images/bonus_animation/android_spaceship.png differ
diff --git a/assets/images/bonus_animation/dash_nest.png b/assets/images/bonus_animation/dash_nest.png
index 210f17cb..42aa8b9e 100644
Binary files a/assets/images/bonus_animation/dash_nest.png and b/assets/images/bonus_animation/dash_nest.png differ
diff --git a/assets/images/bonus_animation/dino_chomp.png b/assets/images/bonus_animation/dino_chomp.png
index a8a9cfe3..c6825c0b 100644
Binary files a/assets/images/bonus_animation/dino_chomp.png and b/assets/images/bonus_animation/dino_chomp.png differ
diff --git a/assets/images/bonus_animation/google_word.png b/assets/images/bonus_animation/google_word.png
index c4ab2948..fa07da65 100644
Binary files a/assets/images/bonus_animation/google_word.png and b/assets/images/bonus_animation/google_word.png differ
diff --git a/assets/images/bonus_animation/sparky_turbo_charge.png b/assets/images/bonus_animation/sparky_turbo_charge.png
index 8b3491e8..c5d2d9d3 100644
Binary files a/assets/images/bonus_animation/sparky_turbo_charge.png and b/assets/images/bonus_animation/sparky_turbo_charge.png differ
diff --git a/assets/images/link_box/info_icon.png b/assets/images/link_box/info_icon.png
deleted file mode 100644
index 78556be0..00000000
Binary files a/assets/images/link_box/info_icon.png and /dev/null differ
diff --git a/assets/images/loading_game/io_pinball.png b/assets/images/loading_game/io_pinball.png
new file mode 100644
index 00000000..c8d9fadc
Binary files /dev/null and b/assets/images/loading_game/io_pinball.png differ
diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart
index ae3094e1..f621f1e2 100644
--- a/lib/app/view/app.dart
+++ b/lib/app/view/app.dart
@@ -1,5 +1,3 @@
-// ignore_for_file: public_member_api_docs
-
import 'package:authentication_repository/authentication_repository.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -11,21 +9,25 @@ 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 PinballPlayer pinballPlayer,
+ required ShareRepository shareRepository,
+ required PinballAudioPlayer pinballAudioPlayer,
}) : _authenticationRepository = authenticationRepository,
_leaderboardRepository = leaderboardRepository,
- _pinballPlayer = pinballPlayer,
+ _shareRepository = shareRepository,
+ _pinballAudioPlayer = pinballAudioPlayer,
super(key: key);
final AuthenticationRepository _authenticationRepository;
final LeaderboardRepository _leaderboardRepository;
- final PinballPlayer _pinballPlayer;
+ final ShareRepository _shareRepository;
+ final PinballAudioPlayer _pinballAudioPlayer;
@override
Widget build(BuildContext context) {
@@ -33,7 +35,8 @@ class App extends StatelessWidget {
providers: [
RepositoryProvider.value(value: _authenticationRepository),
RepositoryProvider.value(value: _leaderboardRepository),
- RepositoryProvider.value(value: _pinballPlayer),
+ RepositoryProvider.value(value: _shareRepository),
+ RepositoryProvider.value(value: _pinballAudioPlayer),
],
child: MultiBlocProvider(
providers: [
diff --git a/lib/assets_manager/cubit/assets_manager_cubit.dart b/lib/assets_manager/cubit/assets_manager_cubit.dart
index b97483d4..eb0f7e31 100644
--- a/lib/assets_manager/cubit/assets_manager_cubit.dart
+++ b/lib/assets_manager/cubit/assets_manager_cubit.dart
@@ -1,27 +1,39 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
+import 'package:pinball/game/game.dart';
+import 'package:pinball/select_character/select_character.dart';
+import 'package:pinball_audio/pinball_audio.dart';
part 'assets_manager_state.dart';
-/// {@template assets_manager_cubit}
-/// Cubit responsable for pre loading any game assets
-/// {@endtemplate}
class AssetsManagerCubit extends Cubit {
- /// {@macro assets_manager_cubit}
- AssetsManagerCubit(List loadables)
- : super(
- AssetsManagerState.initial(
- loadables: loadables,
- ),
- );
+ AssetsManagerCubit(this._game, this._audioPlayer)
+ : super(const AssetsManagerState.initial());
+
+ final PinballGame _game;
+ final PinballAudioPlayer _audioPlayer;
- /// Loads the assets
Future load() async {
+ /// Assigning loadables is a very expensive operation. With this purposeful
+ /// delay here, which is a bit random in duration but enough to let the UI
+ /// do its job without adding too much delay for the user, we are letting
+ /// the UI paint first, and then we start loading the assets.
+ await Future.delayed(const Duration(seconds: 1));
+ emit(
+ state.copyWith(
+ loadables: [
+ _game.preFetchLeaderboard(),
+ ..._game.preLoadAssets(),
+ ..._audioPlayer.load(),
+ ...BonusAnimation.loadAssets(),
+ ...SelectedCharacter.loadAssets(),
+ ],
+ ),
+ );
final all = state.loadables.map((loadable) async {
await loadable;
emit(state.copyWith(loaded: [...state.loaded, loadable]));
}).toList();
-
await Future.wait(all);
}
}
diff --git a/lib/assets_manager/cubit/assets_manager_state.dart b/lib/assets_manager/cubit/assets_manager_state.dart
index 8ef1e874..4847adc6 100644
--- a/lib/assets_manager/cubit/assets_manager_state.dart
+++ b/lib/assets_manager/cubit/assets_manager_state.dart
@@ -11,9 +11,8 @@ class AssetsManagerState extends Equatable {
});
/// {@macro assets_manager_state}
- const AssetsManagerState.initial({
- required List loadables,
- }) : this(loadables: loadables, loaded: const []);
+ const AssetsManagerState.initial()
+ : this(loadables: const [], loaded: const []);
/// List of futures to load
final List loadables;
@@ -22,7 +21,11 @@ class AssetsManagerState extends Equatable {
final List loaded;
/// Returns a value between 0 and 1 to indicate the loading progress
- double get progress => loaded.length / loadables.length;
+ double get progress =>
+ loadables.isEmpty ? 0 : loaded.length / loadables.length;
+
+ /// Only returns false if all the assets have been loaded
+ bool get isLoading => progress != 1;
/// Returns a copy of this instance with the given parameters
/// updated
diff --git a/lib/assets_manager/views/assets_loading_page.dart b/lib/assets_manager/views/assets_loading_page.dart
index ddb76803..72476064 100644
--- a/lib/assets_manager/views/assets_loading_page.dart
+++ b/lib/assets_manager/views/assets_loading_page.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/assets_manager/assets_manager.dart';
+import 'package:pinball/gen/gen.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball_ui/pinball_ui.dart';
@@ -16,30 +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: [
- Text(
- l10n.ioPinball,
- style: headline1!.copyWith(fontSize: 80),
- textAlign: TextAlign.center,
- ),
- 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/bootstrap.dart b/lib/bootstrap.dart
index c5e42951..f4028ee1 100644
--- a/lib/bootstrap.dart
+++ b/lib/bootstrap.dart
@@ -1,5 +1,3 @@
-// ignore_for_file: public_member_api_docs
-
import 'dart:async';
import 'dart:developer';
diff --git a/lib/game/behaviors/ball_spawning_behavior.dart b/lib/game/behaviors/ball_spawning_behavior.dart
index 75656d8f..8995c16b 100644
--- a/lib/game/behaviors/ball_spawning_behavior.dart
+++ b/lib/game/behaviors/ball_spawning_behavior.dart
@@ -1,9 +1,9 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/game/game.dart';
+import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
-import 'package:pinball_theme/pinball_theme.dart';
/// Spawns a new [Ball] into the game when all balls are lost and still
/// [GameStatus.playing].
@@ -23,7 +23,9 @@ class BallSpawningBehavior extends Component
void onNewState(GameState state) {
final plunger = gameRef.descendants().whereType().single;
final canvas = gameRef.descendants().whereType().single;
- final characterTheme = readProvider();
+ final characterTheme = readBloc()
+ .state
+ .characterTheme;
final ball = Ball(assetPath: characterTheme.ball.keyName)
..initialPosition = Vector2(
plunger.body.position.x,
diff --git a/lib/game/behaviors/behaviors.dart b/lib/game/behaviors/behaviors.dart
index c7ad6880..bb196cec 100644
--- a/lib/game/behaviors/behaviors.dart
+++ b/lib/game/behaviors/behaviors.dart
@@ -1,5 +1,9 @@
export 'ball_spawning_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 'cow_bumper_noise_behavior.dart';
+export 'kicker_noise_behavior.dart';
export 'scoring_behavior.dart';
diff --git a/lib/game/behaviors/bonus_ball_spawning_behavior.dart b/lib/game/behaviors/bonus_ball_spawning_behavior.dart
new file mode 100644
index 00000000..26fe423d
--- /dev/null
+++ b/lib/game/behaviors/bonus_ball_spawning_behavior.dart
@@ -0,0 +1,30 @@
+import 'package:flame/components.dart';
+import 'package:pinball/select_character/select_character.dart';
+import 'package:pinball_components/pinball_components.dart';
+import 'package:pinball_flame/pinball_flame.dart';
+
+/// {@template bonus_ball_spawning_behavior}
+/// After a duration, spawns a bonus ball from the [DinoWalls] and boosts it
+/// into the middle of the board.
+/// {@endtemplate}
+class BonusBallSpawningBehavior extends TimerComponent with HasGameRef {
+ /// {@macro bonus_ball_spawning_behavior}
+ BonusBallSpawningBehavior()
+ : super(
+ period: 5,
+ removeOnFinish: true,
+ );
+
+ @override
+ void onTick() {
+ final characterTheme = readBloc()
+ .state
+ .characterTheme;
+ gameRef.descendants().whereType().single.add(
+ Ball(assetPath: characterTheme.ball.keyName)
+ ..add(BallImpulsingBehavior(impulse: Vector2(-40, 0)))
+ ..initialPosition = Vector2(29.2, -24.5)
+ ..zIndex = ZIndexes.ballOnBoard,
+ );
+ }
+}
diff --git a/lib/game/behaviors/bonus_noise_behavior.dart b/lib/game/behaviors/bonus_noise_behavior.dart
index 9d67e964..b330dc9f 100644
--- a/lib/game/behaviors/bonus_noise_behavior.dart
+++ b/lib/game/behaviors/bonus_noise_behavior.dart
@@ -15,7 +15,7 @@ class BonusNoiseBehavior extends Component {
},
onNewState: (state) {
final bonus = state.bonusHistory.last;
- final audioPlayer = readProvider();
+ final audioPlayer = readProvider();
switch (bonus) {
case GameBonus.googleWord:
@@ -25,13 +25,13 @@ class BonusNoiseBehavior extends Component {
audioPlayer.play(PinballAudio.sparky);
break;
case GameBonus.dinoChomp:
- // TODO(erickzanardo): Add sound
+ audioPlayer.play(PinballAudio.dino);
break;
case GameBonus.androidSpaceship:
- // TODO(erickzanardo): Add sound
+ audioPlayer.play(PinballAudio.android);
break;
case GameBonus.dashNest:
- // TODO(erickzanardo): Add sound
+ audioPlayer.play(PinballAudio.dash);
break;
}
},
diff --git a/lib/game/behaviors/bumper_noise_behavior.dart b/lib/game/behaviors/bumper_noise_behavior.dart
index 9c5da701..4144241a 100644
--- a/lib/game/behaviors/bumper_noise_behavior.dart
+++ b/lib/game/behaviors/bumper_noise_behavior.dart
@@ -1,5 +1,3 @@
-// ignore_for_file: public_member_api_docs
-
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_flame/pinball_flame.dart';
@@ -8,6 +6,6 @@ class BumperNoiseBehavior extends ContactBehavior {
@override
void beginContact(Object other, Contact contact) {
super.beginContact(other, contact);
- readProvider().play(PinballAudio.bumper);
+ readProvider().play(PinballAudio.bumper);
}
}
diff --git a/lib/game/behaviors/character_selection_behavior.dart b/lib/game/behaviors/character_selection_behavior.dart
new file mode 100644
index 00000000..27003d75
--- /dev/null
+++ b/lib/game/behaviors/character_selection_behavior.dart
@@ -0,0 +1,27 @@
+import 'package:flame/components.dart';
+import 'package:flame_bloc/flame_bloc.dart';
+import 'package:pinball/select_character/select_character.dart';
+import 'package:pinball_components/pinball_components.dart';
+
+/// 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
+ .onCharacterSelected(state.characterTheme);
+ }
+}
diff --git a/lib/game/behaviors/cow_bumper_noise_behavior.dart b/lib/game/behaviors/cow_bumper_noise_behavior.dart
new file mode 100644
index 00000000..14ad1307
--- /dev/null
+++ b/lib/game/behaviors/cow_bumper_noise_behavior.dart
@@ -0,0 +1,13 @@
+// ignore_for_file: public_member_api_docs
+
+import 'package:flame_forge2d/flame_forge2d.dart';
+import 'package:pinball_audio/pinball_audio.dart';
+import 'package:pinball_flame/pinball_flame.dart';
+
+class CowBumperNoiseBehavior extends ContactBehavior {
+ @override
+ void beginContact(Object other, Contact contact) {
+ super.beginContact(other, contact);
+ readProvider().play(PinballAudio.cowMoo);
+ }
+}
diff --git a/lib/game/behaviors/kicker_noise_behavior.dart b/lib/game/behaviors/kicker_noise_behavior.dart
new file mode 100644
index 00000000..a04ffeff
--- /dev/null
+++ b/lib/game/behaviors/kicker_noise_behavior.dart
@@ -0,0 +1,11 @@
+import 'package:flame_forge2d/flame_forge2d.dart';
+import 'package:pinball_audio/pinball_audio.dart';
+import 'package:pinball_flame/pinball_flame.dart';
+
+class KickerNoiseBehavior extends ContactBehavior {
+ @override
+ void beginContact(Object other, Contact contact) {
+ super.beginContact(other, contact);
+ readProvider().play(PinballAudio.kicker);
+ }
+}
diff --git a/lib/game/behaviors/scoring_behavior.dart b/lib/game/behaviors/scoring_behavior.dart
index 8b403d1e..4d6d8b42 100644
--- a/lib/game/behaviors/scoring_behavior.dart
+++ b/lib/game/behaviors/scoring_behavior.dart
@@ -15,7 +15,7 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@endtemplate}
class ScoringBehavior extends Component
with HasGameRef, FlameBlocReader {
- /// {@macto scoring_behavior}
+ /// {@macro scoring_behavior}
ScoringBehavior({
required Points points,
required Vector2 position,
diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart
index b22baa14..c63bf514 100644
--- a/lib/game/bloc/game_bloc.dart
+++ b/lib/game/bloc/game_bloc.dart
@@ -1,4 +1,3 @@
-// ignore_for_file: public_member_api_docs
import 'dart:math' as math;
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
@@ -13,11 +12,12 @@ class GameBloc extends Bloc {
on(_onScored);
on(_onIncreasedMultiplier);
on(_onBonusActivated);
- on(_onSparkyTurboChargeActivated);
on(_onGameOver);
on(_onGameStarted);
}
+ static const _maxScore = 9999999999;
+
void _onGameStarted(GameStarted _, Emitter emit) {
emit(state.copyWith(status: GameStatus.playing));
}
@@ -27,7 +27,10 @@ class GameBloc extends Bloc {
}
void _onRoundLost(RoundLost event, Emitter emit) {
- final score = state.totalScore + state.roundScore * state.multiplier;
+ final score = math.min(
+ state.totalScore + state.roundScore * state.multiplier,
+ _maxScore,
+ );
final roundsLeft = math.max(state.rounds - 1, 0);
emit(
@@ -43,9 +46,11 @@ class GameBloc extends Bloc {
void _onScored(Scored event, Emitter emit) {
if (state.status.isPlaying) {
- emit(
- state.copyWith(roundScore: state.roundScore + event.points),
+ final combinedScore = math.min(
+ state.totalScore + state.roundScore + event.points,
+ _maxScore,
);
+ emit(state.copyWith(roundScore: combinedScore - state.totalScore));
}
}
@@ -66,18 +71,4 @@ class GameBloc extends Bloc {
),
);
}
-
- Future _onSparkyTurboChargeActivated(
- SparkyTurboChargeActivated event,
- Emitter emit,
- ) async {
- emit(
- state.copyWith(
- bonusHistory: [
- ...state.bonusHistory,
- GameBonus.sparkyTurboCharge,
- ],
- ),
- );
- }
}
diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart
index 6dba8056..126ba299 100644
--- a/lib/game/bloc/game_event.dart
+++ b/lib/game/bloc/game_event.dart
@@ -1,5 +1,3 @@
-// ignore_for_file: public_member_api_docs
-
part of 'game_bloc.dart';
@immutable
@@ -42,13 +40,6 @@ class BonusActivated extends GameEvent {
List