diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 4ba63092..49f40d1f 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -1,5 +1,5 @@ // ignore_for_file: public_member_api_docs - +import 'dart:math' as math; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; @@ -9,19 +9,41 @@ part 'game_state.dart'; class GameBloc extends Bloc { GameBloc() : super(const GameState.initial()) { - on(_onBallLost); + on(_onRoundLost); on(_onScored); + on(_onIncreasedMultiplier); on(_onBonusActivated); on(_onSparkyTurboChargeActivated); } - void _onBallLost(BallLost event, Emitter emit) { - emit(state.copyWith(balls: state.balls - 1)); + void _onRoundLost(RoundLost event, Emitter emit) { + final score = state.score * state.multiplier; + final roundsLeft = math.max(state.rounds - 1, 0); + + emit( + state.copyWith( + score: score, + multiplier: 1, + rounds: roundsLeft, + ), + ); } void _onScored(Scored event, Emitter emit) { if (!state.isGameOver) { - emit(state.copyWith(score: state.score + event.points)); + emit( + state.copyWith(score: state.score + event.points), + ); + } + } + + void _onIncreasedMultiplier(MultiplierIncreased event, Emitter emit) { + if (!state.isGameOver) { + emit( + state.copyWith( + multiplier: math.min(state.multiplier + 1, 6), + ), + ); } } diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index bbb89028..c81ce526 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -7,12 +7,12 @@ abstract class GameEvent extends Equatable { const GameEvent(); } -/// {@template ball_lost_game_event} -/// Event added when a user drops a ball off the screen. +/// {@template round_lost_game_event} +/// Event added when a user drops all balls off the screen and loses a round. /// {@endtemplate} -class BallLost extends GameEvent { - /// {@macro ball_lost_game_event} - const BallLost(); +class RoundLost extends GameEvent { + /// {@macro round_lost_game_event} + const RoundLost(); @override List get props => []; @@ -48,3 +48,14 @@ class SparkyTurboChargeActivated extends GameEvent { @override List get props => []; } + +/// {@template multiplier_increased_game_event} +/// Added when a multiplier is gained. +/// {@endtemplate} +class MultiplierIncreased extends GameEvent { + /// {@macro multiplier_increased_game_event} + const MultiplierIncreased(); + + @override + List get props => []; +} diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 772bfea3..4ce9042d 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -27,34 +27,42 @@ class GameState extends Equatable { /// {@macro game_state} const GameState({ required this.score, - required this.balls, + required this.multiplier, + required this.rounds, required this.bonusHistory, }) : assert(score >= 0, "Score can't be negative"), - assert(balls >= 0, "Number of balls can't be negative"); + assert(multiplier > 0, 'Multiplier must be greater than zero'), + assert(rounds >= 0, "Number of rounds can't be negative"); const GameState.initial() : score = 0, - balls = 3, + multiplier = 1, + rounds = 3, bonusHistory = const []; /// The current score of the game. final int score; - /// The number of balls left in the game. + /// The current multiplier for the score. + final int multiplier; + + /// The number of rounds left in the game. /// - /// When the number of balls is 0, the game is over. - final int balls; + /// When the number of rounds is 0, the game is over. + final int rounds; /// Holds the history of all the [GameBonus]es earned by the player during a /// PinballGame. final List bonusHistory; /// Determines when the game is over. - bool get isGameOver => balls == 0; + bool get isGameOver => rounds == 0; GameState copyWith({ int? score, + int? multiplier, int? balls, + int? rounds, List? bonusHistory, }) { assert( @@ -64,7 +72,8 @@ class GameState extends Equatable { return GameState( score: score ?? this.score, - balls: balls ?? this.balls, + multiplier: multiplier ?? this.multiplier, + rounds: rounds ?? this.rounds, bonusHistory: bonusHistory ?? this.bonusHistory, ); } @@ -72,7 +81,8 @@ class GameState extends Equatable { @override List get props => [ score, - balls, + multiplier, + rounds, bonusHistory, ]; } diff --git a/lib/game/components/alien_zone.dart b/lib/game/components/android_acres.dart similarity index 57% rename from lib/game/components/alien_zone.dart rename to lib/game/components/android_acres.dart index dadc5ba4..752f68f9 100644 --- a/lib/game/components/alien_zone.dart +++ b/lib/game/components/android_acres.dart @@ -5,25 +5,30 @@ import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; -/// {@template alien_zone} -/// Area positioned below [Spaceship] where the [Ball] -/// can bounce off [AlienBumper]s. +/// {@template android_acres} +/// Area positioned on the left side of the board containing the [Spaceship], +/// [SpaceshipRamp], [SpaceshipRail], and [AndroidBumper]s. /// {@endtemplate} -class AlienZone extends Blueprint { - /// {@macro alien_zone} - AlienZone() +class AndroidAcres extends Blueprint { + /// {@macro android_acres} + AndroidAcres() : super( components: [ - AlienBumper.a( + AndroidBumper.a( children: [ ScoringBehavior(points: 20), ], )..initialPosition = Vector2(-32.52, -9.1), - AlienBumper.b( + AndroidBumper.b( children: [ ScoringBehavior(points: 20), ], )..initialPosition = Vector2(-22.89, -17.35), ], + blueprints: [ + SpaceshipRamp(), + Spaceship(position: Vector2(-26.5, -28.5)), + SpaceshipRail(), + ], ); } diff --git a/lib/game/components/board.dart b/lib/game/components/board.dart index 8ee4128f..666cec5b 100644 --- a/lib/game/components/board.dart +++ b/lib/game/components/board.dart @@ -18,17 +18,8 @@ class Board extends Component { final flutterForest = FlutterForest(); - // TODO(alestiago): adjust positioning to real design. - // TODO(alestiago): add dino in pinball game. - final dino = ChromeDino() - ..initialPosition = Vector2( - BoardDimensions.bounds.center.dx + 25, - BoardDimensions.bounds.center.dy - 10, - ); - await addAll([ bottomGroup, - dino, flutterForest, ]); } diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 321be988..37de1948 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -1,4 +1,4 @@ -export 'alien_zone.dart'; +export 'android_acres.dart'; export 'board.dart'; export 'camera_controller.dart'; export 'controlled_ball.dart'; diff --git a/lib/game/components/controlled_ball.dart b/lib/game/components/controlled_ball.dart index f36cfef2..e76aabe1 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -1,5 +1,4 @@ import 'package:flame/components.dart'; -import 'package:flame_forge2d/forge2d_game.dart'; import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -8,12 +7,12 @@ import 'package:pinball_theme/pinball_theme.dart'; /// {@template controlled_ball} /// A [Ball] with a [BallController] attached. +/// +/// When a [Ball] is lost, if there aren't more [Ball]s in play and the game is +/// not over, a new [Ball] will be spawned. /// {@endtemplate} class ControlledBall extends Ball with Controls { /// A [Ball] that launches from the [Plunger]. - /// - /// When a launched [Ball] is lost, it will decrease the [GameState.balls] - /// count, and a new [Ball] is spawned. ControlledBall.launch({ required CharacterTheme characterTheme, }) : super(baseColor: characterTheme.ballColor) { @@ -24,8 +23,6 @@ class ControlledBall extends Ball with Controls { /// {@template bonus_ball} /// {@macro controlled_ball} - /// - /// When a bonus [Ball] is lost, the [GameState.balls] doesn't change. /// {@endtemplate} ControlledBall.bonus({ required CharacterTheme characterTheme, @@ -36,7 +33,7 @@ class ControlledBall extends Ball with Controls { /// [Ball] used in [DebugPinballGame]. ControlledBall.debug() : super(baseColor: const Color(0xFFFF0000)) { - controller = DebugBallController(this); + controller = BallController(this); priority = RenderPriority.ballOnBoard; } } @@ -74,15 +71,9 @@ class BallController extends ComponentController @override void onRemove() { super.onRemove(); - gameRef.read().add(const BallLost()); + final noBallsLeft = gameRef.descendants().whereType().isEmpty; + if (noBallsLeft) { + gameRef.read().add(const RoundLost()); + } } } - -/// {@macro ball_controller} -class DebugBallController extends BallController { - /// {@macro ball_controller} - DebugBallController(Ball component) : super(component); - - @override - void onRemove() {} -} diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 9dc88562..ab4e9860 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -34,8 +34,10 @@ extension PinballGameAssetsX on PinballGame { images.load( components.Assets.images.launchRamp.backgroundRailing.keyName, ), - images.load(components.Assets.images.dino.dinoLandTop.keyName), - images.load(components.Assets.images.dino.dinoLandBottom.keyName), + images.load(components.Assets.images.dino.bottomWall.keyName), + images.load(components.Assets.images.dino.topWall.keyName), + images.load(components.Assets.images.dino.animatronic.head.keyName), + images.load(components.Assets.images.dino.animatronic.mouth.keyName), images.load(components.Assets.images.dash.animatronic.keyName), images.load(components.Assets.images.dash.bumper.a.active.keyName), images.load(components.Assets.images.dash.bumper.a.inactive.keyName), @@ -76,13 +78,11 @@ extension PinballGameAssetsX on PinballGame { components.Assets.images.spaceship.ramp.arrow.active5.keyName, ), images.load(components.Assets.images.spaceship.rail.main.keyName), - images.load(components.Assets.images.spaceship.rail.foreground.keyName), - images.load(components.Assets.images.alienBumper.a.active.keyName), - images.load(components.Assets.images.alienBumper.a.inactive.keyName), - images.load(components.Assets.images.alienBumper.b.active.keyName), - images.load(components.Assets.images.alienBumper.b.inactive.keyName), - images.load(components.Assets.images.chromeDino.mouth.keyName), - images.load(components.Assets.images.chromeDino.head.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.sparky.computer.top.keyName), images.load(components.Assets.images.sparky.computer.base.keyName), images.load(components.Assets.images.sparky.animatronic.keyName), diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 1d483fe2..4b57f1dd 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -53,20 +53,11 @@ class PinballGame extends Forge2DGame final launcher = Launcher(); unawaited(addFromBlueprint(launcher)); unawaited(add(Board())); - await addFromBlueprint(AlienZone()); - await addFromBlueprint(SparkyFireZone()); + await addFromBlueprint(AndroidAcres()); unawaited(addFromBlueprint(Slingshots())); unawaited(addFromBlueprint(DinoWalls())); - unawaited(addFromBlueprint(SpaceshipRamp())); - unawaited( - addFromBlueprint( - Spaceship( - position: Vector2(-26.5, -28.5), - ), - ), - ); - unawaited(addFromBlueprint(SpaceshipRail())); + await add(ChromeDino()..initialPosition = Vector2(12.3, -6.9)); await add( GoogleWord( position: Vector2( @@ -82,7 +73,7 @@ class PinballGame extends Forge2DGame } class _GameBallsController extends ComponentController - with BlocComponent, HasGameRef { + with BlocComponent { _GameBallsController(PinballGame game) : super(game); late final Plunger _plunger; @@ -90,9 +81,9 @@ class _GameBallsController extends ComponentController @override bool listenWhen(GameState? previousState, GameState newState) { final noBallsLeft = component.descendants().whereType().isEmpty; - final canBallRespawn = newState.balls > 0; + final notGameOver = !newState.isGameOver; - return noBallsLeft && canBallRespawn; + return noBallsLeft && notGameOver; } @override @@ -109,7 +100,7 @@ class _GameBallsController extends ComponentController void _spawnBall() { final ball = ControlledBall.launch( - characterTheme: gameRef.characterTheme, + characterTheme: component.characterTheme, )..initialPosition = Vector2( _plunger.body.position.x, _plunger.body.position.y - Ball.size.y, @@ -170,20 +161,10 @@ class DebugPinballGame extends PinballGame with FPSCounter, TapDetector { class _DebugGameBallsController extends _GameBallsController { _DebugGameBallsController(PinballGame game) : super(game); - - @override - bool listenWhen(GameState? previousState, GameState newState) { - final noBallsLeft = component - .descendants() - .whereType() - .where((ball) => ball.controller is! DebugBallController) - .isEmpty; - final canBallRespawn = newState.balls > 0; - - return noBallsLeft && canBallRespawn; - } } +// TODO(wolfenrain): investigate this CI failure. +// coverage:ignore-start class _DebugInformation extends Component with HasGameRef { _DebugInformation() : super(priority: RenderPriority.debugInfo); @@ -215,3 +196,4 @@ class _DebugInformation extends Component with HasGameRef { _debugTextPaint.render(canvas, debugText, position); } } +// coverage:ignore-end diff --git a/lib/game/view/widgets/game_hud.dart b/lib/game/view/widgets/game_hud.dart index 3623e21f..9cfb2d67 100644 --- a/lib/game/view/widgets/game_hud.dart +++ b/lib/game/view/widgets/game_hud.dart @@ -7,7 +7,7 @@ import 'package:pinball/theme/app_colors.dart'; /// {@template game_hud} /// Overlay on the [PinballGame]. /// -/// Displays the current [GameState.score], [GameState.balls] and animates when +/// Displays the current [GameState.score], [GameState.rounds] and animates when /// the player gets a [GameBonus]. /// {@endtemplate} class GameHud extends StatefulWidget { diff --git a/lib/game/view/widgets/round_count_display.dart b/lib/game/view/widgets/round_count_display.dart index 98776764..30135cd2 100644 --- a/lib/game/view/widgets/round_count_display.dart +++ b/lib/game/view/widgets/round_count_display.dart @@ -14,9 +14,7 @@ class RoundCountDisplay extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = context.l10n; - // TODO(arturplaczek): refactor when GameState handle balls and rounds and - // select state.rounds property instead of state.ball - final balls = context.select((GameBloc bloc) => bloc.state.balls); + final rounds = context.select((GameBloc bloc) => bloc.state.rounds); return Row( children: [ @@ -29,9 +27,9 @@ class RoundCountDisplay extends StatelessWidget { const SizedBox(width: 8), Row( children: [ - RoundIndicator(isActive: balls >= 1), - RoundIndicator(isActive: balls >= 2), - RoundIndicator(isActive: balls >= 3), + RoundIndicator(isActive: rounds >= 1), + RoundIndicator(isActive: rounds >= 2), + RoundIndicator(isActive: rounds >= 3), ], ), ], diff --git a/packages/pinball_components/assets/images/alien_bumper/a/inactive.png b/packages/pinball_components/assets/images/android_bumper/a/dimmed.png similarity index 100% rename from packages/pinball_components/assets/images/alien_bumper/a/inactive.png rename to packages/pinball_components/assets/images/android_bumper/a/dimmed.png diff --git a/packages/pinball_components/assets/images/alien_bumper/a/active.png b/packages/pinball_components/assets/images/android_bumper/a/lit.png similarity index 100% rename from packages/pinball_components/assets/images/alien_bumper/a/active.png rename to packages/pinball_components/assets/images/android_bumper/a/lit.png diff --git a/packages/pinball_components/assets/images/alien_bumper/b/inactive.png b/packages/pinball_components/assets/images/android_bumper/b/dimmed.png similarity index 100% rename from packages/pinball_components/assets/images/alien_bumper/b/inactive.png rename to packages/pinball_components/assets/images/android_bumper/b/dimmed.png diff --git a/packages/pinball_components/assets/images/alien_bumper/b/active.png b/packages/pinball_components/assets/images/android_bumper/b/lit.png similarity index 100% rename from packages/pinball_components/assets/images/alien_bumper/b/active.png rename to packages/pinball_components/assets/images/android_bumper/b/lit.png diff --git a/packages/pinball_components/assets/images/boundary/outer.png b/packages/pinball_components/assets/images/boundary/outer.png index 3c06cb6c..1f3bab69 100644 Binary files a/packages/pinball_components/assets/images/boundary/outer.png and b/packages/pinball_components/assets/images/boundary/outer.png differ diff --git a/packages/pinball_components/assets/images/chrome_dino/head.png b/packages/pinball_components/assets/images/chrome_dino/head.png deleted file mode 100644 index 15be6fcd..00000000 Binary files a/packages/pinball_components/assets/images/chrome_dino/head.png and /dev/null differ diff --git a/packages/pinball_components/assets/images/chrome_dino/mouth.png b/packages/pinball_components/assets/images/chrome_dino/mouth.png deleted file mode 100644 index 3d9caeae..00000000 Binary files a/packages/pinball_components/assets/images/chrome_dino/mouth.png and /dev/null differ diff --git a/packages/pinball_components/assets/images/dino/animatronic/head.png b/packages/pinball_components/assets/images/dino/animatronic/head.png new file mode 100644 index 00000000..87332679 Binary files /dev/null and b/packages/pinball_components/assets/images/dino/animatronic/head.png differ diff --git a/packages/pinball_components/assets/images/dino/animatronic/mouth.png b/packages/pinball_components/assets/images/dino/animatronic/mouth.png new file mode 100644 index 00000000..4955bdf3 Binary files /dev/null and b/packages/pinball_components/assets/images/dino/animatronic/mouth.png differ diff --git a/packages/pinball_components/assets/images/dino/dino-land-bottom.png b/packages/pinball_components/assets/images/dino/bottom-wall.png similarity index 100% rename from packages/pinball_components/assets/images/dino/dino-land-bottom.png rename to packages/pinball_components/assets/images/dino/bottom-wall.png diff --git a/packages/pinball_components/assets/images/dino/dino-land-top.png b/packages/pinball_components/assets/images/dino/top-wall.png similarity index 100% rename from packages/pinball_components/assets/images/dino/dino-land-top.png rename to packages/pinball_components/assets/images/dino/top-wall.png diff --git a/packages/pinball_components/assets/images/spaceship/rail/exit.png b/packages/pinball_components/assets/images/spaceship/rail/exit.png new file mode 100644 index 00000000..80a819d0 Binary files /dev/null and b/packages/pinball_components/assets/images/spaceship/rail/exit.png differ diff --git a/packages/pinball_components/assets/images/spaceship/rail/foreground.png b/packages/pinball_components/assets/images/spaceship/rail/foreground.png deleted file mode 100644 index 4d11e865..00000000 Binary files a/packages/pinball_components/assets/images/spaceship/rail/foreground.png and /dev/null differ diff --git a/packages/pinball_components/assets/images/spaceship/rail/main.png b/packages/pinball_components/assets/images/spaceship/rail/main.png index 4b299c2c..9291c784 100644 Binary files a/packages/pinball_components/assets/images/spaceship/rail/main.png and b/packages/pinball_components/assets/images/spaceship/rail/main.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 4d4cda7d..1a272d31 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -10,14 +10,12 @@ import 'package:flutter/widgets.dart'; class $AssetsImagesGen { const $AssetsImagesGen(); - $AssetsImagesAlienBumperGen get alienBumper => - const $AssetsImagesAlienBumperGen(); + $AssetsImagesAndroidBumperGen get androidBumper => + const $AssetsImagesAndroidBumperGen(); $AssetsImagesBackboardGen get backboard => const $AssetsImagesBackboardGen(); $AssetsImagesBallGen get ball => const $AssetsImagesBallGen(); $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); $AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen(); - $AssetsImagesChromeDinoGen get chromeDino => - const $AssetsImagesChromeDinoGen(); $AssetsImagesDashGen get dash => const $AssetsImagesDashGen(); $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); $AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen(); @@ -33,11 +31,13 @@ class $AssetsImagesGen { $AssetsImagesSparkyGen get sparky => const $AssetsImagesSparkyGen(); } -class $AssetsImagesAlienBumperGen { - const $AssetsImagesAlienBumperGen(); +class $AssetsImagesAndroidBumperGen { + const $AssetsImagesAndroidBumperGen(); - $AssetsImagesAlienBumperAGen get a => const $AssetsImagesAlienBumperAGen(); - $AssetsImagesAlienBumperBGen get b => const $AssetsImagesAlienBumperBGen(); + $AssetsImagesAndroidBumperAGen get a => + const $AssetsImagesAndroidBumperAGen(); + $AssetsImagesAndroidBumperBGen get b => + const $AssetsImagesAndroidBumperBGen(); } class $AssetsImagesBackboardGen { @@ -95,18 +95,6 @@ class $AssetsImagesBoundaryGen { const AssetGenImage('assets/images/boundary/outer.png'); } -class $AssetsImagesChromeDinoGen { - const $AssetsImagesChromeDinoGen(); - - /// File path: assets/images/chrome_dino/head.png - AssetGenImage get head => - const AssetGenImage('assets/images/chrome_dino/head.png'); - - /// File path: assets/images/chrome_dino/mouth.png - AssetGenImage get mouth => - const AssetGenImage('assets/images/chrome_dino/mouth.png'); -} - class $AssetsImagesDashGen { const $AssetsImagesDashGen(); @@ -120,13 +108,16 @@ class $AssetsImagesDashGen { class $AssetsImagesDinoGen { const $AssetsImagesDinoGen(); - /// File path: assets/images/dino/dino-land-bottom.png - AssetGenImage get dinoLandBottom => - const AssetGenImage('assets/images/dino/dino-land-bottom.png'); + $AssetsImagesDinoAnimatronicGen get animatronic => + const $AssetsImagesDinoAnimatronicGen(); + + /// File path: assets/images/dino/bottom-wall.png + AssetGenImage get bottomWall => + const AssetGenImage('assets/images/dino/bottom-wall.png'); - /// File path: assets/images/dino/dino-land-top.png - AssetGenImage get dinoLandTop => - const AssetGenImage('assets/images/dino/dino-land-top.png'); + /// File path: assets/images/dino/top-wall.png + AssetGenImage get topWall => + const AssetGenImage('assets/images/dino/top-wall.png'); } class $AssetsImagesFlipperGen { @@ -269,28 +260,28 @@ class $AssetsImagesSparkyGen { const $AssetsImagesSparkyComputerGen(); } -class $AssetsImagesAlienBumperAGen { - const $AssetsImagesAlienBumperAGen(); +class $AssetsImagesAndroidBumperAGen { + const $AssetsImagesAndroidBumperAGen(); - /// File path: assets/images/alien_bumper/a/active.png - AssetGenImage get active => - const AssetGenImage('assets/images/alien_bumper/a/active.png'); + /// 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/alien_bumper/a/inactive.png - AssetGenImage get inactive => - const AssetGenImage('assets/images/alien_bumper/a/inactive.png'); + /// File path: assets/images/android_bumper/a/lit.png + AssetGenImage get lit => + const AssetGenImage('assets/images/android_bumper/a/lit.png'); } -class $AssetsImagesAlienBumperBGen { - const $AssetsImagesAlienBumperBGen(); +class $AssetsImagesAndroidBumperBGen { + const $AssetsImagesAndroidBumperBGen(); - /// File path: assets/images/alien_bumper/b/active.png - AssetGenImage get active => - const AssetGenImage('assets/images/alien_bumper/b/active.png'); + /// 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/alien_bumper/b/inactive.png - AssetGenImage get inactive => - const AssetGenImage('assets/images/alien_bumper/b/inactive.png'); + /// File path: assets/images/android_bumper/b/lit.png + AssetGenImage get lit => + const AssetGenImage('assets/images/android_bumper/b/lit.png'); } class $AssetsImagesDashBumperGen { @@ -302,12 +293,24 @@ class $AssetsImagesDashBumperGen { const $AssetsImagesDashBumperMainGen(); } +class $AssetsImagesDinoAnimatronicGen { + const $AssetsImagesDinoAnimatronicGen(); + + /// File path: assets/images/dino/animatronic/head.png + AssetGenImage get head => + const AssetGenImage('assets/images/dino/animatronic/head.png'); + + /// File path: assets/images/dino/animatronic/mouth.png + AssetGenImage get mouth => + const AssetGenImage('assets/images/dino/animatronic/mouth.png'); +} + class $AssetsImagesSpaceshipRailGen { const $AssetsImagesSpaceshipRailGen(); - /// File path: assets/images/spaceship/rail/foreground.png - AssetGenImage get foreground => - const AssetGenImage('assets/images/spaceship/rail/foreground.png'); + /// 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 => diff --git a/packages/pinball_components/lib/src/components/alien_bumper/behaviors/behaviors.dart b/packages/pinball_components/lib/src/components/alien_bumper/behaviors/behaviors.dart deleted file mode 100644 index 14762ea2..00000000 --- a/packages/pinball_components/lib/src/components/alien_bumper/behaviors/behaviors.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'alien_bumper_ball_contact_behavior.dart'; -export 'alien_bumper_blinking_behavior.dart'; diff --git a/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_cubit.dart b/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_cubit.dart deleted file mode 100644 index d4319a3b..00000000 --- a/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_cubit.dart +++ /dev/null @@ -1,17 +0,0 @@ -// ignore_for_file: public_member_api_docs - -import 'package:bloc/bloc.dart'; - -part 'alien_bumper_state.dart'; - -class AlienBumperCubit extends Cubit { - AlienBumperCubit() : super(AlienBumperState.active); - - void onBallContacted() { - emit(AlienBumperState.inactive); - } - - void onBlinked() { - emit(AlienBumperState.active); - } -} diff --git a/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_state.dart b/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_state.dart deleted file mode 100644 index cbec959e..00000000 --- a/packages/pinball_components/lib/src/components/alien_bumper/cubit/alien_bumper_state.dart +++ /dev/null @@ -1,10 +0,0 @@ -part of 'alien_bumper_cubit.dart'; - -/// Indicates the [AlienBumperCubit]'s current state. -enum AlienBumperState { - /// A lit up bumper. - active, - - /// A dimmed bumper. - inactive, -} diff --git a/packages/pinball_components/lib/src/components/alien_bumper/alien_bumper.dart b/packages/pinball_components/lib/src/components/android_bumper/android_bumper.dart similarity index 55% rename from packages/pinball_components/lib/src/components/alien_bumper/alien_bumper.dart rename to packages/pinball_components/lib/src/components/android_bumper/android_bumper.dart index abe39ae5..ad954975 100644 --- a/packages/pinball_components/lib/src/components/alien_bumper/alien_bumper.dart +++ b/packages/pinball_components/lib/src/components/android_bumper/android_bumper.dart @@ -4,71 +4,71 @@ import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; -import 'package:pinball_components/src/components/alien_bumper/behaviors/behaviors.dart'; +import 'package:pinball_components/src/components/android_bumper/behaviors/behaviors.dart'; import 'package:pinball_flame/pinball_flame.dart'; -export 'cubit/alien_bumper_cubit.dart'; +export 'cubit/android_bumper_cubit.dart'; -/// {@template alien_bumper} +/// {@template android_bumper} /// Bumper for area under the [Spaceship]. /// {@endtemplate} -class AlienBumper extends BodyComponent with InitialPosition { - /// {@macro alien_bumper} - AlienBumper._({ +class AndroidBumper extends BodyComponent with InitialPosition { + /// {@macro android_bumper} + AndroidBumper._({ required double majorRadius, required double minorRadius, - required String onAssetPath, - required String offAssetPath, + required String litAssetPath, + required String dimmedAssetPath, Iterable? children, required this.bloc, }) : _majorRadius = majorRadius, _minorRadius = minorRadius, super( - priority: RenderPriority.alienBumper, + priority: RenderPriority.androidBumper, renderBody: false, children: [ - AlienBumperBallContactBehavior(), - AlienBumperBlinkingBehavior(), - _AlienBumperSpriteGroupComponent( - offAssetPath: offAssetPath, - onAssetPath: onAssetPath, + AndroidBumperBallContactBehavior(), + AndroidBumperBlinkingBehavior(), + _AndroidBumperSpriteGroupComponent( + dimmedAssetPath: dimmedAssetPath, + litAssetPath: litAssetPath, state: bloc.state, ), ...?children, ], ); - /// {@macro alien_bumper} - AlienBumper.a({ + /// {@macro android_bumper} + AndroidBumper.a({ Iterable? children, }) : this._( majorRadius: 3.52, minorRadius: 2.97, - onAssetPath: Assets.images.alienBumper.a.active.keyName, - offAssetPath: Assets.images.alienBumper.a.inactive.keyName, - bloc: AlienBumperCubit(), + litAssetPath: Assets.images.androidBumper.a.lit.keyName, + dimmedAssetPath: Assets.images.androidBumper.a.dimmed.keyName, + bloc: AndroidBumperCubit(), children: children, ); - /// {@macro alien_bumper} - AlienBumper.b({ + /// {@macro android_bumper} + AndroidBumper.b({ Iterable? children, }) : this._( majorRadius: 3.19, minorRadius: 2.79, - onAssetPath: Assets.images.alienBumper.b.active.keyName, - offAssetPath: Assets.images.alienBumper.b.inactive.keyName, - bloc: AlienBumperCubit(), + litAssetPath: Assets.images.androidBumper.b.lit.keyName, + dimmedAssetPath: Assets.images.androidBumper.b.dimmed.keyName, + bloc: AndroidBumperCubit(), children: children, ); - /// Creates an [AlienBumper] without any children. + /// Creates an [AndroidBumper] without any children. /// - /// This can be used for testing [AlienBumper]'s behaviors in isolation. + /// This can be used for testing [AndroidBumper]'s behaviors in isolation. // TODO(alestiago): Refactor injecting bloc once the following is merged: // https://github.com/flame-engine/flame/pull/1538 @visibleForTesting - AlienBumper.test({ + AndroidBumper.test({ required this.bloc, }) : _majorRadius = 3.52, _minorRadius = 2.97; @@ -80,7 +80,7 @@ class AlienBumper extends BodyComponent with InitialPosition { // TODO(alestiago): Consider refactoring once the following is merged: // https://github.com/flame-engine/flame/pull/1538 // ignore: public_member_api_docs - final AlienBumperCubit bloc; + final AndroidBumperCubit bloc; @override void onRemove() { @@ -107,23 +107,23 @@ class AlienBumper extends BodyComponent with InitialPosition { } } -class _AlienBumperSpriteGroupComponent - extends SpriteGroupComponent - with HasGameRef, ParentIsA { - _AlienBumperSpriteGroupComponent({ - required String onAssetPath, - required String offAssetPath, - required AlienBumperState state, - }) : _onAssetPath = onAssetPath, - _offAssetPath = offAssetPath, +class _AndroidBumperSpriteGroupComponent + extends SpriteGroupComponent + with HasGameRef, ParentIsA { + _AndroidBumperSpriteGroupComponent({ + required String litAssetPath, + required String dimmedAssetPath, + required AndroidBumperState state, + }) : _litAssetPath = litAssetPath, + _dimmedAssetPath = dimmedAssetPath, super( anchor: Anchor.center, position: Vector2(0, -0.1), current: state, ); - final String _onAssetPath; - final String _offAssetPath; + final String _litAssetPath; + final String _dimmedAssetPath; @override Future onLoad() async { @@ -131,11 +131,11 @@ class _AlienBumperSpriteGroupComponent parent.bloc.stream.listen((state) => current = state); final sprites = { - AlienBumperState.active: Sprite( - gameRef.images.fromCache(_onAssetPath), + AndroidBumperState.lit: Sprite( + gameRef.images.fromCache(_litAssetPath), ), - AlienBumperState.inactive: - Sprite(gameRef.images.fromCache(_offAssetPath)), + AndroidBumperState.dimmed: + Sprite(gameRef.images.fromCache(_dimmedAssetPath)), }; this.sprites = sprites; size = sprites[current]!.originalSize / 10; diff --git a/packages/pinball_components/lib/src/components/alien_bumper/behaviors/alien_bumper_ball_contact_behavior.dart b/packages/pinball_components/lib/src/components/android_bumper/behaviors/android_bumper_ball_contact_behavior.dart similarity index 82% rename from packages/pinball_components/lib/src/components/alien_bumper/behaviors/alien_bumper_ball_contact_behavior.dart rename to packages/pinball_components/lib/src/components/android_bumper/behaviors/android_bumper_ball_contact_behavior.dart index effa3221..d28aa39c 100644 --- a/packages/pinball_components/lib/src/components/alien_bumper/behaviors/alien_bumper_ball_contact_behavior.dart +++ b/packages/pinball_components/lib/src/components/android_bumper/behaviors/android_bumper_ball_contact_behavior.dart @@ -4,7 +4,7 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; -class AlienBumperBallContactBehavior extends ContactBehavior { +class AndroidBumperBallContactBehavior extends ContactBehavior { @override void beginContact(Object other, Contact contact) { super.beginContact(other, contact); diff --git a/packages/pinball_components/lib/src/components/alien_bumper/behaviors/alien_bumper_blinking_behavior.dart b/packages/pinball_components/lib/src/components/android_bumper/behaviors/android_bumper_blinking_behavior.dart similarity index 52% rename from packages/pinball_components/lib/src/components/alien_bumper/behaviors/alien_bumper_blinking_behavior.dart rename to packages/pinball_components/lib/src/components/android_bumper/behaviors/android_bumper_blinking_behavior.dart index f606ec70..4f7dc135 100644 --- a/packages/pinball_components/lib/src/components/alien_bumper/behaviors/alien_bumper_blinking_behavior.dart +++ b/packages/pinball_components/lib/src/components/android_bumper/behaviors/android_bumper_blinking_behavior.dart @@ -2,20 +2,20 @@ import 'package:flame/components.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; -/// {@template alien_bumper_blinking_behavior} -/// Makes a [AlienBumper] blink back to [AlienBumperState.active] when -/// [AlienBumperState.inactive]. +/// {@template android_bumper_blinking_behavior} +/// Makes an [AndroidBumper] blink back to [AndroidBumperState.lit] when +/// [AndroidBumperState.dimmed]. /// {@endtemplate} -class AlienBumperBlinkingBehavior extends TimerComponent - with ParentIsA { - /// {@macro alien_bumper_blinking_behavior} - AlienBumperBlinkingBehavior() : super(period: 0.05); +class AndroidBumperBlinkingBehavior extends TimerComponent + with ParentIsA { + /// {@macro android_bumper_blinking_behavior} + AndroidBumperBlinkingBehavior() : super(period: 0.05); - void _onNewState(AlienBumperState state) { + void _onNewState(AndroidBumperState state) { switch (state) { - case AlienBumperState.active: + case AndroidBumperState.lit: break; - case AlienBumperState.inactive: + case AndroidBumperState.dimmed: timer ..reset() ..start(); diff --git a/packages/pinball_components/lib/src/components/android_bumper/behaviors/behaviors.dart b/packages/pinball_components/lib/src/components/android_bumper/behaviors/behaviors.dart new file mode 100644 index 00000000..f7ce4900 --- /dev/null +++ b/packages/pinball_components/lib/src/components/android_bumper/behaviors/behaviors.dart @@ -0,0 +1,2 @@ +export 'android_bumper_ball_contact_behavior.dart'; +export 'android_bumper_blinking_behavior.dart'; diff --git a/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_cubit.dart b/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_cubit.dart new file mode 100644 index 00000000..3d3fd4b1 --- /dev/null +++ b/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_cubit.dart @@ -0,0 +1,17 @@ +// ignore_for_file: public_member_api_docs + +import 'package:bloc/bloc.dart'; + +part 'android_bumper_state.dart'; + +class AndroidBumperCubit extends Cubit { + AndroidBumperCubit() : super(AndroidBumperState.dimmed); + + void onBallContacted() { + emit(AndroidBumperState.dimmed); + } + + void onBlinked() { + emit(AndroidBumperState.lit); + } +} diff --git a/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_state.dart b/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_state.dart new file mode 100644 index 00000000..f101c3e9 --- /dev/null +++ b/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_state.dart @@ -0,0 +1,8 @@ +// ignore_for_file: public_member_api_docs + +part of 'android_bumper_cubit.dart'; + +enum AndroidBumperState { + lit, + dimmed, +} diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart index 2ba5ab3f..3d0f9445 100644 --- a/packages/pinball_components/lib/src/components/boundaries.dart +++ b/packages/pinball_components/lib/src/components/boundaries.dart @@ -67,7 +67,7 @@ class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef { _BottomBoundarySpriteComponent() : super( anchor: Anchor.center, - position: Vector2(-5.4, 55.6), + position: Vector2(-5, 55.6), ); @override @@ -102,28 +102,59 @@ class _OuterBoundary extends BodyComponent with InitialPosition { Vector2(3.6, -70.2), Vector2(-14.1, -70.2), ); - final topWallFixtureDef = FixtureDef(topWall); final topLeftCurve = BezierCurveShape( controlPoints: [ - Vector2(-32.3, -57.2), + topWall.vertex1, Vector2(-31.5, -69.9), - Vector2(-14.1, -70.2), + Vector2(-32.3, -57.2), ], ); - final topLeftCurveFixtureDef = FixtureDef(topLeftCurve); - final leftWall = EdgeShape() + final topLeftWall = EdgeShape() ..set( - Vector2(-32.3, -57.2), + topLeftCurve.vertices.last, + Vector2(-33.5, -44), + ); + + final upperLeftWallCurve = BezierCurveShape( + controlPoints: [ + topLeftWall.vertex1, + Vector2(-33.9, -40.7), + Vector2(-32.5, -39), + ], + ); + + final middleLeftWallCurve = BezierCurveShape( + controlPoints: [ + upperLeftWallCurve.vertices.last, + Vector2(-23.2, -31.4), + Vector2(-33.9, -21.8), + ], + ); + + final lowerLeftWallCurve = BezierCurveShape( + controlPoints: [ + middleLeftWallCurve.vertices.last, + Vector2(-32.4, -17.6), + Vector2(-37.3, -11), + ], + ); + + final bottomLeftWall = EdgeShape() + ..set( + lowerLeftWallCurve.vertices.last, Vector2(-43.9, 41.8), ); - final leftWallFixtureDef = FixtureDef(leftWall); return [ - topWallFixtureDef, - topLeftCurveFixtureDef, - leftWallFixtureDef, + FixtureDef(topWall), + FixtureDef(topLeftCurve), + FixtureDef(topLeftWall), + FixtureDef(upperLeftWallCurve), + FixtureDef(middleLeftWallCurve), + FixtureDef(lowerLeftWallCurve), + FixtureDef(bottomLeftWall), ]; } diff --git a/packages/pinball_components/lib/src/components/bumping_behavior.dart b/packages/pinball_components/lib/src/components/bumping_behavior.dart new file mode 100644 index 00000000..654f96b4 --- /dev/null +++ b/packages/pinball_components/lib/src/components/bumping_behavior.dart @@ -0,0 +1,25 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +/// {@template bumping_behavior} +/// Makes any [BodyComponent] that contacts with [parent] bounce off. +/// {@endtemplate} +class BumpingBehavior extends ContactBehavior { + /// {@macro bumping_behavior} + BumpingBehavior({required double strength}) : _strength = strength; + + /// Determines how strong the bump is. + final double _strength; + + @override + void postSolve(Object other, Contact contact, ContactImpulse impulse) { + super.postSolve(other, contact, impulse); + if (other is! BodyComponent) return; + + other.body.applyLinearImpulse( + contact.manifold.localPoint + ..normalize() + ..multiply(Vector2.all(other.body.mass * _strength)), + ); + } +} diff --git a/packages/pinball_components/lib/src/components/chrome_dino.dart b/packages/pinball_components/lib/src/components/chrome_dino.dart index 7846f140..e1a1a1fc 100644 --- a/packages/pinball_components/lib/src/components/chrome_dino.dart +++ b/packages/pinball_components/lib/src/components/chrome_dino.dart @@ -1,31 +1,33 @@ import 'dart:async'; -import 'dart:math' as math; import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart' hide Timer; -import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; /// {@template chrome_dino} -/// Dinosaur that gobbles up a [Ball], swivel his head around, and shoots it -/// back out. +/// Dino that swivels back and forth, opening its mouth to eat a [Ball]. +/// +/// Upon eating a [Ball], the dino rotates and spits the [Ball] out in a +/// different direction. /// {@endtemplate} class ChromeDino extends BodyComponent with InitialPosition { /// {@macro chrome_dino} ChromeDino() : super( - // TODO(alestiago): Remove once sprites are defined. - paint: Paint()..color = Colors.blue, priority: RenderPriority.dino, + renderBody: false, ); /// The size of the dinosaur mouth. - static final size = Vector2(5, 2.5); + static final size = Vector2(5.5, 5); /// Anchors the [ChromeDino] to the [RevoluteJoint] that controls its arc /// motion. Future<_ChromeDinoJoint> _anchorToJoint() async { - final anchor = _ChromeDinoAnchor(); + // TODO(allisonryan0002): try moving to anchor after new body is defined. + final anchor = _ChromeDinoAnchor() + ..initialPosition = initialPosition + Vector2(9, -4); + await add(anchor); final jointDef = _ChromeDinoAnchorRevoluteJointDef( @@ -42,9 +44,11 @@ class ChromeDino extends BodyComponent with InitialPosition { Future onLoad() async { await super.onLoad(); final joint = await _anchorToJoint(); + const framesInAnimation = 98; + const animationFPS = 1 / 24; await add( TimerComponent( - period: 1, + period: (framesInAnimation / 2) * animationFPS, onTick: joint._swivel, repeat: true, ), @@ -54,44 +58,17 @@ class ChromeDino extends BodyComponent with InitialPosition { List _createFixtureDefs() { final fixtureDefs = []; - // TODO(alestiago): Subject to change when sprites are added. - final box = PolygonShape()..setAsBoxXY(size.x / 2, size.y / 2); - final fixtureDef = FixtureDef( - box, - density: 999, - friction: 0.3, - restitution: 0.1, - isSensor: true, - ); - + // TODO(allisonryan0002): Update this shape to better match sprite. + final box = PolygonShape() + ..setAsBox( + size.x / 2, + size.y / 2, + initialPosition + Vector2(-4, 2), + -_ChromeDinoJoint._halfSweepingAngle, + ); + final fixtureDef = FixtureDef(box, density: 1); fixtureDefs.add(fixtureDef); - // FIXME(alestiago): Investigate why adding these fixtures is considered as - // an invalid contact type. - // final upperEdge = EdgeShape() - // ..set( - // Vector2(-size.x / 2, -size.y / 2), - // Vector2(size.x / 2, -size.y / 2), - // ); - // final upperEdgeDef = FixtureDef(upperEdge)..density = 0.5; - // fixtureDefs.add(upperEdgeDef); - - // final lowerEdge = EdgeShape() - // ..set( - // Vector2(-size.x / 2, size.y / 2), - // Vector2(size.x / 2, size.y / 2), - // ); - // final lowerEdgeDef = FixtureDef(lowerEdge)..density = 0.5; - // fixtureDefs.add(lowerEdgeDef); - - // final rightEdge = EdgeShape() - // ..set( - // Vector2(size.x / 2, -size.y / 2), - // Vector2(size.x / 2, size.y / 2), - // ); - // final rightEdgeDef = FixtureDef(rightEdge)..density = 0.5; - // fixtureDefs.add(rightEdgeDef); - return fixtureDefs; } @@ -110,13 +87,18 @@ class ChromeDino extends BodyComponent with InitialPosition { } } -/// {@template flipper_anchor} -/// [JointAnchor] positioned at the end of a [ChromeDino]. -/// {@endtemplate} class _ChromeDinoAnchor extends JointAnchor { - /// {@macro flipper_anchor} - _ChromeDinoAnchor() { - initialPosition = Vector2(ChromeDino.size.x / 2, 0); + _ChromeDinoAnchor(); + + // TODO(allisonryan0002): if these aren't moved when fixing the rendering, see + // if the joint can be created in onMount to resolve render syncing. + @override + Future onLoad() async { + await super.onLoad(); + await addAll([ + _ChromeDinoMouthSprite(), + _ChromeDinoHeadSprite(), + ]); } } @@ -135,22 +117,86 @@ class _ChromeDinoAnchorRevoluteJointDef extends RevoluteJointDef { chromeDino.body.position + anchor.body.position, ); enableLimit = true; - // TODO(alestiago): Apply design angle value. - const angle = math.pi / 3.5; - lowerAngle = -angle / 2; - upperAngle = angle / 2; + lowerAngle = -_ChromeDinoJoint._halfSweepingAngle; + upperAngle = _ChromeDinoJoint._halfSweepingAngle; enableMotor = true; - // TODO(alestiago): Tune this values. - maxMotorTorque = motorSpeed = chromeDino.body.mass * 30; + maxMotorTorque = chromeDino.body.mass * 255; + motorSpeed = 2; } } class _ChromeDinoJoint extends RevoluteJoint { _ChromeDinoJoint(_ChromeDinoAnchorRevoluteJointDef def) : super(def); + static const _halfSweepingAngle = 0.1143; + /// Sweeps the [ChromeDino] up and down repeatedly. void _swivel() { setMotorSpeed(-motorSpeed); } } + +class _ChromeDinoMouthSprite extends SpriteAnimationComponent with HasGameRef { + _ChromeDinoMouthSprite() + : super( + anchor: Anchor(Anchor.center.x + 0.47, Anchor.center.y - 0.29), + angle: _ChromeDinoJoint._halfSweepingAngle, + ); + + @override + Future onLoad() async { + await super.onLoad(); + final image = gameRef.images.fromCache( + Assets.images.dino.animatronic.mouth.keyName, + ); + + const amountPerRow = 11; + const amountPerColumn = 9; + final textureSize = Vector2( + image.width / amountPerRow, + image.height / amountPerColumn, + ); + size = textureSize / 10; + + final data = SpriteAnimationData.sequenced( + amount: (amountPerColumn * amountPerRow) - 1, + amountPerRow: amountPerRow, + stepTime: 1 / 24, + textureSize: textureSize, + ); + animation = SpriteAnimation.fromFrameData(image, data)..currentIndex = 45; + } +} + +class _ChromeDinoHeadSprite extends SpriteAnimationComponent with HasGameRef { + _ChromeDinoHeadSprite() + : super( + anchor: Anchor(Anchor.center.x + 0.47, Anchor.center.y - 0.29), + angle: _ChromeDinoJoint._halfSweepingAngle, + ); + + @override + Future onLoad() async { + await super.onLoad(); + final image = gameRef.images.fromCache( + Assets.images.dino.animatronic.head.keyName, + ); + + const amountPerRow = 11; + const amountPerColumn = 9; + final textureSize = Vector2( + image.width / amountPerRow, + image.height / amountPerColumn, + ); + size = textureSize / 10; + + final data = SpriteAnimationData.sequenced( + amount: (amountPerColumn * amountPerRow) - 1, + amountPerRow: amountPerRow, + stepTime: 1 / 24, + textureSize: textureSize, + ); + animation = SpriteAnimation.fromFrameData(image, data)..currentIndex = 45; + } +} diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 5d6f5744..c6c5c802 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -1,4 +1,4 @@ -export 'alien_bumper/alien_bumper.dart'; +export 'android_bumper/android_bumper.dart'; export 'backboard/backboard.dart'; export 'ball.dart'; export 'baseboard.dart'; diff --git a/packages/pinball_components/lib/src/components/dino_walls.dart b/packages/pinball_components/lib/src/components/dino_walls.dart index 0e0e2efa..39824490 100644 --- a/packages/pinball_components/lib/src/components/dino_walls.dart +++ b/packages/pinball_components/lib/src/components/dino_walls.dart @@ -35,51 +35,46 @@ class _DinoTopWall extends BodyComponent with InitialPosition { List _createFixtureDefs() { final topStraightShape = EdgeShape() ..set( - Vector2(28.65, -35.1), - Vector2(29.5, -35.1), + Vector2(28.65, -34.3), + Vector2(29.5, -34.3), ); - final topStraightFixtureDef = FixtureDef(topStraightShape); final topCurveShape = BezierCurveShape( controlPoints: [ topStraightShape.vertex1, - Vector2(18.8, -27), - Vector2(26.6, -21), + Vector2(18.8, -26.2), + Vector2(26.6, -20.2), ], ); - final topCurveFixtureDef = FixtureDef(topCurveShape); final middleCurveShape = BezierCurveShape( controlPoints: [ topCurveShape.vertices.last, - Vector2(27.8, -20.1), - Vector2(26.8, -19.5), + Vector2(27.8, -19.3), + Vector2(26.8, -18.7), ], ); - final middleCurveFixtureDef = FixtureDef(middleCurveShape); final bottomCurveShape = BezierCurveShape( controlPoints: [ middleCurveShape.vertices.last, - Vector2(23, -15), - Vector2(27, -15), + Vector2(23, -14.2), + Vector2(27, -14.2), ], ); - final bottomCurveFixtureDef = FixtureDef(bottomCurveShape); final bottomStraightShape = EdgeShape() ..set( bottomCurveShape.vertices.last, - Vector2(31, -14.5), + Vector2(31, -13.7), ); - final bottomStraightFixtureDef = FixtureDef(bottomStraightShape); return [ - topStraightFixtureDef, - topCurveFixtureDef, - middleCurveFixtureDef, - bottomCurveFixtureDef, - bottomStraightFixtureDef, + FixtureDef(topStraightShape), + FixtureDef(topCurveShape), + FixtureDef(middleCurveShape), + FixtureDef(bottomCurveShape), + FixtureDef(bottomStraightShape), ]; } @@ -109,12 +104,12 @@ class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef { await super.onLoad(); final sprite = Sprite( gameRef.images.fromCache( - Assets.images.dino.dinoLandTop.keyName, + Assets.images.dino.topWall.keyName, ), ); this.sprite = sprite; size = sprite.originalSize / 10; - position = Vector2(22.8, -38.9); + position = Vector2(22.8, -38.1); } } @@ -131,17 +126,11 @@ class _DinoBottomWall extends BodyComponent with InitialPosition { ); List _createFixtureDefs() { - const restitution = 1.0; - final topStraightShape = EdgeShape() ..set( Vector2(32.4, -8.8), Vector2(25, -7.7), ); - final topStraightFixtureDef = FixtureDef( - topStraightShape, - restitution: restitution, - ); final topLeftCurveShape = BezierCurveShape( controlPoints: [ @@ -150,36 +139,24 @@ class _DinoBottomWall extends BodyComponent with InitialPosition { Vector2(29.8, 13.8), ], ); - final topLeftCurveFixtureDef = FixtureDef( - topLeftCurveShape, - restitution: restitution, - ); final bottomLeftStraightShape = EdgeShape() ..set( topLeftCurveShape.vertices.last, Vector2(31.9, 44.1), ); - final bottomLeftStraightFixtureDef = FixtureDef( - bottomLeftStraightShape, - restitution: restitution, - ); final bottomStraightShape = EdgeShape() ..set( bottomLeftStraightShape.vertex2, Vector2(37.8, 44.1), ); - final bottomStraightFixtureDef = FixtureDef( - bottomStraightShape, - restitution: restitution, - ); return [ - topStraightFixtureDef, - topLeftCurveFixtureDef, - bottomLeftStraightFixtureDef, - bottomStraightFixtureDef, + FixtureDef(topStraightShape), + FixtureDef(topLeftCurveShape), + FixtureDef(bottomLeftStraightShape), + FixtureDef(bottomStraightShape), ]; } @@ -203,11 +180,11 @@ class _DinoBottomWallSpriteComponent extends SpriteComponent with HasGameRef { await super.onLoad(); final sprite = Sprite( gameRef.images.fromCache( - Assets.images.dino.dinoLandBottom.keyName, + Assets.images.dino.bottomWall.keyName, ), ); this.sprite = sprite; size = sprite.originalSize / 10; - position = Vector2(23.6, -9.5); + position = Vector2(23.8, -9.5); } } diff --git a/packages/pinball_components/lib/src/components/render_priority.dart b/packages/pinball_components/lib/src/components/render_priority.dart index cf523029..395ca49c 100644 --- a/packages/pinball_components/lib/src/components/render_priority.dart +++ b/packages/pinball_components/lib/src/components/render_priority.dart @@ -24,7 +24,7 @@ abstract class RenderPriority { static const int ballOnSpaceship = _above + spaceshipSaucer; /// Render priority for the [Ball] while it's on the [SpaceshipRail]. - static const int ballOnSpaceshipRail = _below + spaceshipSaucer; + static const int ballOnSpaceshipRail = _above + spaceshipRail; /// Render priority for the [Ball] while it's on the [LaunchRamp]. static const int ballOnLaunchRamp = _above + launchRamp; @@ -83,13 +83,13 @@ abstract class RenderPriority { static const int turboChargeFlame = _above + ballOnBoard; - // Android Spaceship + // Android Acres static const int spaceshipRail = _above + bottomGroup; - static const int spaceshipRailForeground = _above + spaceshipRail; + static const int spaceshipRailExit = _above + ballOnSpaceshipRail; - static const int spaceshipSaucer = _above + spaceshipRail; + static const int spaceshipSaucer = _above + ballOnSpaceshipRail; static const int spaceshipSaucerWall = _above + spaceshipSaucer; @@ -106,7 +106,7 @@ abstract class RenderPriority { static const int spaceshipRampBoardOpening = _below + ballOnBoard; - static const int alienBumper = _above + ballOnBoard; + static const int androidBumper = _above + ballOnBoard; // Score Text diff --git a/packages/pinball_components/lib/src/components/spaceship_rail.dart b/packages/pinball_components/lib/src/components/spaceship_rail.dart index 3dfd2c1c..91540c62 100644 --- a/packages/pinball_components/lib/src/components/spaceship_rail.dart +++ b/packages/pinball_components/lib/src/components/spaceship_rail.dart @@ -2,88 +2,71 @@ 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_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; /// {@template spaceship_rail} -/// A [Blueprint] for the spaceship drop tube. +/// A [Blueprint] for the rail exiting the [Spaceship]. /// {@endtemplate} class SpaceshipRail extends Blueprint { /// {@macro spaceship_rail} SpaceshipRail() : super( components: [ - _SpaceshipRailRamp(), + _SpaceshipRail(), _SpaceshipRailExit(), - _SpaceshipRailBase(radius: 0.55) - ..initialPosition = Vector2(-26.15, -18.65), - _SpaceshipRailBase(radius: 0.8) - ..initialPosition = Vector2(-25.5, 12.9), - _SpaceshipRailForeground() + _SpaceshipRailExitSpriteComponent() ], ); } -class _SpaceshipRailRamp extends BodyComponent with Layered { - _SpaceshipRailRamp() +class _SpaceshipRail extends BodyComponent with Layered { + _SpaceshipRail() : super( priority: RenderPriority.spaceshipRail, + children: [_SpaceshipRailSpriteComponent()], renderBody: false, - children: [_SpaceshipRailRampSpriteComponent()], ) { layer = Layer.spaceshipExitRail; } List _createFixtureDefs() { - final fixturesDefs = []; - final topArcShape = ArcShape( - center: Vector2(-35.5, -30.9), + center: Vector2(-35.1, -30.9), arcRadius: 2.5, angle: math.pi, rotation: 0.2, ); - final topArcFixtureDef = FixtureDef(topArcShape); - fixturesDefs.add(topArcFixtureDef); final topLeftCurveShape = BezierCurveShape( controlPoints: [ - Vector2(-37.9, -30.4), - Vector2(-38, -23.9), + Vector2(-37.6, -30.4), + Vector2(-37.8, -23.9), Vector2(-30.93, -18.2), ], ); - final topLeftCurveFixtureDef = FixtureDef(topLeftCurveShape); - fixturesDefs.add(topLeftCurveFixtureDef); final middleLeftCurveShape = BezierCurveShape( controlPoints: [ topLeftCurveShape.vertices.last, Vector2(-22.6, -10.3), - Vector2(-30, -0.2), + Vector2(-29.5, -0.2), ], ); - final middleLeftCurveFixtureDef = FixtureDef(middleLeftCurveShape); - fixturesDefs.add(middleLeftCurveFixtureDef); final bottomLeftCurveShape = BezierCurveShape( controlPoints: [ middleLeftCurveShape.vertices.last, - Vector2(-36, 8.6), - Vector2(-32.04, 18.3), + Vector2(-35.6, 8.6), + Vector2(-31.3, 18.3), ], ); - final bottomLeftCurveFixtureDef = FixtureDef(bottomLeftCurveShape); - fixturesDefs.add(bottomLeftCurveFixtureDef); final topRightStraightShape = EdgeShape() ..set( - Vector2(-33, -31.3), Vector2(-27.2, -21.3), + Vector2(-33, -31.3), ); - final topRightStraightFixtureDef = FixtureDef(topRightStraightShape); - fixturesDefs.add(topRightStraightFixtureDef); final middleRightCurveShape = BezierCurveShape( controlPoints: [ @@ -92,8 +75,6 @@ class _SpaceshipRailRamp extends BodyComponent with Layered { Vector2(-25.29, 1.7), ], ); - final middleRightCurveFixtureDef = FixtureDef(middleRightCurveShape); - fixturesDefs.add(middleRightCurveFixtureDef); final bottomRightCurveShape = BezierCurveShape( controlPoints: [ @@ -102,10 +83,16 @@ class _SpaceshipRailRamp extends BodyComponent with Layered { Vector2(-26.8, 15.7), ], ); - final bottomRightCurveFixtureDef = FixtureDef(bottomRightCurveShape); - fixturesDefs.add(bottomRightCurveFixtureDef); - return fixturesDefs; + return [ + FixtureDef(topArcShape), + FixtureDef(topLeftCurveShape), + FixtureDef(middleLeftCurveShape), + FixtureDef(bottomLeftCurveShape), + FixtureDef(topRightStraightShape), + FixtureDef(middleRightCurveShape), + FixtureDef(bottomRightCurveShape), + ]; } @override @@ -116,55 +103,47 @@ class _SpaceshipRailRamp extends BodyComponent with Layered { } } -class _SpaceshipRailRampSpriteComponent extends SpriteComponent - with HasGameRef { +class _SpaceshipRailSpriteComponent extends SpriteComponent with HasGameRef { + _SpaceshipRailSpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(-29.4, -5.7), + ); + @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.spaceship.rail.main.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + Assets.images.spaceship.rail.main.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(-29.4, -5.7); } } -class _SpaceshipRailForeground extends SpriteComponent with HasGameRef { - _SpaceshipRailForeground() - : super(priority: RenderPriority.spaceshipRailForeground); +class _SpaceshipRailExitSpriteComponent extends SpriteComponent + with HasGameRef { + _SpaceshipRailExitSpriteComponent() + : super( + anchor: Anchor.center, + position: Vector2(-28, 19.4), + priority: RenderPriority.spaceshipRailExit, + ); @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.spaceship.rail.foreground.keyName, + final sprite = Sprite( + gameRef.images.fromCache( + Assets.images.spaceship.rail.exit.keyName, + ), ); this.sprite = sprite; size = sprite.originalSize / 10; - anchor = Anchor.center; - position = Vector2(-28.5, 19.7); - } -} - -/// Represents the ground bases of the [_SpaceshipRailRamp]. -class _SpaceshipRailBase extends BodyComponent with InitialPosition { - _SpaceshipRailBase({required this.radius}) : super(renderBody: false); - - final double radius; - - @override - Body createBody() { - final shape = CircleShape()..radius = radius; - final fixtureDef = FixtureDef(shape); - final bodyDef = BodyDef( - position: initialPosition, - ); - - return world.createBody(bodyDef)..createFixture(fixtureDef); } } diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 27bf0aec..c260b626 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -50,6 +50,7 @@ flutter: - assets/images/baseboard/ - assets/images/boundary/ - assets/images/dino/ + - assets/images/dino/animatronic/ - assets/images/flipper/ - assets/images/launch_ramp/ - assets/images/dash/ @@ -60,12 +61,11 @@ flutter: - assets/images/spaceship/rail/ - assets/images/spaceship/ramp/ - assets/images/spaceship/ramp/arrow/ - - assets/images/chrome_dino/ - assets/images/kicker/ - assets/images/plunger/ - assets/images/slingshot/ - - assets/images/alien_bumper/a/ - - assets/images/alien_bumper/b/ + - assets/images/android_bumper/a/ + - assets/images/android_bumper/b/ - assets/images/sparky/ - assets/images/sparky/computer/ - assets/images/sparky/bumper/a/ diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index e5f7f177..40396b88 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -23,7 +23,7 @@ void main() { addPlungerStories(dashbook); addSlingshotStories(dashbook); addSparkyBumperStories(dashbook); - addAlienZoneStories(dashbook); + addAndroidAcresStories(dashbook); addBoundariesStories(dashbook); addGoogleWordStories(dashbook); addLaunchRampStories(dashbook); diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/stories.dart b/packages/pinball_components/sandbox/lib/stories/alien_zone/stories.dart deleted file mode 100644 index b4e7c1b6..00000000 --- a/packages/pinball_components/sandbox/lib/stories/alien_zone/stories.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:dashbook/dashbook.dart'; -import 'package:sandbox/common/common.dart'; -import 'package:sandbox/stories/alien_zone/alien_bumper_a_game.dart'; -import 'package:sandbox/stories/alien_zone/alien_bumper_b_game.dart'; -import 'package:sandbox/stories/alien_zone/spaceship_game.dart'; -import 'package:sandbox/stories/alien_zone/spaceship_rail_game.dart'; -import 'package:sandbox/stories/alien_zone/spaceship_ramp_game.dart'; - -void addAlienZoneStories(Dashbook dashbook) { - dashbook.storiesOf('Alien Zone') - ..addGame( - title: 'Alien Bumper A', - description: AlienBumperAGame.description, - gameBuilder: (_) => AlienBumperAGame(), - ) - ..addGame( - title: 'Alien Bumper B', - description: AlienBumperBGame.description, - gameBuilder: (_) => AlienBumperBGame(), - ) - ..addGame( - title: 'Spaceship', - description: SpaceshipGame.description, - gameBuilder: (_) => SpaceshipGame(), - ) - ..addGame( - title: 'Spaceship Rail', - description: SpaceshipRailGame.description, - gameBuilder: (_) => SpaceshipRailGame(), - ) - ..addGame( - title: 'Spaceship Ramp', - description: SpaceshipRampGame.description, - gameBuilder: (_) => SpaceshipRampGame(), - ); -} diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_b_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_a_game.dart similarity index 68% rename from packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_b_game.dart rename to packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_a_game.dart index abb206ca..4dcd1cb8 100644 --- a/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_b_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_a_game.dart @@ -4,18 +4,18 @@ import 'package:flame/extensions.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; -class AlienBumperBGame extends BallGame { - AlienBumperBGame() +class AndroidBumperAGame extends BallGame { + AndroidBumperAGame() : super( color: const Color(0xFF0000FF), imagesFileNames: [ - Assets.images.alienBumper.b.active.keyName, - Assets.images.alienBumper.b.inactive.keyName, + Assets.images.androidBumper.a.lit.keyName, + Assets.images.androidBumper.a.dimmed.keyName, ], ); static const description = ''' - Shows how a AlienBumperB is rendered. + Shows how a AndroidBumperA is rendered. - Activate the "trace" parameter to overlay the body. '''; @@ -26,7 +26,7 @@ class AlienBumperBGame extends BallGame { camera.followVector2(Vector2.zero()); await add( - AlienBumper.b()..priority = 1, + AndroidBumper.a()..priority = 1, ); await traceAllBodies(); diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_a_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_b_game.dart similarity index 68% rename from packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_a_game.dart rename to packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_b_game.dart index 4832a468..e504fe1e 100644 --- a/packages/pinball_components/sandbox/lib/stories/alien_zone/alien_bumper_a_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_b_game.dart @@ -4,18 +4,18 @@ import 'package:flame/extensions.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; -class AlienBumperAGame extends BallGame { - AlienBumperAGame() +class AndroidBumperBGame extends BallGame { + AndroidBumperBGame() : super( color: const Color(0xFF0000FF), imagesFileNames: [ - Assets.images.alienBumper.a.active.keyName, - Assets.images.alienBumper.a.inactive.keyName, + Assets.images.androidBumper.b.lit.keyName, + Assets.images.androidBumper.b.dimmed.keyName, ], ); static const description = ''' - Shows how a AlienBumperA is rendered. + Shows how a AndroidBumperB is rendered. - Activate the "trace" parameter to overlay the body. '''; @@ -26,7 +26,7 @@ class AlienBumperAGame extends BallGame { camera.followVector2(Vector2.zero()); await add( - AlienBumper.a()..priority = 1, + AndroidBumper.b()..priority = 1, ); await traceAllBodies(); diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/spaceship_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_game.dart similarity index 100% rename from packages/pinball_components/sandbox/lib/stories/alien_zone/spaceship_game.dart rename to packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_game.dart diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/spaceship_rail_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_rail_game.dart similarity index 85% rename from packages/pinball_components/sandbox/lib/stories/alien_zone/spaceship_rail_game.dart rename to packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_rail_game.dart index 2a13fb5e..4bd067fa 100644 --- a/packages/pinball_components/sandbox/lib/stories/alien_zone/spaceship_rail_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_rail_game.dart @@ -12,6 +12,10 @@ class SpaceshipRailGame extends BallGame { color: Colors.blue, ballPriority: RenderPriority.ballOnSpaceshipRail, ballLayer: Layer.spaceshipExitRail, + imagesFileNames: [ + Assets.images.spaceship.rail.main.keyName, + Assets.images.spaceship.rail.exit.keyName, + ], ); static const description = ''' diff --git a/packages/pinball_components/sandbox/lib/stories/alien_zone/spaceship_ramp_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart similarity index 100% rename from packages/pinball_components/sandbox/lib/stories/alien_zone/spaceship_ramp_game.dart rename to packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/stories.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/stories.dart new file mode 100644 index 00000000..92ddd5d5 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/stories.dart @@ -0,0 +1,36 @@ +import 'package:dashbook/dashbook.dart'; +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/spaceship_game.dart'; +import 'package:sandbox/stories/android_acres/spaceship_rail_game.dart'; +import 'package:sandbox/stories/android_acres/spaceship_ramp_game.dart'; + +void addAndroidAcresStories(Dashbook dashbook) { + dashbook.storiesOf('Android Acres') + ..addGame( + title: 'Android Bumper A', + description: AndroidBumperAGame.description, + gameBuilder: (_) => AndroidBumperAGame(), + ) + ..addGame( + title: 'Android Bumper B', + description: AndroidBumperBGame.description, + gameBuilder: (_) => AndroidBumperBGame(), + ) + ..addGame( + title: 'Spaceship', + description: SpaceshipGame.description, + gameBuilder: (_) => SpaceshipGame(), + ) + ..addGame( + title: 'Spaceship Rail', + description: SpaceshipRailGame.description, + gameBuilder: (_) => SpaceshipRailGame(), + ) + ..addGame( + title: 'Spaceship Ramp', + description: SpaceshipRampGame.description, + gameBuilder: (_) => SpaceshipRampGame(), + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/chrome_dino/chrome_dino_game.dart b/packages/pinball_components/sandbox/lib/stories/chrome_dino/chrome_dino_game.dart index 2e6831e3..d6e7ef95 100644 --- a/packages/pinball_components/sandbox/lib/stories/chrome_dino/chrome_dino_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/chrome_dino/chrome_dino_game.dart @@ -1,8 +1,22 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; -class ChromeDinoGame extends Forge2DGame { - static const description = 'Shows how a ChromeDino is rendered.'; +class ChromeDinoGame extends BallGame { + ChromeDinoGame() + : super( + imagesFileNames: [ + Assets.images.dino.animatronic.mouth.keyName, + Assets.images.dino.animatronic.head.keyName, + ], + ); + + static const description = ''' + Shows how ChromeDino is rendered. + + - Activate the "trace" parameter to overlay the body. + - Tap anywhere on the screen to spawn a ball into the game. +'''; @override Future onLoad() async { @@ -10,5 +24,7 @@ class ChromeDinoGame extends Forge2DGame { camera.followVector2(Vector2.zero()); await add(ChromeDino()); + + await traceAllBodies(); } } diff --git a/packages/pinball_components/sandbox/lib/stories/chrome_dino/stories.dart b/packages/pinball_components/sandbox/lib/stories/chrome_dino/stories.dart index 391cdca7..a4c70c03 100644 --- a/packages/pinball_components/sandbox/lib/stories/chrome_dino/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/chrome_dino/stories.dart @@ -4,7 +4,7 @@ import 'package:sandbox/stories/chrome_dino/chrome_dino_game.dart'; void addChromeDinoStories(Dashbook dashbook) { dashbook.storiesOf('Chrome Dino').addGame( - title: 'Trace', + title: 'Traced', description: ChromeDinoGame.description, gameBuilder: (_) => ChromeDinoGame(), ); diff --git a/packages/pinball_components/sandbox/lib/stories/dino_wall/dino_wall_game.dart b/packages/pinball_components/sandbox/lib/stories/dino_wall/dino_wall_game.dart index b491d64b..a6987fcc 100644 --- a/packages/pinball_components/sandbox/lib/stories/dino_wall/dino_wall_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/dino_wall/dino_wall_game.dart @@ -20,8 +20,8 @@ class DinoWallGame extends BallGame { await super.onLoad(); await images.loadAll([ - Assets.images.dino.dinoLandTop.keyName, - Assets.images.dino.dinoLandBottom.keyName, + Assets.images.dino.topWall.keyName, + Assets.images.dino.bottomWall.keyName, ]); await addFromBlueprint(DinoWalls()); diff --git a/packages/pinball_components/sandbox/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index d8103b4d..df51fc0f 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -1,4 +1,4 @@ -export 'alien_zone/stories.dart'; +export 'android_acres/stories.dart'; export 'backboard/stories.dart'; export 'ball/stories.dart'; export 'baseboard/stories.dart'; diff --git a/packages/pinball_components/test/helpers/mocks.dart b/packages/pinball_components/test/helpers/mocks.dart index d69a6131..2230becb 100644 --- a/packages/pinball_components/test/helpers/mocks.dart +++ b/packages/pinball_components/test/helpers/mocks.dart @@ -17,7 +17,7 @@ class MockContact extends Mock implements Contact {} class MockComponent extends Mock implements Component {} -class MockAlienBumperCubit extends Mock implements AlienBumperCubit {} +class MockAndroidBumperCubit extends Mock implements AndroidBumperCubit {} class MockGoogleLetterCubit extends Mock implements GoogleLetterCubit {} diff --git a/packages/pinball_components/test/src/components/alien_bumper/cubit/alien_bumper_cubit_test.dart b/packages/pinball_components/test/src/components/alien_bumper/cubit/alien_bumper_cubit_test.dart deleted file mode 100644 index 140249ea..00000000 --- a/packages/pinball_components/test/src/components/alien_bumper/cubit/alien_bumper_cubit_test.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:bloc_test/bloc_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball_components/pinball_components.dart'; - -void main() { - group( - 'AlienBumperCubit', - () { - blocTest( - 'onBallContacted emits inactive', - build: AlienBumperCubit.new, - act: (bloc) => bloc.onBallContacted(), - expect: () => [AlienBumperState.inactive], - ); - - blocTest( - 'onBlinked emits active', - build: AlienBumperCubit.new, - act: (bloc) => bloc.onBlinked(), - expect: () => [AlienBumperState.active], - ); - }, - ); -} diff --git a/packages/pinball_components/test/src/components/alien_bumper/alien_bumper_test.dart b/packages/pinball_components/test/src/components/android_bumper/android_bumper_test.dart similarity index 50% rename from packages/pinball_components/test/src/components/alien_bumper/alien_bumper_test.dart rename to packages/pinball_components/test/src/components/android_bumper/android_bumper_test.dart index be34d4f8..abc51a28 100644 --- a/packages/pinball_components/test/src/components/alien_bumper/alien_bumper_test.dart +++ b/packages/pinball_components/test/src/components/android_bumper/android_bumper_test.dart @@ -6,48 +6,48 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; -import 'package:pinball_components/src/components/alien_bumper/behaviors/behaviors.dart'; +import 'package:pinball_components/src/components/android_bumper/behaviors/behaviors.dart'; import '../../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); final assets = [ - Assets.images.alienBumper.a.active.keyName, - Assets.images.alienBumper.a.inactive.keyName, - Assets.images.alienBumper.b.active.keyName, - Assets.images.alienBumper.b.inactive.keyName, + Assets.images.androidBumper.a.lit.keyName, + Assets.images.androidBumper.a.dimmed.keyName, + Assets.images.androidBumper.b.lit.keyName, + Assets.images.androidBumper.b.dimmed.keyName, ]; final flameTester = FlameTester(() => TestGame(assets)); - group('AlienBumper', () { + group('AndroidBumper', () { flameTester.test('"a" loads correctly', (game) async { - final alienBumper = AlienBumper.a(); - await game.ensureAdd(alienBumper); - expect(game.contains(alienBumper), isTrue); + final androidBumper = AndroidBumper.a(); + await game.ensureAdd(androidBumper); + expect(game.contains(androidBumper), isTrue); }); flameTester.test('"b" loads correctly', (game) async { - final alienBumper = AlienBumper.b(); - await game.ensureAdd(alienBumper); - expect(game.contains(alienBumper), isTrue); + final androidBumper = AndroidBumper.b(); + await game.ensureAdd(androidBumper); + expect(game.contains(androidBumper), isTrue); }); // TODO(alestiago): Consider refactoring once the following is merged: // https://github.com/flame-engine/flame/pull/1538 // ignore: public_member_api_docs flameTester.test('closes bloc when removed', (game) async { - final bloc = MockAlienBumperCubit(); + final bloc = MockAndroidBumperCubit(); whenListen( bloc, - const Stream.empty(), - initialState: AlienBumperState.active, + const Stream.empty(), + initialState: AndroidBumperState.lit, ); when(bloc.close).thenAnswer((_) async {}); - final alienBumper = AlienBumper.test(bloc: bloc); + final androidBumper = AndroidBumper.test(bloc: bloc); - await game.ensureAdd(alienBumper); - game.remove(alienBumper); + await game.ensureAdd(androidBumper); + game.remove(androidBumper); await game.ready(); verify(bloc.close).called(1); @@ -56,19 +56,19 @@ void main() { group('adds', () { flameTester.test('new children', (game) async { final component = Component(); - final alienBumper = AlienBumper.a( + final androidBumper = AndroidBumper.a( children: [component], ); - await game.ensureAdd(alienBumper); - expect(alienBumper.children, contains(component)); + await game.ensureAdd(androidBumper); + expect(androidBumper.children, contains(component)); }); - flameTester.test('an AlienBumperBallContactBehavior', (game) async { - final alienBumper = AlienBumper.a(); - await game.ensureAdd(alienBumper); + flameTester.test('an AndroidBumperBallContactBehavior', (game) async { + final androidBumper = AndroidBumper.a(); + await game.ensureAdd(androidBumper); expect( - alienBumper.children - .whereType() + androidBumper.children + .whereType() .single, isNotNull, ); diff --git a/packages/pinball_components/test/src/components/alien_bumper/behaviors/alien_bumper_ball_contact_behavior_test.dart b/packages/pinball_components/test/src/components/android_bumper/behaviors/android_bumper_ball_contact_behavior_test.dart similarity index 54% rename from packages/pinball_components/test/src/components/alien_bumper/behaviors/alien_bumper_ball_contact_behavior_test.dart rename to packages/pinball_components/test/src/components/android_bumper/behaviors/android_bumper_ball_contact_behavior_test.dart index 93904958..69e6ce43 100644 --- a/packages/pinball_components/test/src/components/alien_bumper/behaviors/alien_bumper_ball_contact_behavior_test.dart +++ b/packages/pinball_components/test/src/components/android_bumper/behaviors/android_bumper_ball_contact_behavior_test.dart @@ -5,7 +5,7 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; -import 'package:pinball_components/src/components/alien_bumper/behaviors/behaviors.dart'; +import 'package:pinball_components/src/components/android_bumper/behaviors/behaviors.dart'; import '../../../../helpers/helpers.dart'; @@ -14,33 +14,33 @@ void main() { final flameTester = FlameTester(TestGame.new); group( - 'AlienBumperBallContactBehavior', + 'AndroidBumperBallContactBehavior', () { test('can be instantiated', () { expect( - AlienBumperBallContactBehavior(), - isA(), + AndroidBumperBallContactBehavior(), + isA(), ); }); flameTester.test( 'beginContact emits onBallContacted when contacts with a ball', (game) async { - final behavior = AlienBumperBallContactBehavior(); - final bloc = MockAlienBumperCubit(); + final behavior = AndroidBumperBallContactBehavior(); + final bloc = MockAndroidBumperCubit(); whenListen( bloc, - const Stream.empty(), - initialState: AlienBumperState.active, + const Stream.empty(), + initialState: AndroidBumperState.lit, ); - final alienBumper = AlienBumper.test(bloc: bloc); - await alienBumper.add(behavior); - await game.ensureAdd(alienBumper); + final androidBumper = AndroidBumper.test(bloc: bloc); + await androidBumper.add(behavior); + await game.ensureAdd(androidBumper); behavior.beginContact(MockBall(), MockContact()); - verify(alienBumper.bloc.onBallContacted).called(1); + verify(androidBumper.bloc.onBallContacted).called(1); }, ); }, diff --git a/packages/pinball_components/test/src/components/alien_bumper/behaviors/alien_bumper_blinking_behavior_test.dart b/packages/pinball_components/test/src/components/android_bumper/behaviors/android_bumper_blinking_behavior_test.dart similarity index 55% rename from packages/pinball_components/test/src/components/alien_bumper/behaviors/alien_bumper_blinking_behavior_test.dart rename to packages/pinball_components/test/src/components/android_bumper/behaviors/android_bumper_blinking_behavior_test.dart index e8e5a571..f7b09dfb 100644 --- a/packages/pinball_components/test/src/components/alien_bumper/behaviors/alien_bumper_blinking_behavior_test.dart +++ b/packages/pinball_components/test/src/components/android_bumper/behaviors/android_bumper_blinking_behavior_test.dart @@ -5,7 +5,7 @@ import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; -import 'package:pinball_components/src/components/alien_bumper/behaviors/behaviors.dart'; +import 'package:pinball_components/src/components/android_bumper/behaviors/behaviors.dart'; import '../../../../helpers/helpers.dart'; @@ -14,25 +14,25 @@ void main() { final flameTester = FlameTester(TestGame.new); group( - 'AlienBumperBlinkingBehavior', + 'AndroidBumperBlinkingBehavior', () { flameTester.testGameWidget( - 'calls onBlinked after 0.05 seconds when inactive', + 'calls onBlinked after 0.05 seconds when dimmed', setUp: (game, tester) async { - final behavior = AlienBumperBlinkingBehavior(); - final bloc = MockAlienBumperCubit(); - final streamController = StreamController(); + final behavior = AndroidBumperBlinkingBehavior(); + final bloc = MockAndroidBumperCubit(); + final streamController = StreamController(); whenListen( bloc, streamController.stream, - initialState: AlienBumperState.active, + initialState: AndroidBumperState.lit, ); - final alienBumper = AlienBumper.test(bloc: bloc); - await alienBumper.add(behavior); - await game.ensureAdd(alienBumper); + final androidBumper = AndroidBumper.test(bloc: bloc); + await androidBumper.add(behavior); + await game.ensureAdd(androidBumper); - streamController.add(AlienBumperState.inactive); + streamController.add(AndroidBumperState.dimmed); await tester.pump(); game.update(0.05); diff --git a/packages/pinball_components/test/src/components/android_bumper/cubit/android_bumper_cubit_test.dart b/packages/pinball_components/test/src/components/android_bumper/cubit/android_bumper_cubit_test.dart new file mode 100644 index 00000000..06095228 --- /dev/null +++ b/packages/pinball_components/test/src/components/android_bumper/cubit/android_bumper_cubit_test.dart @@ -0,0 +1,24 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +void main() { + group( + 'AndroidBumperCubit', + () { + blocTest( + 'onBallContacted emits dimmed', + build: AndroidBumperCubit.new, + act: (bloc) => bloc.onBallContacted(), + expect: () => [AndroidBumperState.dimmed], + ); + + blocTest( + 'onBlinked emits lit', + build: AndroidBumperCubit.new, + act: (bloc) => bloc.onBlinked(), + expect: () => [AndroidBumperState.lit], + ); + }, + ); +} diff --git a/packages/pinball_components/test/src/components/bumping_behavior_test.dart b/packages/pinball_components/test/src/components/bumping_behavior_test.dart new file mode 100644 index 00000000..d346a0ae --- /dev/null +++ b/packages/pinball_components/test/src/components/bumping_behavior_test.dart @@ -0,0 +1,84 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball_components/src/components/bumping_behavior.dart'; + +import '../../helpers/helpers.dart'; +import 'layer_test.dart'; + +class MockContactImpulse extends Mock implements ContactImpulse {} + +class MockManifold extends Mock implements Manifold {} + +class TestHeavyBodyComponent extends BodyComponent { + @override + Body createBody() { + final shape = CircleShape(); + return world.createBody( + BodyDef( + type: BodyType.dynamic, + ), + )..createFixtureFromShape(shape, 20); + } +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final flameTester = FlameTester(TestGame.new); + + group('BumpingBehavior', () { + flameTester.test('can be added', (game) async { + final behavior = BumpingBehavior(strength: 0); + final component = TestBodyComponent(); + await component.add(behavior); + await game.ensureAdd(component); + }); + + flameTester.testGameWidget( + 'the bump is greater when the strengh is greater', + setUp: (game, tester) async { + final component1 = TestBodyComponent(); + final behavior1 = BumpingBehavior(strength: 1); + await component1.add(behavior1); + + final component2 = TestBodyComponent(); + final behavior2 = BumpingBehavior(strength: 2); + await component2.add(behavior2); + + final dummy1 = TestHeavyBodyComponent(); + final dummy2 = TestHeavyBodyComponent(); + + await game.ensureAddAll([ + component1, + component2, + dummy1, + dummy2, + ]); + + expect(dummy1.body.inverseMass, greaterThan(0)); + expect(dummy2.body.inverseMass, greaterThan(0)); + + final contact = MockContact(); + final manifold = MockManifold(); + final contactImpulse = MockContactImpulse(); + when(() => manifold.localPoint).thenReturn(Vector2.all(1)); + when(() => contact.manifold).thenReturn(manifold); + + behavior1.postSolve(dummy1, contact, contactImpulse); + behavior2.postSolve(dummy2, contact, contactImpulse); + + expect( + dummy2.body.linearVelocity.x, + greaterThan(dummy1.body.linearVelocity.x), + ); + expect( + dummy2.body.linearVelocity.y, + greaterThan(dummy1.body.linearVelocity.y), + ); + }, + ); + }); +} diff --git a/packages/pinball_components/test/src/components/chrome_dino_test.dart b/packages/pinball_components/test/src/components/chrome_dino_test.dart index 8a0adb85..f97270b9 100644 --- a/packages/pinball_components/test/src/components/chrome_dino_test.dart +++ b/packages/pinball_components/test/src/components/chrome_dino_test.dart @@ -1,13 +1,19 @@ // ignore_for_file: cascade_invocations -import 'package:flame_forge2d/flame_forge2d.dart'; +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 flameTester = FlameTester(Forge2DGame.new); + final assets = [ + Assets.images.dino.animatronic.mouth.keyName, + Assets.images.dino.animatronic.head.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); group('ChromeDino', () { flameTester.test( @@ -20,19 +26,84 @@ void main() { }, ); - flameTester.test( - 'swivels', - (game) async { - // TODO(alestiago): Write golden tests to check the - // swivel animation. - final chromeDino = ChromeDino(); - await game.ensureAdd(chromeDino); + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.images.loadAll(assets); + await game.ensureAdd(ChromeDino()); + game.camera.followVector2(Vector2.zero()); + await tester.pump(); + }, + verify: (game, tester) async { + final sweepAnimationDuration = game + .descendants() + .whereType() + .first + .animation! + .totalDuration() / + 2; + + await expectLater( + find.byGame(), + matchesGoldenFile('golden/chrome_dino/up.png'), + ); - final previousPosition = chromeDino.body.position.clone(); - game.update(64); + game.update(sweepAnimationDuration * 0.25); + await tester.pump(); + await expectLater( + find.byGame(), + matchesGoldenFile('golden/chrome_dino/middle.png'), + ); - expect(chromeDino.body.position, isNot(equals(previousPosition))); + game.update(sweepAnimationDuration * 0.25); + await tester.pump(); + await expectLater( + find.byGame(), + matchesGoldenFile('golden/chrome_dino/down.png'), + ); }, ); + + group('swivels', () { + flameTester.test( + 'up', + (game) async { + final chromeDino = ChromeDino(); + await game.ensureAdd(chromeDino); + game.camera.followVector2(Vector2.zero()); + + final sweepAnimationDuration = game + .descendants() + .whereType() + .first + .animation! + .totalDuration() / + 2; + game.update(sweepAnimationDuration * 1.5); + + expect(chromeDino.body.angularVelocity, isPositive); + }, + ); + + flameTester.test( + 'down', + (game) async { + final chromeDino = ChromeDino(); + await game.ensureAdd(chromeDino); + game.camera.followVector2(Vector2.zero()); + + final sweepAnimationDuration = game + .descendants() + .whereType() + .first + .animation! + .totalDuration() / + 2; + game.update(sweepAnimationDuration * 0.5); + + expect(chromeDino.body.angularVelocity, isNegative); + }, + ); + }); }); } diff --git a/packages/pinball_components/test/src/components/dash_animatronic_test.dart b/packages/pinball_components/test/src/components/dash_animatronic_test.dart index d0707223..d64c3f07 100644 --- a/packages/pinball_components/test/src/components/dash_animatronic_test.dart +++ b/packages/pinball_components/test/src/components/dash_animatronic_test.dart @@ -45,6 +45,7 @@ void main() { ); }, ); + flameTester.test( 'loads correctly', (game) async { diff --git a/packages/pinball_components/test/src/components/dino_walls_test.dart b/packages/pinball_components/test/src/components/dino_walls_test.dart index 7ed97248..ff64fb00 100644 --- a/packages/pinball_components/test/src/components/dino_walls_test.dart +++ b/packages/pinball_components/test/src/components/dino_walls_test.dart @@ -12,8 +12,8 @@ void main() { group('DinoWalls', () { TestWidgetsFlutterBinding.ensureInitialized(); final assets = [ - Assets.images.dino.dinoLandTop.keyName, - Assets.images.dino.dinoLandBottom.keyName, + Assets.images.dino.topWall.keyName, + Assets.images.dino.bottomWall.keyName, ]; final flameTester = FlameTester(() => TestGame(assets)); diff --git a/packages/pinball_components/test/src/components/golden/boundaries.png b/packages/pinball_components/test/src/components/golden/boundaries.png index 2612679a..9e9b5633 100644 Binary files a/packages/pinball_components/test/src/components/golden/boundaries.png and b/packages/pinball_components/test/src/components/golden/boundaries.png differ diff --git a/packages/pinball_components/test/src/components/golden/chrome_dino/down.png b/packages/pinball_components/test/src/components/golden/chrome_dino/down.png new file mode 100644 index 00000000..ef91da0a Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/chrome_dino/down.png differ diff --git a/packages/pinball_components/test/src/components/golden/chrome_dino/middle.png b/packages/pinball_components/test/src/components/golden/chrome_dino/middle.png new file mode 100644 index 00000000..d4e6286a Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/chrome_dino/middle.png differ diff --git a/packages/pinball_components/test/src/components/golden/chrome_dino/up.png b/packages/pinball_components/test/src/components/golden/chrome_dino/up.png new file mode 100644 index 00000000..042028d1 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/chrome_dino/up.png differ diff --git a/packages/pinball_components/test/src/components/golden/dino-walls.png b/packages/pinball_components/test/src/components/golden/dino-walls.png index 5956b43b..b47c453f 100644 Binary files a/packages/pinball_components/test/src/components/golden/dino-walls.png and b/packages/pinball_components/test/src/components/golden/dino-walls.png differ diff --git a/packages/pinball_components/test/src/components/golden/spaceship-rail.png b/packages/pinball_components/test/src/components/golden/spaceship-rail.png index d81f7dba..d8ce5fca 100644 Binary files a/packages/pinball_components/test/src/components/golden/spaceship-rail.png and b/packages/pinball_components/test/src/components/golden/spaceship-rail.png differ diff --git a/packages/pinball_components/test/src/components/spaceship_rail_test.dart b/packages/pinball_components/test/src/components/spaceship_rail_test.dart index d3242ff6..bc5a7f75 100644 --- a/packages/pinball_components/test/src/components/spaceship_rail_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_rail_test.dart @@ -11,13 +11,19 @@ import '../../helpers/helpers.dart'; void main() { group('SpaceshipRail', () { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final assets = [ + Assets.images.spaceship.rail.main.keyName, + Assets.images.spaceship.rail.exit.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); flameTester.testGameWidget( 'renders correctly', setUp: (game, tester) async { + await game.images.loadAll(assets); await game.addFromBlueprint(SpaceshipRail()); await game.ready(); + await tester.pump(); game.camera.followVector2(Vector2.zero()); game.camera.zoom = 8; diff --git a/pubspec.lock b/pubspec.lock index 4a851209..9ee8ae6c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "39.0.0" + version: "31.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "2.8.0" args: dependency: transitive description: @@ -71,6 +71,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" clock: dependency: transitive description: @@ -316,7 +323,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.4" + version: "0.6.3" json_annotation: dependency: transitive description: @@ -351,7 +358,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.3" meta: dependency: transitive description: @@ -414,7 +421,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.0" path_provider: dependency: transitive description: @@ -587,7 +594,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.8.1" stack_trace: dependency: transitive description: @@ -629,21 +636,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.21.1" + version: "1.19.5" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" + version: "0.4.8" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.13" + version: "0.4.9" typed_data: dependency: transitive description: @@ -664,7 +671,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.2" + version: "2.1.1" very_good_analysis: dependency: "direct dev" description: diff --git a/test/game/bloc/game_bloc_test.dart b/test/game/bloc/game_bloc_test.dart index 37e14f73..3711105e 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -4,27 +4,69 @@ import 'package:pinball/game/game.dart'; void main() { group('GameBloc', () { - test('initial state has 3 balls and empty score', () { + test('initial state has 3 rounds and empty score', () { final gameBloc = GameBloc(); expect(gameBloc.state.score, equals(0)); - expect(gameBloc.state.balls, equals(3)); + expect(gameBloc.state.rounds, equals(3)); }); - group('LostBall', () { + group('RoundLost', () { blocTest( - 'decreases number of balls', + 'decreases number of rounds ' + 'when there are already available rounds', build: GameBloc.new, act: (bloc) { - bloc.add(const BallLost()); + bloc.add(const RoundLost()); }, expect: () => [ const GameState( score: 0, - balls: 2, + multiplier: 1, + rounds: 2, bonusHistory: [], ), ], ); + + blocTest( + 'apply multiplier to score ' + 'when round is lost', + build: GameBloc.new, + seed: () => const GameState( + score: 5, + multiplier: 3, + rounds: 2, + bonusHistory: [], + ), + act: (bloc) { + bloc.add(const RoundLost()); + }, + expect: () => [ + isA() + ..having((state) => state.score, 'score', 15) + ..having((state) => state.rounds, 'rounds', 1), + ], + ); + + blocTest( + 'resets multiplier ' + 'when round is lost', + build: GameBloc.new, + seed: () => const GameState( + score: 5, + multiplier: 3, + rounds: 2, + bonusHistory: [], + ), + act: (bloc) { + bloc.add(const RoundLost()); + }, + expect: () => [ + isA() + ..having((state) => state.multiplier, 'multiplier', 1) + ..having((state) => state.rounds, 'rounds', 1), + ], + ); }); group('Scored', () { @@ -36,16 +78,12 @@ void main() { ..add(const Scored(points: 2)) ..add(const Scored(points: 3)), expect: () => [ - const GameState( - score: 2, - balls: 3, - bonusHistory: [], - ), - const GameState( - score: 5, - balls: 3, - bonusHistory: [], - ), + isA() + ..having((state) => state.score, 'score', 2) + ..having((state) => state.isGameOver, 'isGameOver', false), + isA() + ..having((state) => state.score, 'score', 5) + ..having((state) => state.isGameOver, 'isGameOver', false), ], ); @@ -54,27 +92,85 @@ void main() { 'when game is over', build: GameBloc.new, act: (bloc) { - for (var i = 0; i < bloc.state.balls; i++) { - bloc.add(const BallLost()); + for (var i = 0; i < bloc.state.rounds; i++) { + bloc.add(const RoundLost()); } bloc.add(const Scored(points: 2)); }, expect: () => [ - const GameState( - score: 0, - balls: 2, - bonusHistory: [], - ), - const GameState( - score: 0, - balls: 1, - bonusHistory: [], - ), - const GameState( - score: 0, - balls: 0, - bonusHistory: [], - ), + isA() + ..having((state) => state.score, 'score', 0) + ..having((state) => state.rounds, 'rounds', 2) + ..having((state) => state.isGameOver, 'isGameOver', false), + isA() + ..having((state) => state.score, 'score', 0) + ..having((state) => state.rounds, 'rounds', 1) + ..having((state) => state.isGameOver, 'isGameOver', false), + isA() + ..having((state) => state.score, 'score', 0) + ..having((state) => state.rounds, 'rounds', 0) + ..having((state) => state.isGameOver, 'isGameOver', true), + ], + ); + }); + + group('MultiplierIncreased', () { + blocTest( + 'increases multiplier ' + 'when multiplier is below 6 and game is not over', + build: GameBloc.new, + act: (bloc) => bloc + ..add(const MultiplierIncreased()) + ..add(const MultiplierIncreased()), + expect: () => [ + isA() + ..having((state) => state.score, 'score', 0) + ..having((state) => state.multiplier, 'multiplier', 2) + ..having((state) => state.isGameOver, 'isGameOver', false), + isA() + ..having((state) => state.score, 'score', 0) + ..having((state) => state.multiplier, 'multiplier', 3) + ..having((state) => state.isGameOver, 'isGameOver', false), + ], + ); + + blocTest( + "doesn't increase multiplier " + 'when multiplier is 6 and game is not over', + build: GameBloc.new, + seed: () => const GameState( + score: 0, + multiplier: 6, + rounds: 3, + bonusHistory: [], + ), + act: (bloc) => bloc..add(const MultiplierIncreased()), + expect: () => const [], + ); + + blocTest( + "doesn't increase multiplier " + 'when game is over', + build: GameBloc.new, + act: (bloc) { + for (var i = 0; i < bloc.state.rounds; i++) { + bloc.add(const RoundLost()); + } + bloc.add(const MultiplierIncreased()); + }, + expect: () => [ + isA() + ..having((state) => state.score, 'score', 0) + ..having((state) => state.multiplier, 'multiplier', 1) + ..having((state) => state.isGameOver, 'isGameOver', false), + isA() + ..having((state) => state.score, 'score', 0) + ..having((state) => state.multiplier, 'multiplier', 1) + ..having((state) => state.isGameOver, 'isGameOver', false), + isA() + ..having((state) => state.score, 'score', 0) + ..having((state) => state.multiplier, 'multiplier', 1) + ..having((state) => state.isGameOver, 'isGameOver', true), ], ); }); @@ -88,17 +184,19 @@ void main() { act: (bloc) => bloc ..add(const BonusActivated(GameBonus.googleWord)) ..add(const BonusActivated(GameBonus.dashNest)), - expect: () => const [ - GameState( - score: 0, - balls: 3, - bonusHistory: [GameBonus.googleWord], - ), - GameState( - score: 0, - balls: 3, - bonusHistory: [GameBonus.googleWord, GameBonus.dashNest], - ), + expect: () => [ + isA() + ..having( + (state) => state.bonusHistory, + 'bonusHistory', + [GameBonus.googleWord], + ), + isA() + ..having( + (state) => state.bonusHistory, + 'bonusHistory', + [GameBonus.googleWord, GameBonus.dashNest], + ), ], ); }, @@ -109,12 +207,13 @@ void main() { 'adds game bonus', build: GameBloc.new, act: (bloc) => bloc..add(const SparkyTurboChargeActivated()), - expect: () => const [ - GameState( - score: 0, - balls: 3, - bonusHistory: [GameBonus.sparkyTurboCharge], - ), + expect: () => [ + isA() + ..having( + (state) => state.bonusHistory, + 'bonusHistory', + [GameBonus.sparkyTurboCharge], + ), ], ); }); diff --git a/test/game/bloc/game_event_test.dart b/test/game/bloc/game_event_test.dart index d7d587bd..6a39bd67 100644 --- a/test/game/bloc/game_event_test.dart +++ b/test/game/bloc/game_event_test.dart @@ -5,15 +5,15 @@ import 'package:pinball/game/game.dart'; void main() { group('GameEvent', () { - group('BallLost', () { + group('RoundLost', () { test('can be instantiated', () { - expect(const BallLost(), isNotNull); + expect(const RoundLost(), isNotNull); }); test('supports value equality', () { expect( - BallLost(), - equals(const BallLost()), + RoundLost(), + equals(const RoundLost()), ); }); }); @@ -41,6 +41,19 @@ void main() { }); }); + group('MultiplierIncreased', () { + test('can be instantiated', () { + expect(const MultiplierIncreased(), isNotNull); + }); + + test('supports value equality', () { + expect( + MultiplierIncreased(), + equals(const MultiplierIncreased()), + ); + }); + }); + group('BonusActivated', () { test('can be instantiated', () { expect(const BonusActivated(GameBonus.dashNest), isNotNull); diff --git a/test/game/bloc/game_state_test.dart b/test/game/bloc/game_state_test.dart index 8170346f..add25e05 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -9,13 +9,15 @@ void main() { expect( GameState( score: 0, - balls: 0, + multiplier: 1, + rounds: 3, bonusHistory: const [], ), equals( const GameState( score: 0, - balls: 0, + multiplier: 1, + rounds: 3, bonusHistory: [], ), ), @@ -27,7 +29,8 @@ void main() { expect( const GameState( score: 0, - balls: 0, + multiplier: 1, + rounds: 3, bonusHistory: [], ), isNotNull, @@ -37,12 +40,13 @@ void main() { test( 'throws AssertionError ' - 'when balls are negative', + 'when score is negative', () { expect( () => GameState( - balls: -1, - score: 0, + score: -1, + multiplier: 1, + rounds: 3, bonusHistory: const [], ), throwsAssertionError, @@ -52,12 +56,29 @@ void main() { test( 'throws AssertionError ' - 'when score is negative', + 'when multiplier is less than 1', () { expect( () => GameState( - balls: 0, - score: -1, + score: 1, + multiplier: 0, + rounds: 3, + bonusHistory: const [], + ), + throwsAssertionError, + ); + }, + ); + + test( + 'throws AssertionError ' + 'when rounds is negative', + () { + expect( + () => GameState( + score: 1, + multiplier: 1, + rounds: -1, bonusHistory: const [], ), throwsAssertionError, @@ -68,10 +89,11 @@ void main() { group('isGameOver', () { test( 'is true ' - 'when no balls are left', () { + 'when no rounds are left', () { const gameState = GameState( - balls: 0, score: 0, + multiplier: 1, + rounds: 0, bonusHistory: [], ); expect(gameState.isGameOver, isTrue); @@ -79,10 +101,11 @@ void main() { test( 'is false ' - 'when one 1 ball left', () { + 'when one 1 round left', () { const gameState = GameState( - balls: 1, score: 0, + multiplier: 1, + rounds: 1, bonusHistory: [], ); expect(gameState.isGameOver, isFalse); @@ -95,8 +118,9 @@ void main() { 'when scored is decreased', () { const gameState = GameState( - balls: 0, score: 2, + multiplier: 1, + rounds: 3, bonusHistory: [], ); expect( @@ -111,8 +135,9 @@ void main() { 'when no argument specified', () { const gameState = GameState( - balls: 0, score: 2, + multiplier: 1, + rounds: 3, bonusHistory: [], ); expect( @@ -128,12 +153,14 @@ void main() { () { const gameState = GameState( score: 2, - balls: 0, + multiplier: 1, + rounds: 3, bonusHistory: [], ); final otherGameState = GameState( score: gameState.score + 1, - balls: gameState.balls + 1, + multiplier: gameState.multiplier + 1, + rounds: gameState.rounds + 1, bonusHistory: const [GameBonus.googleWord], ); expect(gameState, isNot(equals(otherGameState))); @@ -141,7 +168,8 @@ void main() { expect( gameState.copyWith( score: otherGameState.score, - balls: otherGameState.balls, + multiplier: otherGameState.multiplier, + rounds: otherGameState.rounds, bonusHistory: otherGameState.bonusHistory, ), equals(otherGameState), diff --git a/test/game/components/alien_zone_test.dart b/test/game/components/alien_zone_test.dart deleted file mode 100644 index 7feaded8..00000000 --- a/test/game/components/alien_zone_test.dart +++ /dev/null @@ -1,48 +0,0 @@ -// ignore_for_file: cascade_invocations - -import 'package:flame_test/flame_test.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:pinball/game/game.dart'; -import 'package:pinball_components/pinball_components.dart'; -import 'package:pinball_flame/pinball_flame.dart'; - -import '../../helpers/helpers.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - final assets = [ - Assets.images.alienBumper.a.active.keyName, - Assets.images.alienBumper.a.inactive.keyName, - Assets.images.alienBumper.b.active.keyName, - Assets.images.alienBumper.b.inactive.keyName, - ]; - final flameTester = FlameTester( - () => EmptyPinballTestGame(assets: assets), - ); - - group('AlienZone', () { - flameTester.test( - 'loads correctly', - (game) async { - await game.addFromBlueprint(AlienZone()); - await game.ready(); - }, - ); - - group('loads', () { - flameTester.test( - 'two AlienBumper', - (game) async { - final alienZone = AlienZone(); - await game.addFromBlueprint(alienZone); - await game.ready(); - - expect( - game.descendants().whereType().length, - equals(2), - ); - }, - ); - }); - }); -} diff --git a/test/game/components/android_acres_test.dart b/test/game/components/android_acres_test.dart new file mode 100644 index 00000000..419524c6 --- /dev/null +++ b/test/game/components/android_acres_test.dart @@ -0,0 +1,90 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_flame/pinball_flame.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + final assets = [ + Assets.images.spaceship.ramp.boardOpening.keyName, + Assets.images.spaceship.ramp.railingForeground.keyName, + Assets.images.spaceship.ramp.railingBackground.keyName, + Assets.images.spaceship.ramp.main.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.spaceship.rail.main.keyName, + Assets.images.spaceship.rail.exit.keyName, + Assets.images.androidBumper.a.lit.keyName, + Assets.images.androidBumper.a.dimmed.keyName, + Assets.images.androidBumper.b.lit.keyName, + Assets.images.androidBumper.b.dimmed.keyName, + ]; + final flameTester = FlameTester( + () => EmptyPinballTestGame(assets: assets), + ); + + group('AndroidAcres', () { + flameTester.test( + 'loads correctly', + (game) async { + await game.addFromBlueprint(AndroidAcres()); + await game.ready(); + }, + ); + + group('loads', () { + flameTester.test( + 'a Spaceship', + (game) async { + expect( + AndroidAcres().blueprints.whereType().single, + isNotNull, + ); + }, + ); + + flameTester.test( + 'a SpaceshipRamp', + (game) async { + expect( + AndroidAcres().blueprints.whereType().single, + isNotNull, + ); + }, + ); + + flameTester.test( + 'a SpaceshipRail', + (game) async { + expect( + AndroidAcres().blueprints.whereType().single, + isNotNull, + ); + }, + ); + + flameTester.test( + 'two AndroidBumper', + (game) async { + final androidZone = AndroidAcres(); + await game.addFromBlueprint(androidZone); + await game.ready(); + + expect( + game.descendants().whereType().length, + equals(2), + ); + }, + ); + }); + }); +} diff --git a/test/game/components/board_test.dart b/test/game/components/board_test.dart index a73d7a50..63b7251b 100644 --- a/test/game/components/board_test.dart +++ b/test/game/components/board_test.dart @@ -105,18 +105,6 @@ void main() { expect(flutterForest.length, equals(1)); }, ); - - flameTester.test( - 'one ChromeDino', - (game) async { - final board = Board(); - await game.ready(); - await game.ensureAdd(board); - - final chromeDino = board.descendants().whereType(); - expect(chromeDino.length, equals(1)); - }, - ); }); }); } diff --git a/test/game/components/controlled_ball_test.dart b/test/game/components/controlled_ball_test.dart index e615d508..c84ddaa7 100644 --- a/test/game/components/controlled_ball_test.dart +++ b/test/game/components/controlled_ball_test.dart @@ -53,16 +53,39 @@ void main() { }); flameBlocTester.testGameWidget( - 'lost adds BallLost to GameBloc', + "lost doesn't adds RoundLost to GameBloc " + 'when there are balls left', + setUp: (game, tester) async { + final controller = BallController(ball); + await ball.add(controller); + await game.ensureAdd(ball); + + final otherBall = Ball(baseColor: const Color(0xFF00FFFF)); + final otherController = BallController(otherBall); + await otherBall.add(otherController); + await game.ensureAdd(otherBall); + + controller.lost(); + await game.ready(); + }, + verify: (game, tester) async { + verifyNever(() => gameBloc.add(const RoundLost())); + }, + ); + + flameBlocTester.testGameWidget( + 'lost adds RoundLost to GameBloc ' + 'when there are no balls left', setUp: (game, tester) async { final controller = BallController(ball); await ball.add(controller); await game.ensureAdd(ball); controller.lost(); + await game.ready(); }, verify: (game, tester) async { - verify(() => gameBloc.add(const BallLost())).called(1); + verify(() => gameBloc.add(const RoundLost())).called(1); }, ); diff --git a/test/game/components/controlled_flipper_test.dart b/test/game/components/controlled_flipper_test.dart index 2f970254..36a8161b 100644 --- a/test/game/components/controlled_flipper_test.dart +++ b/test/game/components/controlled_flipper_test.dart @@ -25,7 +25,8 @@ void main() { final bloc = MockGameBloc(); const state = GameState( score: 0, - balls: 0, + multiplier: 1, + rounds: 0, bonusHistory: [], ); whenListen(bloc, Stream.value(state), initialState: state); diff --git a/test/game/components/controlled_plunger_test.dart b/test/game/components/controlled_plunger_test.dart index eee2bcb0..a39bdef6 100644 --- a/test/game/components/controlled_plunger_test.dart +++ b/test/game/components/controlled_plunger_test.dart @@ -20,7 +20,8 @@ void main() { final bloc = MockGameBloc(); const state = GameState( score: 0, - balls: 0, + multiplier: 1, + rounds: 0, bonusHistory: [], ); whenListen(bloc, Stream.value(state), initialState: state); diff --git a/test/game/components/game_flow_controller_test.dart b/test/game/components/game_flow_controller_test.dart index 3de04b90..ef93892c 100644 --- a/test/game/components/game_flow_controller_test.dart +++ b/test/game/components/game_flow_controller_test.dart @@ -15,7 +15,8 @@ void main() { test('is true when the game over state has changed', () { final state = GameState( score: 10, - balls: 0, + multiplier: 1, + rounds: 0, bonusHistory: const [], ); @@ -66,7 +67,8 @@ void main() { gameFlowController.onNewState( GameState( score: 10, - balls: 0, + multiplier: 1, + rounds: 0, bonusHistory: const [], ), ); diff --git a/test/game/components/scoring_behavior_test.dart b/test/game/components/scoring_behavior_test.dart index d5e706b0..4fb07f40 100644 --- a/test/game/components/scoring_behavior_test.dart +++ b/test/game/components/scoring_behavior_test.dart @@ -43,7 +43,8 @@ void main() { bloc = MockGameBloc(); const state = GameState( score: 0, - balls: 0, + multiplier: 1, + rounds: 3, bonusHistory: [], ); whenListen(bloc, Stream.value(state), initialState: state); diff --git a/test/game/components/wall_test.dart b/test/game/components/wall_test.dart index 2905ab9a..16f7ce34 100644 --- a/test/game/components/wall_test.dart +++ b/test/game/components/wall_test.dart @@ -146,9 +146,9 @@ void main() { }, ); - flameTester.test( + flameBlocTester.testGameWidget( 'when ball is debug', - (game) async { + setUp: (game, tester) async { final ball = ControlledBall.debug(); final wall = BottomWall(); await game.ensureAddAll([ball, wall]); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index b265c0aa..2fdbe6c4 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -13,28 +13,54 @@ import '../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); final assets = [ - Assets.images.dash.bumper.main.active.keyName, - Assets.images.dash.bumper.main.inactive.keyName, + 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.backboard.backboardScores.keyName, + Assets.images.backboard.backboardGameOver.keyName, + Assets.images.backboard.display.keyName, + Assets.images.ball.ball.keyName, + Assets.images.ball.flameEffect.keyName, + Assets.images.baseboard.left.keyName, + Assets.images.baseboard.right.keyName, + Assets.images.boundary.bottom.keyName, + Assets.images.boundary.outer.keyName, + Assets.images.boundary.outerBottom.keyName, + Assets.images.dino.animatronic.mouth.keyName, + Assets.images.dino.animatronic.head.keyName, + Assets.images.dino.topWall.keyName, + Assets.images.dino.bottomWall.keyName, + Assets.images.dash.animatronic.keyName, Assets.images.dash.bumper.a.active.keyName, Assets.images.dash.bumper.a.inactive.keyName, Assets.images.dash.bumper.b.active.keyName, Assets.images.dash.bumper.b.inactive.keyName, - Assets.images.dash.animatronic.keyName, + Assets.images.dash.bumper.main.active.keyName, + Assets.images.dash.bumper.main.inactive.keyName, + Assets.images.flipper.left.keyName, + Assets.images.flipper.right.keyName, + Assets.images.googleWord.letter1.keyName, + Assets.images.googleWord.letter2.keyName, + Assets.images.googleWord.letter3.keyName, + Assets.images.googleWord.letter4.keyName, + Assets.images.googleWord.letter5.keyName, + Assets.images.googleWord.letter6.keyName, + Assets.images.kicker.left.keyName, + Assets.images.kicker.right.keyName, + Assets.images.launchRamp.ramp.keyName, + Assets.images.launchRamp.foregroundRailing.keyName, + Assets.images.launchRamp.backgroundRailing.keyName, + Assets.images.plunger.plunger.keyName, + Assets.images.plunger.rocket.keyName, Assets.images.signpost.inactive.keyName, Assets.images.signpost.active1.keyName, Assets.images.signpost.active2.keyName, Assets.images.signpost.active3.keyName, - Assets.images.alienBumper.a.active.keyName, - Assets.images.alienBumper.a.inactive.keyName, - Assets.images.alienBumper.b.active.keyName, - Assets.images.alienBumper.b.inactive.keyName, - 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.animatronic.keyName, + Assets.images.slingshot.upper.keyName, + Assets.images.slingshot.lower.keyName, + Assets.images.spaceship.saucer.keyName, + Assets.images.spaceship.bridge.keyName, Assets.images.spaceship.ramp.boardOpening.keyName, Assets.images.spaceship.ramp.railingForeground.keyName, Assets.images.spaceship.ramp.railingBackground.keyName, @@ -45,18 +71,26 @@ void main() { Assets.images.spaceship.ramp.arrow.active3.keyName, Assets.images.spaceship.ramp.arrow.active4.keyName, Assets.images.spaceship.ramp.arrow.active5.keyName, - Assets.images.baseboard.left.keyName, - Assets.images.baseboard.right.keyName, - Assets.images.flipper.left.keyName, - Assets.images.flipper.right.keyName, - Assets.images.boundary.outer.keyName, - Assets.images.boundary.outerBottom.keyName, - Assets.images.boundary.bottom.keyName, - Assets.images.slingshot.upper.keyName, - Assets.images.slingshot.lower.keyName, - Assets.images.dino.dinoLandTop.keyName, - Assets.images.dino.dinoLandBottom.keyName, + Assets.images.spaceship.rail.main.keyName, + Assets.images.spaceship.rail.exit.keyName, + 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.animatronic.keyName, + Assets.images.sparky.computer.top.keyName, + Assets.images.sparky.computer.base.keyName, + Assets.images.sparky.animatronic.keyName, + Assets.images.sparky.bumper.a.inactive.keyName, + Assets.images.sparky.bumper.a.active.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, ]; + final flameTester = FlameTester( () => PinballTestGame(assets: assets), ); @@ -72,7 +106,6 @@ void main() { 'has only one BottomWall', (game) async { await game.ready(); - expect( game.children.whereType().length, equals(1), @@ -91,34 +124,34 @@ void main() { }, ); - flameTester.test('has one Board', (game) async { - await game.ready(); - expect( - game.children.whereType().length, - equals(1), - ); - }); + flameTester.test( + 'has one Board', + (game) async { + await game.ready(); + expect( + game.children.whereType().length, + equals(1), + ); + }, + ); + + flameTester.test( + 'one GoogleWord', + (game) async { + await game.ready(); + expect(game.children.whereType().length, equals(1)); + }, + ); group('controller', () { - // TODO(alestiago): Write test to be controller agnostic. group('listenWhen', () { - late GameBloc gameBloc; - - setUp(() { - gameBloc = GameBloc(); - }); - - final flameBlocTester = FlameBlocTester( - gameBuilder: EmptyPinballTestGame.new, - blocBuilder: () => gameBloc, - // assets: assets, - ); - - flameBlocTester.testGameWidget( - 'listens when all balls are lost and there are more than 0 balls', + flameTester.testGameWidget( + 'listens when all balls are lost and there are more than 0 rounds', setUp: (game, tester) async { + // TODO(ruimiguel): check why testGameWidget doesn't add any ball + // to the game. Test needs to have no balls, so fortunately works. final newState = MockGameState(); - when(() => newState.balls).thenReturn(2); + when(() => newState.isGameOver).thenReturn(false); game.descendants().whereType().forEach( (ball) => ball.controller.lost(), ); @@ -135,10 +168,10 @@ void main() { "doesn't listen when some balls are left", (game) async { final newState = MockGameState(); - when(() => newState.balls).thenReturn(1); + when(() => newState.isGameOver).thenReturn(false); expect( - game.descendants().whereType().length, + game.descendants().whereType().length, greaterThan(0), ); expect( @@ -148,19 +181,20 @@ void main() { }, ); - flameBlocTester.test( - "doesn't listen when no balls left", - (game) async { + flameTester.testGameWidget( + "doesn't listen when game is over", + setUp: (game, tester) async { + // TODO(ruimiguel): check why testGameWidget doesn't add any ball + // to the game. Test needs to have no balls, so fortunately works. final newState = MockGameState(); - when(() => newState.balls).thenReturn(0); - + when(() => newState.isGameOver).thenReturn(true); game.descendants().whereType().forEach( (ball) => ball.controller.lost(), ); await game.ready(); expect( - game.descendants().whereType().isEmpty, + game.descendants().whereType().isEmpty, isTrue, ); expect( @@ -177,14 +211,13 @@ void main() { flameTester.test( 'spawns a ball', (game) async { - await game.ready(); final previousBalls = - game.descendants().whereType().toList(); + game.descendants().whereType().toList(); game.controller.onNewState(MockGameState()); await game.ready(); final currentBalls = - game.descendants().whereType().toList(); + game.descendants().whereType().toList(); expect( currentBalls.length, @@ -199,57 +232,26 @@ void main() { }); group('DebugPinballGame', () { - debugModeFlameTester.test('adds a ball on tap up', (game) async { - await game.ready(); - - final eventPosition = MockEventPosition(); - when(() => eventPosition.game).thenReturn(Vector2.all(10)); - - final tapUpEvent = MockTapUpInfo(); - when(() => tapUpEvent.eventPosition).thenReturn(eventPosition); - - final previousBalls = game.descendants().whereType().toList(); - - game.onTapUp(tapUpEvent); - await game.ready(); + debugModeFlameTester.test( + 'adds a ball on tap up', + (game) async { + final eventPosition = MockEventPosition(); + when(() => eventPosition.game).thenReturn(Vector2.all(10)); - expect( - game.children.whereType().length, - equals(previousBalls.length + 1), - ); - }); - - group('controller', () { - late GameBloc gameBloc; + final tapUpEvent = MockTapUpInfo(); + when(() => tapUpEvent.eventPosition).thenReturn(eventPosition); - setUp(() { - gameBloc = GameBloc(); - }); - - final debugModeFlameBlocTester = - FlameBlocTester( - gameBuilder: DebugPinballTestGame.new, - blocBuilder: () => gameBloc, - assets: assets, - ); + final previousBalls = + game.descendants().whereType().toList(); - debugModeFlameBlocTester.testGameWidget( - 'ignores debug balls', - setUp: (game, tester) async { - final newState = MockGameState(); - when(() => newState.balls).thenReturn(1); - - await game.ready(); - game.children.removeWhere((component) => component is Ball); - await game.ready(); - await game.ensureAdd(ControlledBall.debug()); + game.onTapUp(tapUpEvent); + await game.ready(); - expect( - game.controller.listenWhen(MockGameState(), newState), - isTrue, - ); - }, - ); - }); + expect( + game.children.whereType().length, + equals(previousBalls.length + 1), + ); + }, + ); }); } diff --git a/test/game/view/widgets/game_hud_test.dart b/test/game/view/widgets/game_hud_test.dart index fe8bd092..d101d06e 100644 --- a/test/game/view/widgets/game_hud_test.dart +++ b/test/game/view/widgets/game_hud_test.dart @@ -28,7 +28,8 @@ void main() { const initialState = GameState( score: 1000, - balls: 2, + multiplier: 1, + rounds: 1, bonusHistory: [], ); diff --git a/test/game/view/widgets/round_count_display_test.dart b/test/game/view/widgets/round_count_display_test.dart index 8281ce83..dfa28869 100644 --- a/test/game/view/widgets/round_count_display_test.dart +++ b/test/game/view/widgets/round_count_display_test.dart @@ -11,7 +11,8 @@ void main() { late GameBloc gameBloc; const initialState = GameState( score: 0, - balls: 3, + multiplier: 1, + rounds: 3, bonusHistory: [], ); @@ -37,7 +38,7 @@ void main() { testWidgets('two active round indicator', (tester) async { final state = initialState.copyWith( - balls: 2, + rounds: 2, ); whenListen( gameBloc, @@ -68,7 +69,7 @@ void main() { testWidgets('one active round indicator', (tester) async { final state = initialState.copyWith( - balls: 1, + rounds: 1, ); whenListen( gameBloc, diff --git a/test/game/view/widgets/score_view_test.dart b/test/game/view/widgets/score_view_test.dart index 0d3af694..63f7d1c5 100644 --- a/test/game/view/widgets/score_view_test.dart +++ b/test/game/view/widgets/score_view_test.dart @@ -15,7 +15,8 @@ void main() { const score = 123456789; const initialState = GameState( score: score, - balls: 1, + multiplier: 1, + rounds: 1, bonusHistory: [], ); @@ -46,7 +47,7 @@ void main() { stateController.add( initialState.copyWith( - balls: 0, + rounds: 0, ), ); diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index da6f5124..b58dc619 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -82,6 +82,6 @@ class MockActiveOverlaysNotifier extends Mock class MockGameFlowController extends Mock implements GameFlowController {} -class MockAlienBumper extends Mock implements AlienBumper {} +class MockAndroidBumper extends Mock implements AndroidBumper {} class MockSparkyBumper extends Mock implements SparkyBumper {}