Merge branch 'main' into feat/kicker-blinking-assets

pull/283/head
Allison Ryan 3 years ago
commit 1641fae88e

@ -0,0 +1,22 @@
name: authentication_repository
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
paths:
- "packages/authentication_repository/**"
- ".github/workflows/authentication_repository.yaml"
pull_request:
paths:
- "packages/authentication_repository/**"
- ".github/workflows/authentication_repository.yaml"
jobs:
build:
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
with:
working_directory: packages/authentication_repository

@ -7,6 +7,7 @@
// 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';
import 'package:flutter_localizations/flutter_localizations.dart';
@ -15,16 +16,20 @@ import 'package:pinball/game/game.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_ui/pinball_ui.dart';
class App extends StatelessWidget {
const App({
Key? key,
required AuthenticationRepository authenticationRepository,
required LeaderboardRepository leaderboardRepository,
required PinballAudio pinballAudio,
}) : _leaderboardRepository = leaderboardRepository,
}) : _authenticationRepository = authenticationRepository,
_leaderboardRepository = leaderboardRepository,
_pinballAudio = pinballAudio,
super(key: key);
final AuthenticationRepository _authenticationRepository;
final LeaderboardRepository _leaderboardRepository;
final PinballAudio _pinballAudio;
@ -32,19 +37,21 @@ class App extends StatelessWidget {
Widget build(BuildContext context) {
return MultiRepositoryProvider(
providers: [
RepositoryProvider.value(value: _authenticationRepository),
RepositoryProvider.value(value: _leaderboardRepository),
RepositoryProvider.value(value: _pinballAudio),
],
child: BlocProvider(
create: (context) => CharacterThemeCubit(),
child: const MaterialApp(
child: MaterialApp(
title: 'I/O Pinball',
localizationsDelegates: [
theme: PinballTheme.standard,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
home: PinballGamePage(),
home: const PinballGamePage(),
),
),
);

@ -12,6 +12,7 @@ import 'dart:developer';
import 'package:bloc/bloc.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/widgets.dart';
class AppBlocObserver extends BlocObserver {
@ -28,9 +29,12 @@ class AppBlocObserver extends BlocObserver {
}
}
Future<void> bootstrap(
Future<Widget> Function(FirebaseFirestore firestore) builder,
) async {
typedef BootstrapBuilder = Future<Widget> Function(
FirebaseFirestore firestore,
FirebaseAuth firebaseAuth,
);
Future<void> bootstrap(BootstrapBuilder builder) async {
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (details) {
log(details.exceptionAsString(), stackTrace: details.stack);
@ -39,7 +43,12 @@ Future<void> bootstrap(
await runZonedGuarded(
() async {
await BlocOverrides.runZoned(
() async => runApp(await builder(FirebaseFirestore.instance)),
() async => runApp(
await builder(
FirebaseFirestore.instance,
FirebaseAuth.instance,
),
),
blocObserver: AppBlocObserver(),
);
},

@ -0,0 +1,76 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball_ui/pinball_ui.dart';
/// {@template footer}
/// Footer widget with links to the main tech stack.
/// {@endtemplate}
class Footer extends StatelessWidget {
/// {@macro footer}
const Footer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(50, 0, 50, 32),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
_MadeWithFlutterAndFirebase(),
_GoogleIO(),
],
),
);
}
}
class _GoogleIO extends StatelessWidget {
const _GoogleIO({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final theme = Theme.of(context);
return Text(
l10n.footerGoogleIOText,
style: theme.textTheme.bodyText1!.copyWith(color: PinballColors.white),
);
}
}
class _MadeWithFlutterAndFirebase extends StatelessWidget {
const _MadeWithFlutterAndFirebase({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final theme = Theme.of(context);
return RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: l10n.footerMadeWithText,
style: theme.textTheme.bodyText1!.copyWith(color: PinballColors.white),
children: <TextSpan>[
TextSpan(
text: l10n.footerFlutterLinkText,
recognizer: TapGestureRecognizer()
..onTap = () => openLink('https://flutter.dev'),
style: const TextStyle(
decoration: TextDecoration.underline,
),
),
const TextSpan(text: ' & '),
TextSpan(
text: l10n.footerFirebaseLinkText,
recognizer: TapGestureRecognizer()
..onTap = () => openLink('https://firebase.google.com'),
style: const TextStyle(
decoration: TextDecoration.underline,
),
),
],
),
);
}
}

@ -6,8 +6,8 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template android_acres}
/// Area positioned on the left side of the board containing the [Spaceship],
/// [SpaceshipRamp], [SpaceshipRail], and [AndroidBumper]s.
/// Area positioned on the left side of the board containing the
/// [AndroidSpaceship], [SpaceshipRamp], [SpaceshipRail], and [AndroidBumper]s.
/// {@endtemplate}
class AndroidAcres extends Blueprint {
/// {@macro android_acres}
@ -23,7 +23,7 @@ class AndroidAcres extends Blueprint {
children: [
ScoringBehavior(points: 20000),
],
)..initialPosition = Vector2(-32.6, -9.2),
)..initialPosition = Vector2(-32.8, -9.2),
AndroidBumper.cow(
children: [
ScoringBehavior(points: 20),
@ -32,7 +32,7 @@ class AndroidAcres extends Blueprint {
],
blueprints: [
SpaceshipRamp(),
Spaceship(position: Vector2(-26.5, -28.5)),
AndroidSpaceship(position: Vector2(-26.5, -28.5)),
SpaceshipRail(),
],
);

@ -16,6 +16,7 @@ class BottomGroup extends Component {
_BottomGroupSide(side: BoardSide.right),
_BottomGroupSide(side: BoardSide.left),
],
priority: RenderPriority.bottomGroup,
);
}
@ -28,8 +29,7 @@ class _BottomGroupSide extends Component {
/// {@macro bottom_group_side}
_BottomGroupSide({
required BoardSide side,
}) : _side = side,
super(priority: RenderPriority.bottomGroup);
}) : _side = side;
final BoardSide _side;

@ -12,4 +12,4 @@ export 'google_word/google_word.dart';
export 'launcher.dart';
export 'multipliers/multipliers.dart';
export 'scoring_behavior.dart';
export 'sparky_fire_zone.dart';
export 'sparky_scorch.dart';

@ -5,15 +5,13 @@ import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_fire_zone}
/// Area positioned at the top left of the board where the [Ball]
/// can bounce off [SparkyBumper]s.
///
/// When a [Ball] hits [SparkyBumper]s, the bumper animates.
/// {@template sparky_scorch}
/// Area positioned at the top left of the board containing the
/// [SparkyComputer], [SparkyAnimatronic], and [SparkyBumper]s.
/// {@endtemplate}
class SparkyFireZone extends Blueprint {
/// {@macro sparky_fire_zone}
SparkyFireZone()
class SparkyScorch extends Blueprint {
/// {@macro sparky_scorch}
SparkyScorch()
: super(
components: [
SparkyBumper.a(

@ -13,6 +13,7 @@ extension PinballGameAssetsX on PinballGame {
const dinoTheme = DinoTheme();
return [
images.load(components.Assets.images.boardBackground.keyName),
images.load(components.Assets.images.ball.ball.keyName),
images.load(components.Assets.images.ball.flameEffect.keyName),
images.load(components.Assets.images.signpost.inactive.keyName),
@ -50,50 +51,52 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.boundary.bottom.keyName),
images.load(components.Assets.images.boundary.outer.keyName),
images.load(components.Assets.images.boundary.outerBottom.keyName),
images.load(components.Assets.images.spaceship.saucer.keyName),
images.load(components.Assets.images.spaceship.bridge.keyName),
images.load(components.Assets.images.spaceship.ramp.boardOpening.keyName),
images.load(components.Assets.images.android.spaceship.saucer.keyName),
images
.load(components.Assets.images.android.spaceship.animatronic.keyName),
images.load(components.Assets.images.android.spaceship.lightBeam.keyName),
images.load(components.Assets.images.android.ramp.boardOpening.keyName),
images.load(
components.Assets.images.spaceship.ramp.railingForeground.keyName,
components.Assets.images.android.ramp.railingForeground.keyName,
),
images.load(
components.Assets.images.spaceship.ramp.railingBackground.keyName,
components.Assets.images.android.ramp.railingBackground.keyName,
),
images.load(components.Assets.images.spaceship.ramp.main.keyName),
images
.load(components.Assets.images.spaceship.ramp.arrow.inactive.keyName),
images.load(components.Assets.images.android.ramp.main.keyName),
images.load(components.Assets.images.android.ramp.arrow.inactive.keyName),
images.load(
components.Assets.images.spaceship.ramp.arrow.active1.keyName,
components.Assets.images.android.ramp.arrow.active1.keyName,
),
images.load(
components.Assets.images.spaceship.ramp.arrow.active2.keyName,
components.Assets.images.android.ramp.arrow.active2.keyName,
),
images.load(
components.Assets.images.spaceship.ramp.arrow.active3.keyName,
components.Assets.images.android.ramp.arrow.active3.keyName,
),
images.load(
components.Assets.images.spaceship.ramp.arrow.active4.keyName,
components.Assets.images.android.ramp.arrow.active4.keyName,
),
images.load(
components.Assets.images.spaceship.ramp.arrow.active5.keyName,
components.Assets.images.android.ramp.arrow.active5.keyName,
),
images.load(components.Assets.images.spaceship.rail.main.keyName),
images.load(components.Assets.images.spaceship.rail.exit.keyName),
images.load(components.Assets.images.androidBumper.a.lit.keyName),
images.load(components.Assets.images.androidBumper.a.dimmed.keyName),
images.load(components.Assets.images.androidBumper.b.lit.keyName),
images.load(components.Assets.images.androidBumper.b.dimmed.keyName),
images.load(components.Assets.images.androidBumper.cow.lit.keyName),
images.load(components.Assets.images.androidBumper.cow.dimmed.keyName),
images.load(components.Assets.images.android.rail.main.keyName),
images.load(components.Assets.images.android.rail.exit.keyName),
images.load(components.Assets.images.android.bumper.a.lit.keyName),
images.load(components.Assets.images.android.bumper.a.dimmed.keyName),
images.load(components.Assets.images.android.bumper.b.lit.keyName),
images.load(components.Assets.images.android.bumper.b.dimmed.keyName),
images.load(components.Assets.images.android.bumper.cow.lit.keyName),
images.load(components.Assets.images.android.bumper.cow.dimmed.keyName),
images.load(components.Assets.images.sparky.computer.top.keyName),
images.load(components.Assets.images.sparky.computer.base.keyName),
images.load(components.Assets.images.sparky.computer.glow.keyName),
images.load(components.Assets.images.sparky.animatronic.keyName),
images.load(components.Assets.images.sparky.bumper.a.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.a.active.keyName),
images.load(components.Assets.images.sparky.bumper.b.active.keyName),
images.load(components.Assets.images.sparky.bumper.b.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.c.active.keyName),
images.load(components.Assets.images.sparky.bumper.c.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.a.lit.keyName),
images.load(components.Assets.images.sparky.bumper.a.dimmed.keyName),
images.load(components.Assets.images.sparky.bumper.b.lit.keyName),
images.load(components.Assets.images.sparky.bumper.b.dimmed.keyName),
images.load(components.Assets.images.sparky.bumper.c.lit.keyName),
images.load(components.Assets.images.sparky.bumper.c.dimmed.keyName),
images.load(components.Assets.images.backboard.backboardScores.keyName),
images.load(components.Assets.images.backboard.backboardGameOver.keyName),
images.load(components.Assets.images.googleWord.letter1.keyName),

@ -9,11 +9,10 @@ import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/gen/assets.gen.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
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_theme/pinball_theme.dart';
class PinballGame extends Forge2DGame
with
@ -46,16 +45,16 @@ class PinballGame extends Forge2DGame
unawaited(add(gameFlowController = GameFlowController(this)));
unawaited(add(CameraController(this)));
unawaited(add(Backboard.waiting(position: Vector2(0, -88))));
await add(BoardBackgroundSpriteComponent());
await add(Drain());
await add(BottomGroup());
unawaited(addFromBlueprint(Boundaries()));
unawaited(addFromBlueprint(LaunchRamp()));
final launcher = Launcher();
unawaited(addFromBlueprint(launcher));
await add(Multipliers());
await add(FlutterForest());
await addFromBlueprint(SparkyFireZone());
await addFromBlueprint(SparkyScorch());
await addFromBlueprint(AndroidAcres());
await addFromBlueprint(DinoDesert());
unawaited(addFromBlueprint(Slingshots()));
@ -68,7 +67,7 @@ class PinballGame extends Forge2DGame
),
);
controller.attachTo(launcher.components.whereType<Plunger>().first);
controller.attachTo(launcher.components.whereType<Plunger>().single);
await super.onLoad();
}
@ -186,26 +185,25 @@ class DebugPinballGame extends PinballGame with FPSCounter {
@override
Future<void> onLoad() async {
await super.onLoad();
await _loadBackground();
await add(_DebugInformation());
}
// TODO(alestiago): Move to PinballGame once we have the real background
// component.
Future<void> _loadBackground() async {
final sprite = await loadSprite(
Assets.images.components.background.path,
);
final spriteComponent = SpriteComponent(
sprite: sprite,
size: Vector2(120, 160),
anchor: Anchor.center,
)
..position = Vector2(0, -7.8)
..priority = RenderPriority.background;
await add(spriteComponent);
}
// TODO(allisonryan0002): Remove after google letters have been correctly
// placed.
// Future<void> _loadBackground() async {
// final sprite = await loadSprite(
// Assets.images.components.background.path,
// );
// final spriteComponent = SpriteComponent(
// sprite: sprite,
// size: Vector2(120, 160),
// anchor: Anchor.center,
// )
// ..position = Vector2(0, -7.8)
// ..priority = RenderPriority.boardBackground;
// await add(spriteComponent);
// }
@override
void onTapUp(TapUpInfo info) {

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/gen/gen.dart';
import 'package:pinball/theme/app_colors.dart';
import 'package:pinball_ui/pinball_ui.dart';
/// {@template game_hud}
/// Overlay on the [PinballGame].
@ -72,7 +72,7 @@ class _ScoreViewDecoration extends StatelessWidget {
decoration: BoxDecoration(
borderRadius: radius,
border: Border.all(
color: AppColors.white,
color: PinballColors.white,
width: borderWidth,
),
image: DecorationImage(

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/theme/theme.dart';
import 'package:pinball_ui/pinball_ui.dart';
/// {@template round_count_display}
/// Colored square indicating if a round is available.
@ -20,9 +20,7 @@ class RoundCountDisplay extends StatelessWidget {
children: [
Text(
l10n.rounds,
style: AppTextStyle.subtitle1.copyWith(
color: AppColors.yellow,
),
style: Theme.of(context).textTheme.subtitle1,
),
const SizedBox(width: 8),
Row(
@ -53,9 +51,9 @@ class RoundIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
final color = isActive ? AppColors.yellow : AppColors.yellow.withAlpha(128);
final color =
isActive ? PinballColors.yellow : PinballColors.yellow.withAlpha(128);
const size = 8.0;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Container(

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/theme/theme.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template score_view}
@ -38,9 +37,7 @@ class _GameOver extends StatelessWidget {
return Text(
l10n.gameOver,
style: AppTextStyle.headline1.copyWith(
color: AppColors.white,
),
style: Theme.of(context).textTheme.headline1,
);
}
}
@ -58,9 +55,7 @@ class _ScoreDisplay extends StatelessWidget {
children: [
Text(
l10n.score.toLowerCase(),
style: AppTextStyle.subtitle1.copyWith(
color: AppColors.yellow,
),
style: Theme.of(context).textTheme.subtitle1,
),
const _ScoreText(),
const RoundCountDisplay(),
@ -78,9 +73,7 @@ class _ScoreText extends StatelessWidget {
return Text(
score.formatScore(),
style: AppTextStyle.headline1.copyWith(
color: AppColors.white,
),
style: Theme.of(context).textTheme.headline1,
);
}
}

@ -107,5 +107,21 @@
"rounds": "Ball Ct:",
"@rounds": {
"description": "Text displayed on the scoreboard widget to indicate rounds left"
},
"footerMadeWithText": "Made with ",
"@footerMadeWithText": {
"description": "Text shown on the footer which mentions technologies used to build the app."
},
"footerFlutterLinkText": "Flutter",
"@footerFlutterLinkText": {
"description": "Text on the link shown on the footer which navigates to the Flutter page"
},
"footerFirebaseLinkText": "Firebase",
"@footerFirebaseLinkText": {
"description": "Text on the link shown on the footer which navigates to the Firebase page"
},
"footerGoogleIOText": "Google I/O",
"@footerGoogleIOText": {
"description": "Text shown on the footer which mentions Google I/O"
}
}

@ -1,15 +0,0 @@
{
"@@locale": "es",
"play": "Jugar",
"@play": {
"description": "Text displayed on the landing page play button"
},
"start": "Comienzo",
"@start": {
"description": "Text displayed on the character selection page start button"
},
"characterSelectionTitle": "¡Elige a tu personaje!",
"@characterSelectionTitle": {
"description": "Title text displayed on the character selection page"
}
}

@ -5,16 +5,27 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:async';
import 'package:authentication_repository/authentication_repository.dart';
import 'package:firebase_core/firebase_core.dart';
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';
void main() {
bootstrap((firestore) async {
bootstrap((firestore, firebaseAuth) async {
final leaderboardRepository = LeaderboardRepository(firestore);
final authenticationRepository = AuthenticationRepository(firebaseAuth);
final pinballAudio = PinballAudio();
unawaited(
Firebase.initializeApp().then(
(_) => authenticationRepository.authenticateAnonymously(),
),
);
return App(
authenticationRepository: authenticationRepository,
leaderboardRepository: leaderboardRepository,
pinballAudio: pinballAudio,
);

@ -5,16 +5,27 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:async';
import 'package:authentication_repository/authentication_repository.dart';
import 'package:firebase_core/firebase_core.dart';
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';
void main() {
bootstrap((firestore) async {
bootstrap((firestore, firebaseAuth) async {
final leaderboardRepository = LeaderboardRepository(firestore);
final authenticationRepository = AuthenticationRepository(firebaseAuth);
final pinballAudio = PinballAudio();
unawaited(
Firebase.initializeApp().then(
(_) => authenticationRepository.authenticateAnonymously(),
),
);
return App(
authenticationRepository: authenticationRepository,
leaderboardRepository: leaderboardRepository,
pinballAudio: pinballAudio,
);

@ -5,16 +5,27 @@
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'dart:async';
import 'package:authentication_repository/authentication_repository.dart';
import 'package:firebase_core/firebase_core.dart';
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';
void main() {
bootstrap((firestore) async {
bootstrap((firestore, firebaseAuth) async {
final leaderboardRepository = LeaderboardRepository(firestore);
final authenticationRepository = AuthenticationRepository(firebaseAuth);
final pinballAudio = PinballAudio();
unawaited(
Firebase.initializeApp().then(
(_) => authenticationRepository.authenticateAnonymously(),
),
);
return App(
authenticationRepository: authenticationRepository,
leaderboardRepository: leaderboardRepository,
pinballAudio: pinballAudio,
);

@ -5,7 +5,6 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:pinball/gen/gen.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/theme/theme.dart';
import 'package:pinball_ui/pinball_ui.dart';
import 'package:platform_helper/platform_helper.dart';
@ -122,7 +121,7 @@ class _MobileLaunchControls extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
const textStyle = AppTextStyle.subtitle3;
final textStyle = Theme.of(context).textTheme.headline3;
return Column(
children: [
Text(
@ -138,9 +137,7 @@ class _MobileLaunchControls extends StatelessWidget {
),
TextSpan(
text: l10n.launch,
style: textStyle.copyWith(
color: AppColors.blue,
),
style: textStyle?.copyWith(color: PinballColors.blue),
),
],
),
@ -156,7 +153,7 @@ class _MobileFlipperControls extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
const textStyle = AppTextStyle.subtitle3;
final textStyle = Theme.of(context).textTheme.headline3;
return Column(
children: [
Text(
@ -172,9 +169,7 @@ class _MobileFlipperControls extends StatelessWidget {
),
TextSpan(
text: l10n.flip,
style: textStyle.copyWith(
color: AppColors.orange,
),
style: textStyle?.copyWith(color: PinballColors.orange),
),
],
),
@ -207,21 +202,22 @@ class _HowToPlayHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
const headerTextStyle = AppTextStyle.title;
final textStyle = Theme.of(context).textTheme.headline3?.copyWith(
color: PinballColors.darkBlue,
);
return FittedBox(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
l10n.howToPlay,
style: headerTextStyle.copyWith(
style: textStyle?.copyWith(
fontWeight: FontWeight.bold,
),
),
Text(
l10n.tipsForFlips,
style: headerTextStyle,
style: textStyle,
),
],
),
@ -241,7 +237,7 @@ class _DesktopLaunchControls extends StatelessWidget {
children: [
Text(
l10n.launchControls,
style: AppTextStyle.headline4,
style: Theme.of(context).textTheme.headline4,
),
const SizedBox(height: 10),
Wrap(
@ -270,7 +266,7 @@ class _DesktopFlipperControls extends StatelessWidget {
children: [
Text(
l10n.flipperControls,
style: AppTextStyle.subtitle2,
style: Theme.of(context).textTheme.subtitle2,
),
const SizedBox(height: 10),
Column(
@ -311,8 +307,9 @@ class KeyButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
final textStyle =
_control.isArrow ? AppTextStyle.headline1 : AppTextStyle.headline3;
_control.isArrow ? textTheme.headline1 : textTheme.headline3;
const height = 60.0;
final width = _control.isSpace ? height * 2.83 : height;
return DecoratedBox(
@ -334,7 +331,7 @@ class KeyButton extends StatelessWidget {
quarterTurns: _control.isDown ? 1 : 0,
child: Text(
_control.getCharacter(context),
style: textStyle.copyWith(color: AppColors.white),
style: textStyle?.copyWith(color: PinballColors.white),
),
),
),

@ -1,2 +0,0 @@
export 'app_colors.dart';
export 'app_text_style.dart';

@ -0,0 +1,39 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# VSCode related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json

@ -0,0 +1,11 @@
# authentication_repository
[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link]
[![License: MIT][license_badge]][license_link]
Repository to manage user authentication.
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
[license_link]: https://opensource.org/licenses/MIT
[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

@ -0,0 +1 @@
include: package:very_good_analysis/analysis_options.2.4.0.yaml

@ -0,0 +1,3 @@
library authentication_repository;
export 'src/authentication_repository.dart';

@ -0,0 +1,36 @@
import 'package:firebase_auth/firebase_auth.dart';
/// {@template authentication_exception}
/// Exception for authentication repository failures.
/// {@endtemplate}
class AuthenticationException implements Exception {
/// {@macro authentication_exception}
const AuthenticationException(this.error, this.stackTrace);
/// The error that was caught.
final Object error;
/// The Stacktrace associated with the [error].
final StackTrace stackTrace;
}
/// {@template authentication_repository}
/// Repository to manage user authentication.
/// {@endtemplate}
class AuthenticationRepository {
/// {@macro authentication_repository}
AuthenticationRepository(this._firebaseAuth);
final FirebaseAuth _firebaseAuth;
/// Sign in the existing user anonymously using [FirebaseAuth]. If the
/// authentication process can't be completed, it will throw an
/// [AuthenticationException].
Future<void> authenticateAnonymously() async {
try {
await _firebaseAuth.signInAnonymously();
} on Exception catch (error, stackTrace) {
throw AuthenticationException(error, stackTrace);
}
}
}

@ -0,0 +1,18 @@
name: authentication_repository
description: Repository to manage user authentication.
version: 1.0.0+1
publish_to: none
environment:
sdk: ">=2.16.0 <3.0.0"
dependencies:
firebase_auth: ^3.3.16
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
mocktail: ^0.2.0
very_good_analysis: ^2.4.0

@ -0,0 +1,40 @@
import 'package:authentication_repository/authentication_repository.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class MockFirebaseAuth extends Mock implements FirebaseAuth {}
class MockUserCredential extends Mock implements UserCredential {}
void main() {
late FirebaseAuth firebaseAuth;
late UserCredential userCredential;
late AuthenticationRepository authenticationRepository;
group('AuthenticationRepository', () {
setUp(() {
firebaseAuth = MockFirebaseAuth();
userCredential = MockUserCredential();
authenticationRepository = AuthenticationRepository(firebaseAuth);
});
group('authenticateAnonymously', () {
test('completes if no exception is thrown', () async {
when(() => firebaseAuth.signInAnonymously())
.thenAnswer((_) async => userCredential);
await authenticationRepository.authenticateAnonymously();
verify(() => firebaseAuth.signInAnonymously()).called(1);
});
test('throws AuthenticationException when firebase auth fails', () async {
when(() => firebaseAuth.signInAnonymously())
.thenThrow(Exception('oops'));
expect(
() => authenticationRepository.authenticateAnonymously(),
throwsA(isA<AuthenticationException>()),
);
});
});
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

@ -10,11 +10,14 @@ import 'package:flutter/widgets.dart';
class $AssetsImagesGen {
const $AssetsImagesGen();
$AssetsImagesAndroidBumperGen get androidBumper =>
const $AssetsImagesAndroidBumperGen();
$AssetsImagesAndroidGen get android => const $AssetsImagesAndroidGen();
$AssetsImagesBackboardGen get backboard => const $AssetsImagesBackboardGen();
$AssetsImagesBallGen get ball => const $AssetsImagesBallGen();
$AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen();
/// File path: assets/images/board-background.png
AssetGenImage get boardBackground =>
const AssetGenImage('assets/images/board-background.png');
$AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen();
$AssetsImagesDashGen get dash => const $AssetsImagesDashGen();
$AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen();
@ -29,19 +32,18 @@ class $AssetsImagesGen {
$AssetsImagesPlungerGen get plunger => const $AssetsImagesPlungerGen();
$AssetsImagesSignpostGen get signpost => const $AssetsImagesSignpostGen();
$AssetsImagesSlingshotGen get slingshot => const $AssetsImagesSlingshotGen();
$AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen();
$AssetsImagesSparkyGen get sparky => const $AssetsImagesSparkyGen();
}
class $AssetsImagesAndroidBumperGen {
const $AssetsImagesAndroidBumperGen();
class $AssetsImagesAndroidGen {
const $AssetsImagesAndroidGen();
$AssetsImagesAndroidBumperAGen get a =>
const $AssetsImagesAndroidBumperAGen();
$AssetsImagesAndroidBumperBGen get b =>
const $AssetsImagesAndroidBumperBGen();
$AssetsImagesAndroidBumperCowGen get cow =>
const $AssetsImagesAndroidBumperCowGen();
$AssetsImagesAndroidBumperGen get bumper =>
const $AssetsImagesAndroidBumperGen();
$AssetsImagesAndroidRailGen get rail => const $AssetsImagesAndroidRailGen();
$AssetsImagesAndroidRampGen get ramp => const $AssetsImagesAndroidRampGen();
$AssetsImagesAndroidSpaceshipGen get spaceship =>
const $AssetsImagesAndroidSpaceshipGen();
}
class $AssetsImagesBackboardGen {
@ -246,23 +248,6 @@ class $AssetsImagesSlingshotGen {
const AssetGenImage('assets/images/slingshot/upper.png');
}
class $AssetsImagesSpaceshipGen {
const $AssetsImagesSpaceshipGen();
/// File path: assets/images/spaceship/bridge.png
AssetGenImage get bridge =>
const AssetGenImage('assets/images/spaceship/bridge.png');
$AssetsImagesSpaceshipRailGen get rail =>
const $AssetsImagesSpaceshipRailGen();
$AssetsImagesSpaceshipRampGen get ramp =>
const $AssetsImagesSpaceshipRampGen();
/// File path: assets/images/spaceship/saucer.png
AssetGenImage get saucer =>
const AssetGenImage('assets/images/spaceship/saucer.png');
}
class $AssetsImagesSparkyGen {
const $AssetsImagesSparkyGen();
@ -276,40 +261,66 @@ class $AssetsImagesSparkyGen {
const $AssetsImagesSparkyComputerGen();
}
class $AssetsImagesAndroidBumperAGen {
const $AssetsImagesAndroidBumperAGen();
class $AssetsImagesAndroidBumperGen {
const $AssetsImagesAndroidBumperGen();
/// File path: assets/images/android_bumper/a/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/android_bumper/a/dimmed.png');
$AssetsImagesAndroidBumperAGen get a =>
const $AssetsImagesAndroidBumperAGen();
$AssetsImagesAndroidBumperBGen get b =>
const $AssetsImagesAndroidBumperBGen();
$AssetsImagesAndroidBumperCowGen get cow =>
const $AssetsImagesAndroidBumperCowGen();
}
/// File path: assets/images/android_bumper/a/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/android_bumper/a/lit.png');
class $AssetsImagesAndroidRailGen {
const $AssetsImagesAndroidRailGen();
/// File path: assets/images/android/rail/exit.png
AssetGenImage get exit =>
const AssetGenImage('assets/images/android/rail/exit.png');
/// File path: assets/images/android/rail/main.png
AssetGenImage get main =>
const AssetGenImage('assets/images/android/rail/main.png');
}
class $AssetsImagesAndroidBumperBGen {
const $AssetsImagesAndroidBumperBGen();
class $AssetsImagesAndroidRampGen {
const $AssetsImagesAndroidRampGen();
/// File path: assets/images/android_bumper/b/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/android_bumper/b/dimmed.png');
$AssetsImagesAndroidRampArrowGen get arrow =>
const $AssetsImagesAndroidRampArrowGen();
/// File path: assets/images/android_bumper/b/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/android_bumper/b/lit.png');
/// File path: assets/images/android/ramp/board-opening.png
AssetGenImage get boardOpening =>
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
AssetGenImage get railingBackground =>
const AssetGenImage('assets/images/android/ramp/railing-background.png');
/// File path: assets/images/android/ramp/railing-foreground.png
AssetGenImage get railingForeground =>
const AssetGenImage('assets/images/android/ramp/railing-foreground.png');
}
class $AssetsImagesAndroidBumperCowGen {
const $AssetsImagesAndroidBumperCowGen();
class $AssetsImagesAndroidSpaceshipGen {
const $AssetsImagesAndroidSpaceshipGen();
/// File path: assets/images/android_bumper/cow/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/android_bumper/cow/dimmed.png');
/// File path: assets/images/android/spaceship/animatronic.png
AssetGenImage get animatronic =>
const AssetGenImage('assets/images/android/spaceship/animatronic.png');
/// File path: assets/images/android_bumper/cow/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/android_bumper/cow/lit.png');
/// File path: assets/images/android/spaceship/light-beam.png
AssetGenImage get lightBeam =>
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 $AssetsImagesDashBumperGen {
@ -393,41 +404,6 @@ class $AssetsImagesMultiplierX6Gen {
const AssetGenImage('assets/images/multiplier/x6/lit.png');
}
class $AssetsImagesSpaceshipRailGen {
const $AssetsImagesSpaceshipRailGen();
/// File path: assets/images/spaceship/rail/exit.png
AssetGenImage get exit =>
const AssetGenImage('assets/images/spaceship/rail/exit.png');
/// File path: assets/images/spaceship/rail/main.png
AssetGenImage get main =>
const AssetGenImage('assets/images/spaceship/rail/main.png');
}
class $AssetsImagesSpaceshipRampGen {
const $AssetsImagesSpaceshipRampGen();
$AssetsImagesSpaceshipRampArrowGen get arrow =>
const $AssetsImagesSpaceshipRampArrowGen();
/// File path: assets/images/spaceship/ramp/board-opening.png
AssetGenImage get boardOpening =>
const AssetGenImage('assets/images/spaceship/ramp/board-opening.png');
/// File path: assets/images/spaceship/ramp/main.png
AssetGenImage get main =>
const AssetGenImage('assets/images/spaceship/ramp/main.png');
/// File path: assets/images/spaceship/ramp/railing-background.png
AssetGenImage get railingBackground => const AssetGenImage(
'assets/images/spaceship/ramp/railing-background.png');
/// File path: assets/images/spaceship/ramp/railing-foreground.png
AssetGenImage get railingForeground => const AssetGenImage(
'assets/images/spaceship/ramp/railing-foreground.png');
}
class $AssetsImagesSparkyBumperGen {
const $AssetsImagesSparkyBumperGen();
@ -443,11 +419,79 @@ class $AssetsImagesSparkyComputerGen {
AssetGenImage get base =>
const AssetGenImage('assets/images/sparky/computer/base.png');
/// File path: assets/images/sparky/computer/glow.png
AssetGenImage get glow =>
const AssetGenImage('assets/images/sparky/computer/glow.png');
/// File path: assets/images/sparky/computer/top.png
AssetGenImage get top =>
const AssetGenImage('assets/images/sparky/computer/top.png');
}
class $AssetsImagesAndroidBumperAGen {
const $AssetsImagesAndroidBumperAGen();
/// File path: assets/images/android/bumper/a/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/android/bumper/a/dimmed.png');
/// File path: assets/images/android/bumper/a/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/android/bumper/a/lit.png');
}
class $AssetsImagesAndroidBumperBGen {
const $AssetsImagesAndroidBumperBGen();
/// File path: assets/images/android/bumper/b/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/android/bumper/b/dimmed.png');
/// File path: assets/images/android/bumper/b/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/android/bumper/b/lit.png');
}
class $AssetsImagesAndroidBumperCowGen {
const $AssetsImagesAndroidBumperCowGen();
/// File path: assets/images/android/bumper/cow/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/android/bumper/cow/dimmed.png');
/// File path: assets/images/android/bumper/cow/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/android/bumper/cow/lit.png');
}
class $AssetsImagesAndroidRampArrowGen {
const $AssetsImagesAndroidRampArrowGen();
/// File path: assets/images/android/ramp/arrow/active1.png
AssetGenImage get active1 =>
const AssetGenImage('assets/images/android/ramp/arrow/active1.png');
/// File path: assets/images/android/ramp/arrow/active2.png
AssetGenImage get active2 =>
const AssetGenImage('assets/images/android/ramp/arrow/active2.png');
/// File path: assets/images/android/ramp/arrow/active3.png
AssetGenImage get active3 =>
const AssetGenImage('assets/images/android/ramp/arrow/active3.png');
/// File path: assets/images/android/ramp/arrow/active4.png
AssetGenImage get active4 =>
const AssetGenImage('assets/images/android/ramp/arrow/active4.png');
/// File path: assets/images/android/ramp/arrow/active5.png
AssetGenImage get active5 =>
const AssetGenImage('assets/images/android/ramp/arrow/active5.png');
/// File path: assets/images/android/ramp/arrow/inactive.png
AssetGenImage get inactive =>
const AssetGenImage('assets/images/android/ramp/arrow/inactive.png');
}
class $AssetsImagesDashBumperAGen {
const $AssetsImagesDashBumperAGen();
@ -484,68 +528,40 @@ class $AssetsImagesDashBumperMainGen {
const AssetGenImage('assets/images/dash/bumper/main/inactive.png');
}
class $AssetsImagesSpaceshipRampArrowGen {
const $AssetsImagesSpaceshipRampArrowGen();
/// File path: assets/images/spaceship/ramp/arrow/active1.png
AssetGenImage get active1 =>
const AssetGenImage('assets/images/spaceship/ramp/arrow/active1.png');
/// File path: assets/images/spaceship/ramp/arrow/active2.png
AssetGenImage get active2 =>
const AssetGenImage('assets/images/spaceship/ramp/arrow/active2.png');
/// File path: assets/images/spaceship/ramp/arrow/active3.png
AssetGenImage get active3 =>
const AssetGenImage('assets/images/spaceship/ramp/arrow/active3.png');
/// File path: assets/images/spaceship/ramp/arrow/active4.png
AssetGenImage get active4 =>
const AssetGenImage('assets/images/spaceship/ramp/arrow/active4.png');
/// File path: assets/images/spaceship/ramp/arrow/active5.png
AssetGenImage get active5 =>
const AssetGenImage('assets/images/spaceship/ramp/arrow/active5.png');
/// File path: assets/images/spaceship/ramp/arrow/inactive.png
AssetGenImage get inactive =>
const AssetGenImage('assets/images/spaceship/ramp/arrow/inactive.png');
}
class $AssetsImagesSparkyBumperAGen {
const $AssetsImagesSparkyBumperAGen();
/// File path: assets/images/sparky/bumper/a/active.png
AssetGenImage get active =>
const AssetGenImage('assets/images/sparky/bumper/a/active.png');
/// File path: assets/images/sparky/bumper/a/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/sparky/bumper/a/dimmed.png');
/// File path: assets/images/sparky/bumper/a/inactive.png
AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky/bumper/a/inactive.png');
/// File path: assets/images/sparky/bumper/a/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/sparky/bumper/a/lit.png');
}
class $AssetsImagesSparkyBumperBGen {
const $AssetsImagesSparkyBumperBGen();
/// File path: assets/images/sparky/bumper/b/active.png
AssetGenImage get active =>
const AssetGenImage('assets/images/sparky/bumper/b/active.png');
/// File path: assets/images/sparky/bumper/b/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/sparky/bumper/b/dimmed.png');
/// File path: assets/images/sparky/bumper/b/inactive.png
AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky/bumper/b/inactive.png');
/// File path: assets/images/sparky/bumper/b/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/sparky/bumper/b/lit.png');
}
class $AssetsImagesSparkyBumperCGen {
const $AssetsImagesSparkyBumperCGen();
/// File path: assets/images/sparky/bumper/c/active.png
AssetGenImage get active =>
const AssetGenImage('assets/images/sparky/bumper/c/active.png');
/// File path: assets/images/sparky/bumper/c/dimmed.png
AssetGenImage get dimmed =>
const AssetGenImage('assets/images/sparky/bumper/c/dimmed.png');
/// File path: assets/images/sparky/bumper/c/inactive.png
AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky/bumper/c/inactive.png');
/// File path: assets/images/sparky/bumper/c/lit.png
AssetGenImage get lit =>
const AssetGenImage('assets/images/sparky/bumper/c/lit.png');
}
class Assets {

@ -10,7 +10,7 @@ import 'package:pinball_flame/pinball_flame.dart';
export 'cubit/android_bumper_cubit.dart';
/// {@template android_bumper}
/// Bumper for area under the [Spaceship].
/// Bumper for area under the [AndroidSpaceship].
/// {@endtemplate}
class AndroidBumper extends BodyComponent with InitialPosition {
/// {@macro android_bumper}
@ -46,8 +46,8 @@ class AndroidBumper extends BodyComponent with InitialPosition {
}) : this._(
majorRadius: 3.52,
minorRadius: 2.97,
litAssetPath: Assets.images.androidBumper.a.lit.keyName,
dimmedAssetPath: Assets.images.androidBumper.a.dimmed.keyName,
litAssetPath: Assets.images.android.bumper.a.lit.keyName,
dimmedAssetPath: Assets.images.android.bumper.a.dimmed.keyName,
spritePosition: Vector2(0, -0.1),
bloc: AndroidBumperCubit(),
children: children,
@ -59,8 +59,8 @@ class AndroidBumper extends BodyComponent with InitialPosition {
}) : this._(
majorRadius: 3.19,
minorRadius: 2.79,
litAssetPath: Assets.images.androidBumper.b.lit.keyName,
dimmedAssetPath: Assets.images.androidBumper.b.dimmed.keyName,
litAssetPath: Assets.images.android.bumper.b.lit.keyName,
dimmedAssetPath: Assets.images.android.bumper.b.dimmed.keyName,
spritePosition: Vector2(0, -0.1),
bloc: AndroidBumperCubit(),
children: children,
@ -72,8 +72,8 @@ class AndroidBumper extends BodyComponent with InitialPosition {
}) : this._(
majorRadius: 3.4,
minorRadius: 2.9,
litAssetPath: Assets.images.androidBumper.cow.lit.keyName,
dimmedAssetPath: Assets.images.androidBumper.cow.dimmed.keyName,
litAssetPath: Assets.images.android.bumper.cow.lit.keyName,
dimmedAssetPath: Assets.images.android.bumper.cow.dimmed.keyName,
spritePosition: Vector2(0, -0.68),
bloc: AndroidBumperCubit(),
children: children,

@ -0,0 +1,209 @@
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'dart:math' as math;
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
class AndroidSpaceship extends Blueprint {
AndroidSpaceship({required Vector2 position})
: super(
components: [
_SpaceshipSaucer()..initialPosition = position,
_SpaceshipSaucerSpriteAnimationComponent()..position = position,
_LightBeamSpriteComponent()..position = position + Vector2(2.5, 5),
_AndroidHead()..initialPosition = position + Vector2(0.5, 0.25),
_SpaceshipHole(
outsideLayer: Layer.spaceshipExitRail,
outsidePriority: RenderPriority.ballOnSpaceshipRail,
)..initialPosition = position - Vector2(5.3, -5.4),
_SpaceshipHole(
outsideLayer: Layer.board,
outsidePriority: RenderPriority.ballOnBoard,
)..initialPosition = position - Vector2(-7.5, -1.1),
],
);
}
class _SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
_SpaceshipSaucer() : super(renderBody: false) {
layer = Layer.spaceship;
}
@override
Body createBody() {
final shape = _SpaceshipSaucerShape();
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
angle: -1.7,
);
return world.createBody(bodyDef)..createFixtureFromShape(shape);
}
}
class _SpaceshipSaucerShape extends ChainShape {
_SpaceshipSaucerShape() {
const minorRadius = 9.75;
const majorRadius = 11.9;
createChain(
[
for (var angle = 0.2618; angle <= 6.0214; angle += math.pi / 180)
Vector2(
minorRadius * math.cos(angle),
majorRadius * math.sin(angle),
),
],
);
}
}
class _SpaceshipSaucerSpriteAnimationComponent extends SpriteAnimationComponent
with HasGameRef {
_SpaceshipSaucerSpriteAnimationComponent()
: super(
anchor: Anchor.center,
priority: RenderPriority.spaceshipSaucer,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = gameRef.images.fromCache(
Assets.images.android.spaceship.saucer.keyName,
);
const amountPerRow = 5;
const amountPerColumn = 3;
final textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
size = textureSize / 10;
animation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: amountPerRow * amountPerColumn,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: textureSize,
),
);
}
}
// TODO(allisonryan0002): add pulsing behavior.
class _LightBeamSpriteComponent extends SpriteComponent with HasGameRef {
_LightBeamSpriteComponent()
: super(
anchor: Anchor.center,
priority: RenderPriority.spaceshipLightBeam,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.android.spaceship.lightBeam.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}
class _AndroidHead extends BodyComponent with InitialPosition, Layered {
_AndroidHead()
: super(
priority: RenderPriority.androidHead,
children: [_AndroidHeadSpriteAnimationComponent()],
renderBody: false,
) {
layer = Layer.spaceship;
}
@override
Body createBody() {
final shape = EllipseShape(
center: Vector2.zero(),
majorRadius: 3.1,
minorRadius: 2,
)..rotate(1.4);
// TODO(allisonryan0002): use bumping behavior.
final fixtureDef = FixtureDef(
shape,
restitution: 0.1,
);
final bodyDef = BodyDef(position: initialPosition);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
class _AndroidHeadSpriteAnimationComponent extends SpriteAnimationComponent
with HasGameRef {
_AndroidHeadSpriteAnimationComponent()
: super(
anchor: Anchor.center,
position: Vector2(-0.24, -2.6),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = gameRef.images.fromCache(
Assets.images.android.spaceship.animatronic.keyName,
);
const amountPerRow = 18;
const amountPerColumn = 4;
final textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
size = textureSize / 10;
animation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: amountPerRow * amountPerColumn,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: textureSize,
),
);
}
}
class _SpaceshipHole extends LayerSensor {
_SpaceshipHole({required Layer outsideLayer, required int outsidePriority})
: super(
insideLayer: Layer.spaceship,
outsideLayer: outsideLayer,
orientation: LayerEntranceOrientation.down,
insidePriority: RenderPriority.ballOnSpaceship,
outsidePriority: outsidePriority,
) {
layer = Layer.spaceship;
}
@override
Shape get shape {
return ArcShape(
center: Vector2(0, -3.2),
arcRadius: 5,
angle: 1,
rotation: -2,
);
}
}

@ -115,7 +115,7 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
math.pow(defaultGravity, 2) - math.pow(positionalXForce, 2),
);
body.gravityOverride = Vector2(positionalXForce, positionalYForce);
body.gravityOverride = Vector2(-positionalXForce, positionalYForce);
}
}

@ -0,0 +1,26 @@
// ignore_for_file: public_member_api_docs
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
class BoardBackgroundSpriteComponent extends SpriteComponent with HasGameRef {
BoardBackgroundSpriteComponent()
: super(
anchor: Anchor.center,
priority: RenderPriority.boardBackground,
position: Vector2(0, -1),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.boardBackground.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}

@ -1,7 +1,9 @@
export 'android_bumper/android_bumper.dart';
export 'android_spaceship.dart';
export 'backboard/backboard.dart';
export 'ball.dart';
export 'baseboard.dart';
export 'board_background_sprite_component.dart';
export 'board_dimensions.dart';
export 'board_side.dart';
export 'boundaries.dart';
@ -27,7 +29,6 @@ export 'score_text.dart';
export 'shapes/shapes.dart';
export 'signpost.dart';
export 'slingshot.dart';
export 'spaceship.dart';
export 'spaceship_rail.dart';
export 'spaceship_ramp.dart';
export 'sparky_animatronic.dart';

@ -20,7 +20,7 @@ abstract class RenderPriority {
static const int ballOnSpaceshipRamp =
_above + spaceshipRampBackgroundRailing;
/// Render priority for the [Ball] while it's on the [Spaceship].
/// Render priority for the [Ball] while it's on the [AndroidSpaceship].
static const int ballOnSpaceship = _above + spaceshipSaucer;
/// Render priority for the [Ball] while it's on the [SpaceshipRail].
@ -33,13 +33,13 @@ abstract class RenderPriority {
// TODO(allisonryan0002): fix this magic priority. Could bump all priorities
// so there are no negatives.
static const int background = 3 * _below + _base;
static const int boardBackground = 3 * _below + _base;
// Boundaries
static const int bottomBoundary = _above + dinoBottomWall;
static const int outerBoundary = _above + background;
static const int outerBoundary = _above + boardBackground;
static const int outerBottomBoundary = _above + rocket;
@ -57,7 +57,7 @@ abstract class RenderPriority {
static const int rocket = _below + bottomBoundary;
// Dino Land
// Dino Desert
static const int dinoTopWall = _above + ballOnBoard;
@ -71,12 +71,14 @@ abstract class RenderPriority {
static const int flutterForest = _above + launchRampForegroundRailing;
// Sparky Fire Zone
// Sparky Scorch
static const int computerBase = _below + ballOnBoard;
static const int computerTop = _above + ballOnBoard;
static const int computerGlow = _above + ballOnBoard;
static const int sparkyAnimatronic = _above + spaceshipRampForegroundRailing;
static const int sparkyBumper = _above + ballOnBoard;
@ -91,7 +93,7 @@ abstract class RenderPriority {
static const int spaceshipSaucer = _above + ballOnSpaceshipRail;
static const int spaceshipSaucerWall = _above + spaceshipSaucer;
static const int spaceshipLightBeam = _below + spaceshipSaucer;
static const int androidHead = _above + spaceshipSaucer;

@ -1,246 +0,0 @@
import 'dart:async';
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/gen/assets.gen.dart';
import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship}
/// A [Blueprint] which creates the spaceship feature.
/// {@endtemplate}
class Spaceship extends Blueprint {
/// {@macro spaceship}
Spaceship({required Vector2 position})
: super(
components: [
SpaceshipSaucer()..initialPosition = position,
_SpaceshipEntrance()..initialPosition = position,
AndroidHead()..initialPosition = position,
_SpaceshipHole(
outsideLayer: Layer.spaceshipExitRail,
outsidePriority: RenderPriority.ballOnSpaceshipRail,
)..initialPosition = position - Vector2(5.2, -4.8),
_SpaceshipHole(
outsideLayer: Layer.board,
outsidePriority: RenderPriority.ballOnBoard,
)..initialPosition = position - Vector2(-7.2, -0.8),
SpaceshipWall()..initialPosition = position,
],
);
/// Total size of the spaceship.
static final size = Vector2(25, 19);
}
/// {@template spaceship_saucer}
/// A [BodyComponent] for the base, or the saucer of the spaceship
/// {@endtemplate}
class SpaceshipSaucer extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_saucer}
SpaceshipSaucer()
: super(
priority: RenderPriority.spaceshipSaucer,
renderBody: false,
children: [
_SpaceshipSaucerSpriteComponent(),
],
) {
layer = Layer.spaceship;
}
@override
Body createBody() {
final shape = CircleShape()..radius = 3;
final fixtureDef = FixtureDef(
shape,
isSensor: true,
);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
class _SpaceshipSaucerSpriteComponent extends SpriteComponent with HasGameRef {
_SpaceshipSaucerSpriteComponent()
: super(
anchor: Anchor.center,
// TODO(alestiago): Refactor to use sprite orignial size instead.
size: Spaceship.size,
);
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Use cached sprite.
sprite = await gameRef.loadSprite(
Assets.images.spaceship.saucer.keyName,
);
}
}
/// {@template spaceship_bridge}
/// A [BodyComponent] that provides both the collision and the rotation
/// animation for the bridge.
/// {@endtemplate}
class AndroidHead extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_bridge}
AndroidHead()
: super(
priority: RenderPriority.androidHead,
children: [_AndroidHeadSpriteAnimation()],
renderBody: false,
) {
layer = Layer.spaceship;
}
@override
Body createBody() {
final circleShape = CircleShape()..radius = 2;
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
);
return world.createBody(bodyDef)
..createFixture(
FixtureDef(circleShape)..restitution = 0.4,
);
}
}
class _AndroidHeadSpriteAnimation extends SpriteAnimationComponent
with HasGameRef {
@override
Future<void> onLoad() async {
await super.onLoad();
final image = await gameRef.images.load(
Assets.images.spaceship.bridge.keyName,
);
size = Vector2(8.2, 10);
position = Vector2(0, -2);
anchor = Anchor.center;
final data = SpriteAnimationData.sequenced(
amount: 72,
amountPerRow: 24,
stepTime: 0.05,
textureSize: size * 10,
);
animation = SpriteAnimation.fromFrameData(image, data);
}
}
class _SpaceshipEntrance extends LayerSensor {
_SpaceshipEntrance()
: super(
insideLayer: Layer.spaceship,
orientation: LayerEntranceOrientation.up,
insidePriority: RenderPriority.ballOnSpaceship,
) {
layer = Layer.spaceship;
}
@override
Shape get shape {
final radius = Spaceship.size.y / 2;
return PolygonShape()
..setAsEdge(
Vector2(
radius * cos(20 * pi / 180),
radius * sin(20 * pi / 180),
)..rotate(90 * pi / 180),
Vector2(
radius * cos(340 * pi / 180),
radius * sin(340 * pi / 180),
)..rotate(90 * pi / 180),
);
}
}
class _SpaceshipHole extends LayerSensor {
_SpaceshipHole({required Layer outsideLayer, required int outsidePriority})
: super(
insideLayer: Layer.spaceship,
outsideLayer: outsideLayer,
orientation: LayerEntranceOrientation.down,
insidePriority: RenderPriority.ballOnSpaceship,
outsidePriority: outsidePriority,
) {
layer = Layer.spaceship;
}
@override
Shape get shape {
return ArcShape(
center: Vector2(0, -3.2),
arcRadius: 5,
angle: 1,
rotation: -2,
);
}
}
/// {@template spaceship_wall_shape}
/// The [ChainShape] that defines the shape of the [SpaceshipWall].
/// {@endtemplate}
class _SpaceshipWallShape extends ChainShape {
/// {@macro spaceship_wall_shape}
_SpaceshipWallShape() {
final minorRadius = (Spaceship.size.y - 2) / 2;
final majorRadius = (Spaceship.size.x - 2) / 2;
createChain(
[
// TODO(alestiago): Try converting this logic to radian.
for (var angle = 20; angle <= 340; angle++)
Vector2(
minorRadius * cos(angle * pi / 180),
majorRadius * sin(angle * pi / 180),
),
],
);
}
}
/// {@template spaceship_wall}
/// A [BodyComponent] that provides the collision for the wall
/// surrounding the spaceship.
///
/// It has a small opening to allow the [Ball] to get inside the spaceship
/// saucer.
///
/// It also contains the [SpriteComponent] for the lower wall
/// {@endtemplate}
class SpaceshipWall extends BodyComponent with InitialPosition, Layered {
/// {@macro spaceship_wall}
SpaceshipWall()
: super(
priority: RenderPriority.spaceshipSaucerWall,
renderBody: false,
) {
layer = Layer.spaceship;
}
@override
Body createBody() {
final shape = _SpaceshipWallShape();
final fixtureDef = FixtureDef(shape);
final bodyDef = BodyDef(
position: initialPosition,
userData: this,
angle: -1.7,
);
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}

@ -6,7 +6,7 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_rail}
/// A [Blueprint] for the rail exiting the [Spaceship].
/// A [Blueprint] for the rail exiting the [AndroidSpaceship].
/// {@endtemplate}
class SpaceshipRail extends Blueprint {
/// {@macro spaceship_rail}
@ -116,7 +116,7 @@ class _SpaceshipRailSpriteComponent extends SpriteComponent with HasGameRef {
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.spaceship.rail.main.keyName,
Assets.images.android.rail.main.keyName,
),
);
this.sprite = sprite;
@ -139,7 +139,7 @@ class _SpaceshipRailExitSpriteComponent extends SpriteComponent
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.spaceship.rail.exit.keyName,
Assets.images.android.rail.exit.keyName,
),
);
this.sprite = sprite;

@ -8,7 +8,7 @@ import 'package:pinball_components/pinball_components.dart' hide Assets;
import 'package:pinball_flame/pinball_flame.dart';
/// {@template spaceship_ramp}
/// A [Blueprint] which creates the ramp leading into the [Spaceship].
/// A [Blueprint] which creates the ramp leading into the [AndroidSpaceship].
/// {@endtemplate}
class SpaceshipRamp extends Blueprint {
/// {@macro spaceship_ramp}
@ -73,17 +73,17 @@ extension on SpaceshipRampArrowSpriteState {
String get path {
switch (this) {
case SpaceshipRampArrowSpriteState.inactive:
return Assets.images.spaceship.ramp.arrow.inactive.keyName;
return Assets.images.android.ramp.arrow.inactive.keyName;
case SpaceshipRampArrowSpriteState.active1:
return Assets.images.spaceship.ramp.arrow.active1.keyName;
return Assets.images.android.ramp.arrow.active1.keyName;
case SpaceshipRampArrowSpriteState.active2:
return Assets.images.spaceship.ramp.arrow.active2.keyName;
return Assets.images.android.ramp.arrow.active2.keyName;
case SpaceshipRampArrowSpriteState.active3:
return Assets.images.spaceship.ramp.arrow.active3.keyName;
return Assets.images.android.ramp.arrow.active3.keyName;
case SpaceshipRampArrowSpriteState.active4:
return Assets.images.spaceship.ramp.arrow.active4.keyName;
return Assets.images.android.ramp.arrow.active4.keyName;
case SpaceshipRampArrowSpriteState.active5:
return Assets.images.spaceship.ramp.arrow.active5.keyName;
return Assets.images.android.ramp.arrow.active5.keyName;
}
}
@ -161,7 +161,7 @@ class _SpaceshipRampBackgroundRailingSpriteComponent extends SpriteComponent
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.spaceship.ramp.railingBackground.keyName,
Assets.images.android.ramp.railingBackground.keyName,
),
);
this.sprite = sprite;
@ -182,7 +182,7 @@ class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.spaceship.ramp.main.keyName,
Assets.images.android.ramp.main.keyName,
),
);
this.sprite = sprite;
@ -234,7 +234,7 @@ class _SpaceshipRampBoardOpeningSpriteComponent extends SpriteComponent
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.spaceship.ramp.boardOpening.keyName,
Assets.images.android.ramp.boardOpening.keyName,
),
);
this.sprite = sprite;
@ -304,7 +304,7 @@ class _SpaceshipRampForegroundRailingSpriteComponent extends SpriteComponent
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.spaceship.ramp.railingForeground.keyName,
Assets.images.android.ramp.railingForeground.keyName,
),
);
this.sprite = sprite;

@ -3,19 +3,19 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_bumper_blinking_behavior}
/// Makes a [SparkyBumper] blink back to [SparkyBumperState.active] when
/// [SparkyBumperState.inactive].
/// Makes a [SparkyBumper] blink back to [SparkyBumperState.lit] when
/// [SparkyBumperState.dimmed].
/// {@endtemplate}
class SparkyBumperBlinkingBehavior extends TimerComponent
with ParentIsA<SparkyBumper> {
/// {@macro sparky_bumper_sprite_behavior}
/// {@macro sparky_bumper_blinking_behavior}
SparkyBumperBlinkingBehavior() : super(period: 0.05);
void _onNewState(SparkyBumperState state) {
switch (state) {
case SparkyBumperState.active:
case SparkyBumperState.lit:
break;
case SparkyBumperState.inactive:
case SparkyBumperState.dimmed:
timer
..reset()
..start();

@ -5,13 +5,13 @@ import 'package:bloc/bloc.dart';
part 'sparky_bumper_state.dart';
class SparkyBumperCubit extends Cubit<SparkyBumperState> {
SparkyBumperCubit() : super(SparkyBumperState.active);
SparkyBumperCubit() : super(SparkyBumperState.lit);
void onBallContacted() {
emit(SparkyBumperState.inactive);
emit(SparkyBumperState.dimmed);
}
void onBlinked() {
emit(SparkyBumperState.active);
emit(SparkyBumperState.lit);
}
}

@ -1,10 +1,8 @@
// ignore_for_file: public_member_api_docs
part of 'sparky_bumper_cubit.dart';
/// Indicates the [SparkyBumperCubit]'s current state.
enum SparkyBumperState {
/// A lit up bumper.
active,
/// A dimmed bumper.
inactive,
lit,
dimmed,
}

@ -17,8 +17,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
SparkyBumper._({
required double majorRadius,
required double minorRadius,
required String onAssetPath,
required String offAssetPath,
required String litAssetPath,
required String dimmedAssetPath,
required Vector2 spritePosition,
required this.bloc,
Iterable<Component>? children,
@ -31,8 +31,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
SparkyBumperBallContactBehavior(),
SparkyBumperBlinkingBehavior(),
_SparkyBumperSpriteGroupComponent(
onAssetPath: onAssetPath,
offAssetPath: offAssetPath,
litAssetPath: litAssetPath,
dimmedAssetPath: dimmedAssetPath,
position: spritePosition,
state: bloc.state,
),
@ -46,8 +46,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
}) : this._(
majorRadius: 2.9,
minorRadius: 2.1,
onAssetPath: Assets.images.sparky.bumper.a.active.keyName,
offAssetPath: Assets.images.sparky.bumper.a.inactive.keyName,
litAssetPath: Assets.images.sparky.bumper.a.lit.keyName,
dimmedAssetPath: Assets.images.sparky.bumper.a.dimmed.keyName,
spritePosition: Vector2(0, -0.25),
bloc: SparkyBumperCubit(),
children: children,
@ -59,8 +59,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
}) : this._(
majorRadius: 2.85,
minorRadius: 2,
onAssetPath: Assets.images.sparky.bumper.b.active.keyName,
offAssetPath: Assets.images.sparky.bumper.b.inactive.keyName,
litAssetPath: Assets.images.sparky.bumper.b.lit.keyName,
dimmedAssetPath: Assets.images.sparky.bumper.b.dimmed.keyName,
spritePosition: Vector2(0, -0.35),
bloc: SparkyBumperCubit(),
children: children,
@ -72,8 +72,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
}) : this._(
majorRadius: 3,
minorRadius: 2.2,
onAssetPath: Assets.images.sparky.bumper.c.active.keyName,
offAssetPath: Assets.images.sparky.bumper.c.inactive.keyName,
litAssetPath: Assets.images.sparky.bumper.c.lit.keyName,
dimmedAssetPath: Assets.images.sparky.bumper.c.dimmed.keyName,
spritePosition: Vector2(0, -0.4),
bloc: SparkyBumperCubit(),
children: children,
@ -127,20 +127,20 @@ class _SparkyBumperSpriteGroupComponent
extends SpriteGroupComponent<SparkyBumperState>
with HasGameRef, ParentIsA<SparkyBumper> {
_SparkyBumperSpriteGroupComponent({
required String onAssetPath,
required String offAssetPath,
required String litAssetPath,
required String dimmedAssetPath,
required Vector2 position,
required SparkyBumperState state,
}) : _onAssetPath = onAssetPath,
_offAssetPath = offAssetPath,
}) : _litAssetPath = litAssetPath,
_dimmedAssetPath = dimmedAssetPath,
super(
anchor: Anchor.center,
position: position,
current: state,
);
final String _onAssetPath;
final String _offAssetPath;
final String _litAssetPath;
final String _dimmedAssetPath;
@override
Future<void> onLoad() async {
@ -151,11 +151,11 @@ class _SparkyBumperSpriteGroupComponent
parent.bloc.stream.listen((state) => current = state);
final sprites = {
SparkyBumperState.active: Sprite(
gameRef.images.fromCache(_onAssetPath),
SparkyBumperState.lit: Sprite(
gameRef.images.fromCache(_litAssetPath),
),
SparkyBumperState.inactive: Sprite(
gameRef.images.fromCache(_offAssetPath),
SparkyBumperState.dimmed: Sprite(
gameRef.images.fromCache(_dimmedAssetPath),
),
};
this.sprites = sprites;

@ -15,6 +15,7 @@ class SparkyComputer extends Blueprint {
components: [
_ComputerBase(),
_ComputerTopSpriteComponent(),
_ComputerGlowSpriteComponent(),
],
);
}
@ -65,15 +66,17 @@ class _ComputerBaseSpriteComponent extends SpriteComponent with HasGameRef {
_ComputerBaseSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-11.95, -48.35),
position: Vector2(-12.1, -48.15),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.sparky.computer.base.keyName,
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.sparky.computer.base.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
@ -84,7 +87,7 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef {
_ComputerTopSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-12.45, -49.75),
position: Vector2(-12.52, -49.37),
priority: RenderPriority.computerTop,
);
@ -92,8 +95,32 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef {
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.sparky.computer.top.keyName,
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.sparky.computer.top.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}
class _ComputerGlowSpriteComponent extends SpriteComponent with HasGameRef {
_ComputerGlowSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(7.4, 10),
priority: RenderPriority.computerGlow,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.sparky.computer.glow.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;

@ -57,16 +57,16 @@ flutter:
- assets/images/dash/bumper/a/
- assets/images/dash/bumper/b/
- assets/images/dash/bumper/main/
- assets/images/spaceship/
- assets/images/spaceship/rail/
- assets/images/spaceship/ramp/
- assets/images/spaceship/ramp/arrow/
- assets/images/android/spaceship/
- assets/images/android/rail/
- assets/images/android/ramp/
- assets/images/android/ramp/arrow/
- assets/images/android/bumper/a/
- assets/images/android/bumper/b/
- assets/images/android/bumper/cow/
- assets/images/kicker/
- assets/images/plunger/
- assets/images/slingshot/
- assets/images/android_bumper/a/
- assets/images/android_bumper/b/
- assets/images/android_bumper/cow/
- assets/images/sparky/
- assets/images/sparky/computer/
- assets/images/sparky/bumper/a/

@ -19,7 +19,7 @@ void main() {
addBottomGroupStories(dashbook);
addPlungerStories(dashbook);
addSlingshotStories(dashbook);
addSparkyBumperStories(dashbook);
addSparkyScorchStories(dashbook);
addAndroidAcresStories(dashbook);
addBoundariesStories(dashbook);
addGoogleWordStories(dashbook);

@ -9,8 +9,8 @@ class AndroidBumperAGame extends BallGame {
: super(
color: const Color(0xFF0000FF),
imagesFileNames: [
Assets.images.androidBumper.a.lit.keyName,
Assets.images.androidBumper.a.dimmed.keyName,
Assets.images.android.bumper.a.lit.keyName,
Assets.images.android.bumper.a.dimmed.keyName,
],
);

@ -9,8 +9,8 @@ class AndroidBumperBGame extends BallGame {
: super(
color: const Color(0xFF0000FF),
imagesFileNames: [
Assets.images.androidBumper.b.lit.keyName,
Assets.images.androidBumper.b.dimmed.keyName,
Assets.images.android.bumper.b.lit.keyName,
Assets.images.android.bumper.b.dimmed.keyName,
],
);

@ -8,8 +8,8 @@ class AndroidBumperCowGame extends BallGame {
AndroidBumperCowGame()
: super(
imagesFileNames: [
Assets.images.androidBumper.cow.lit.keyName,
Assets.images.androidBumper.cow.dimmed.keyName,
Assets.images.android.bumper.cow.lit.keyName,
Assets.images.android.bumper.cow.dimmed.keyName,
],
);

@ -0,0 +1,38 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class AndroidSpaceshipGame extends BallGame {
AndroidSpaceshipGame()
: super(
ballPriority: RenderPriority.ballOnSpaceship,
ballLayer: Layer.spaceship,
imagesFileNames: [
Assets.images.android.spaceship.saucer.keyName,
Assets.images.android.spaceship.animatronic.keyName,
Assets.images.android.spaceship.lightBeam.keyName,
],
);
static const description = '''
Shows how the AndroidSpaceship is rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a Ball into the game.
''';
@override
Future<void> onLoad() async {
await super.onLoad();
camera.followVector2(Vector2.zero());
await addFromBlueprint(
AndroidSpaceship(position: Vector2.zero()),
);
await traceAllBodies();
}
}

@ -1,35 +0,0 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/common/common.dart';
class SpaceshipGame extends AssetsGame with TapDetector {
static const description = '''
Shows how a Spaceship works.
- Tap anywhere on the screen to spawn a Ball into the game.
''';
@override
Future<void> onLoad() async {
await super.onLoad();
camera.followVector2(Vector2.zero());
await addFromBlueprint(
Spaceship(position: Vector2.zero()),
);
await ready();
}
@override
void onTapUp(TapUpInfo info) {
add(
Ball(baseColor: Colors.blue)
..initialPosition = info.eventPosition.game
..layer = Layer.spaceshipEntranceRamp,
);
}
}

@ -13,8 +13,8 @@ class SpaceshipRailGame extends BallGame {
ballPriority: RenderPriority.ballOnSpaceshipRail,
ballLayer: Layer.spaceshipExitRail,
imagesFileNames: [
Assets.images.spaceship.rail.main.keyName,
Assets.images.spaceship.rail.exit.keyName,
Assets.images.android.rail.main.keyName,
Assets.images.android.rail.exit.keyName,
],
);

@ -14,16 +14,16 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents {
ballPriority: RenderPriority.ballOnSpaceshipRamp,
ballLayer: Layer.spaceshipEntranceRamp,
imagesFileNames: [
Assets.images.spaceship.ramp.railingBackground.keyName,
Assets.images.spaceship.ramp.main.keyName,
Assets.images.spaceship.ramp.boardOpening.keyName,
Assets.images.spaceship.ramp.railingForeground.keyName,
Assets.images.spaceship.ramp.arrow.inactive.keyName,
Assets.images.spaceship.ramp.arrow.active1.keyName,
Assets.images.spaceship.ramp.arrow.active2.keyName,
Assets.images.spaceship.ramp.arrow.active3.keyName,
Assets.images.spaceship.ramp.arrow.active4.keyName,
Assets.images.spaceship.ramp.arrow.active5.keyName,
Assets.images.android.ramp.railingBackground.keyName,
Assets.images.android.ramp.main.keyName,
Assets.images.android.ramp.boardOpening.keyName,
Assets.images.android.ramp.railingForeground.keyName,
Assets.images.android.ramp.arrow.inactive.keyName,
Assets.images.android.ramp.arrow.active1.keyName,
Assets.images.android.ramp.arrow.active2.keyName,
Assets.images.android.ramp.arrow.active3.keyName,
Assets.images.android.ramp.arrow.active4.keyName,
Assets.images.android.ramp.arrow.active5.keyName,
],
);

@ -3,7 +3,7 @@ import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/android_acres/android_bumper_a_game.dart';
import 'package:sandbox/stories/android_acres/android_bumper_b_game.dart';
import 'package:sandbox/stories/android_acres/android_bumper_cow_game.dart';
import 'package:sandbox/stories/android_acres/spaceship_game.dart';
import 'package:sandbox/stories/android_acres/android_spaceship_game.dart';
import 'package:sandbox/stories/android_acres/spaceship_rail_game.dart';
import 'package:sandbox/stories/android_acres/spaceship_ramp_game.dart';
@ -25,9 +25,9 @@ void addAndroidAcresStories(Dashbook dashbook) {
gameBuilder: (_) => AndroidBumperCowGame(),
)
..addGame(
title: 'Spaceship',
description: SpaceshipGame.description,
gameBuilder: (_) => SpaceshipGame(),
title: 'Android Spaceship',
description: AndroidSpaceshipGame.description,
gameBuilder: (_) => AndroidSpaceshipGame(),
)
..addGame(
title: 'Spaceship Rail',

@ -15,7 +15,7 @@ class LaunchRampGame extends BallGame {
);
static const description = '''
Shows how LaunchRamp are rendered.
Shows how the LaunchRamp is rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
@ -26,7 +26,7 @@ class LaunchRampGame extends BallGame {
await super.onLoad();
camera
..followVector2(Vector2(0, 0))
..followVector2(Vector2.zero())
..zoom = 7.5;
await addFromBlueprint(LaunchRamp());
await ready();

@ -1,11 +0,0 @@
import 'package:dashbook/dashbook.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/sparky_bumper/sparky_bumper_game.dart';
void addSparkyBumperStories(Dashbook dashbook) {
dashbook.storiesOf('Sparky Bumpers').addGame(
title: 'Traced',
description: SparkyBumperGame.description,
gameBuilder: (_) => SparkyBumperGame(),
);
}

@ -9,6 +9,7 @@ class SparkyBumperGame extends BallGame {
Shows how a SparkyBumper is rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
''';
@override
@ -16,12 +17,12 @@ class SparkyBumperGame extends BallGame {
await super.onLoad();
await images.loadAll([
Assets.images.sparky.bumper.a.active.keyName,
Assets.images.sparky.bumper.a.inactive.keyName,
Assets.images.sparky.bumper.b.active.keyName,
Assets.images.sparky.bumper.b.inactive.keyName,
Assets.images.sparky.bumper.c.active.keyName,
Assets.images.sparky.bumper.c.inactive.keyName,
Assets.images.sparky.bumper.a.lit.keyName,
Assets.images.sparky.bumper.a.dimmed.keyName,
Assets.images.sparky.bumper.b.lit.keyName,
Assets.images.sparky.bumper.b.dimmed.keyName,
Assets.images.sparky.bumper.c.lit.keyName,
Assets.images.sparky.bumper.c.dimmed.keyName,
]);
final center = screenToWorld(camera.viewport.canvasSize! / 2);

@ -0,0 +1,31 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SparkyComputerGame extends BallGame {
static const description = '''
Shows how the SparkyComputer is rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
''';
@override
Future<void> onLoad() async {
await super.onLoad();
await images.loadAll([
Assets.images.sparky.computer.base.keyName,
Assets.images.sparky.computer.top.keyName,
Assets.images.sparky.computer.glow.keyName,
]);
camera.followVector2(Vector2(-10, -40));
await addFromBlueprint(SparkyComputer());
await ready();
await traceAllBodies();
}
}

@ -0,0 +1,18 @@
import 'package:dashbook/dashbook.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/sparky_scorch/sparky_bumper_game.dart';
import 'package:sandbox/stories/sparky_scorch/sparky_computer_game.dart';
void addSparkyScorchStories(Dashbook dashbook) {
dashbook.storiesOf('Sparky Scorch')
..addGame(
title: 'Sparky Computer',
description: SparkyComputerGame.description,
gameBuilder: (_) => SparkyComputerGame(),
)
..addGame(
title: 'Sparky Bumper',
description: SparkyBumperGame.description,
gameBuilder: (_) => SparkyBumperGame(),
);
}

@ -14,4 +14,4 @@ export 'multipliers/stories.dart';
export 'plunger/stories.dart';
export 'score_text/stories.dart';
export 'slingshot/stories.dart';
export 'sparky_bumper/stories.dart';
export 'sparky_scorch/stories.dart';

@ -13,12 +13,12 @@ import '../../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final assets = [
Assets.images.androidBumper.a.lit.keyName,
Assets.images.androidBumper.a.dimmed.keyName,
Assets.images.androidBumper.b.lit.keyName,
Assets.images.androidBumper.b.dimmed.keyName,
Assets.images.androidBumper.cow.lit.keyName,
Assets.images.androidBumper.cow.dimmed.keyName,
Assets.images.android.bumper.a.lit.keyName,
Assets.images.android.bumper.a.dimmed.keyName,
Assets.images.android.bumper.b.lit.keyName,
Assets.images.android.bumper.b.dimmed.keyName,
Assets.images.android.bumper.cow.lit.keyName,
Assets.images.android.bumper.cow.dimmed.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));

@ -0,0 +1,66 @@
// 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_flame/pinball_flame.dart';
import '../../helpers/helpers.dart';
void main() {
group('AndroidSpaceship', () {
group('Spaceship', () {
final assets = [
Assets.images.android.spaceship.saucer.keyName,
Assets.images.android.spaceship.animatronic.keyName,
Assets.images.android.spaceship.lightBeam.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
flameTester.test('loads correctly', (game) async {
await game.addFromBlueprint(AndroidSpaceship(position: Vector2.zero()));
await game.ready();
});
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
await game
.addFromBlueprint(AndroidSpaceship(position: Vector2.zero()));
game.camera.followVector2(Vector2.zero());
await game.ready();
await tester.pump();
},
verify: (game, tester) async {
final animationDuration = game
.descendants()
.whereType<SpriteAnimationComponent>()
.last
.animation!
.totalDuration();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/android_spaceship/start.png'),
);
game.update(animationDuration * 0.5);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/android_spaceship/middle.png'),
);
game.update(animationDuration * 0.5);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/android_spaceship/end.png'),
);
},
);
});
});
}

@ -0,0 +1,48 @@
// 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 '../../helpers/helpers.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final assets = [
Assets.images.boardBackground.keyName,
];
final flameTester = FlameTester(() => TestGame(assets));
group('BoardBackgroundSpriteComponent', () {
flameTester.test(
'loads correctly',
(game) async {
final boardBackground = BoardBackgroundSpriteComponent();
await game.ensureAdd(boardBackground);
expect(game.contains(boardBackground), isTrue);
},
);
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
final boardBackground = BoardBackgroundSpriteComponent();
await game.ensureAdd(boardBackground);
await tester.pump();
game.camera
..followVector2(Vector2.zero())
..zoom = 3.7;
},
verify: (game, tester) async {
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/board-background.png'),
);
},
);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 209 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save