feat: connect game ball to theme selection

pull/375/head
Allison Ryan 3 years ago
parent 0502d97400
commit 910b2ce2da

@ -1,9 +1,9 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart';
/// Spawns a new [Ball] into the game when all balls are lost and still
/// [GameStatus.playing].
@ -23,9 +23,10 @@ class BallSpawningBehavior extends Component
void onNewState(GameState state) {
final plunger = gameRef.descendants().whereType<Plunger>().single;
final canvas = gameRef.descendants().whereType<ZCanvasComponent>().single;
final characterTheme = readProvider<CharacterTheme>();
final ball = ControlledBall.launch(characterTheme: characterTheme)
..initialPosition = Vector2(
final characterThemeBloc = readProvider<CharacterThemeCubit>();
final ball = ControlledBall.launch(
characterTheme: characterThemeBloc.state.characterTheme,
)..initialPosition = Vector2(
plunger.body.position.x,
plunger.body.position.y - Ball.size.y,
);

@ -0,0 +1,29 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// Updates the launch [Ball] to reflect character selections.
class BallThemingBehavior extends Component
with
FlameBlocListenable<CharacterThemeCubit, CharacterThemeState>,
HasGameRef {
@override
void onNewState(CharacterThemeState state) {
final ballsInGame = gameRef.descendants().whereType<Ball>();
if (ballsInGame.isNotEmpty) {
gameRef.removeAll(ballsInGame);
}
final plunger = gameRef.descendants().whereType<Plunger>().single;
final canvas = gameRef.descendants().whereType<ZCanvasComponent>().single;
final ball = ControlledBall.launch(characterTheme: state.characterTheme)
..initialPosition = Vector2(
plunger.body.position.x,
plunger.body.position.y - Ball.size.y + 1.1,
);
canvas.add(ball);
}
}

@ -1,4 +1,5 @@
export 'ball_spawning_behavior.dart';
export 'ball_theming_behavior.dart';
export 'bumper_noise_behavior.dart';
export 'camera_focusing_behavior.dart';
export 'scoring_behavior.dart';

@ -1,9 +1,9 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart';
/// Bonus obtained at the [FlutterForest].
///
@ -43,7 +43,8 @@ class FlutterForestBonusBehavior extends Component
bloc.add(const BonusActivated(GameBonus.dashNest));
canvas.add(
ControlledBall.bonus(
characterTheme: readProvider<CharacterTheme>(),
characterTheme:
readProvider<CharacterThemeCubit>().state.characterTheme,
)..initialPosition = Vector2(29.2, -24.5),
);
animatronic.playing = true;

@ -1,9 +1,9 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart';
/// Listens to the [GameBloc] and updates the game accordingly.
class GameBlocStatusListener extends Component
@ -26,7 +26,8 @@ class GameBlocStatusListener extends Component
readProvider<PinballPlayer>().play(PinballAudio.gameOverVoiceOver);
gameRef.descendants().whereType<Backbox>().first.requestInitials(
score: state.displayScore,
character: readProvider<CharacterTheme>(),
character:
readProvider<CharacterThemeCubit>().state.characterTheme,
);
break;
}

@ -11,22 +11,22 @@ import 'package:leaderboard_repository/leaderboard_repository.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart';
class PinballGame extends PinballForge2DGame
with HasKeyboardHandlerComponents, MultiTouchTapDetector {
PinballGame({
required CharacterTheme characterTheme,
required CharacterThemeCubit characterThemeBloc,
required this.leaderboardRepository,
required GameBloc gameBloc,
required AppLocalizations l10n,
required PinballPlayer player,
}) : _gameBloc = gameBloc,
_player = player,
_characterTheme = characterTheme,
_characterThemeBloc = characterThemeBloc,
_l10n = l10n,
super(
gravity: Vector2(0, 30),
@ -40,7 +40,7 @@ class PinballGame extends PinballForge2DGame
@override
Color backgroundColor() => Colors.transparent;
final CharacterTheme _characterTheme;
final CharacterThemeCubit _characterThemeBloc;
final PinballPlayer _player;
@ -53,19 +53,27 @@ class PinballGame extends PinballForge2DGame
@override
Future<void> onLoad() async {
await add(
FlameBlocProvider<GameBloc, GameState>.value(
value: _gameBloc,
FlameMultiBlocProvider(
providers: [
FlameBlocProvider<GameBloc, GameState>.value(
value: _gameBloc,
),
FlameBlocProvider<CharacterThemeCubit, CharacterThemeState>.value(
value: _characterThemeBloc,
),
],
children: [
MultiFlameProvider(
providers: [
FlameProvider<PinballPlayer>.value(_player),
FlameProvider<CharacterTheme>.value(_characterTheme),
FlameProvider<CharacterThemeCubit>.value(_characterThemeBloc),
FlameProvider<LeaderboardRepository>.value(leaderboardRepository),
FlameProvider<AppLocalizations>.value(_l10n),
],
children: [
GameBlocStatusListener(),
BallSpawningBehavior(),
BallThemingBehavior(),
CameraFocusingBehavior(),
CanvasComponent(
onSpritePainted: (paint) {
@ -157,13 +165,13 @@ class PinballGame extends PinballForge2DGame
class DebugPinballGame extends PinballGame with FPSCounter, PanDetector {
DebugPinballGame({
required CharacterTheme characterTheme,
required CharacterThemeCubit characterThemeBloc,
required LeaderboardRepository leaderboardRepository,
required AppLocalizations l10n,
required PinballPlayer player,
required GameBloc gameBloc,
}) : super(
characterTheme: characterTheme,
characterThemeBloc: characterThemeBloc,
player: player,
leaderboardRepository: leaderboardRepository,
l10n: l10n,

@ -35,8 +35,7 @@ class PinballGamePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final characterTheme =
context.read<CharacterThemeCubit>().state.characterTheme;
final characterThemeBloc = context.read<CharacterThemeCubit>();
final player = context.read<PinballPlayer>();
final leaderboardRepository = context.read<LeaderboardRepository>();
@ -47,14 +46,14 @@ class PinballGamePage extends StatelessWidget {
final gameBloc = context.read<GameBloc>();
final game = isDebugMode
? DebugPinballGame(
characterTheme: characterTheme,
characterThemeBloc: characterThemeBloc,
player: player,
leaderboardRepository: leaderboardRepository,
l10n: context.l10n,
gameBloc: gameBloc,
)
: PinballGame(
characterTheme: characterTheme,
characterThemeBloc: characterThemeBloc,
player: player,
leaderboardRepository: leaderboardRepository,
l10n: context.l10n,

@ -6,6 +6,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/behaviors/ball_spawning_behavior.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart' as theme;
@ -25,8 +26,8 @@ class _TestGame extends Forge2DGame {
FlameBlocProvider<GameBloc, GameState>.value(
value: gameBloc ?? GameBloc(),
children: [
FlameProvider<theme.CharacterTheme>.value(
const theme.DashTheme(),
FlameProvider<CharacterThemeCubit>.value(
CharacterThemeCubit(),
children: children,
),
],

@ -0,0 +1,86 @@
// ignore_for_file: cascade_invocations
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart' as theme;
class _TestGame extends Forge2DGame {
@override
Future<void> onLoad() async {
images.prefix = '';
await images.loadAll([
theme.Assets.images.dash.ball.keyName,
theme.Assets.images.dino.ball.keyName,
]);
}
Future<void> pump(
List<Component> children, {
CharacterThemeCubit? characterThemeBloc,
}) async {
await ensureAdd(
FlameBlocProvider<CharacterThemeCubit, CharacterThemeState>.value(
value: characterThemeBloc ?? CharacterThemeCubit(),
children: children,
),
);
}
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group(
'BallThemingBehavior',
() {
final flameTester = FlameTester(_TestGame.new);
test('can be instantiated', () {
expect(
BallThemingBehavior(),
isA<BallThemingBehavior>(),
);
});
flameTester.test(
'loads',
(game) async {
final behavior = BallThemingBehavior();
await game.pump([behavior]);
expect(game.descendants(), contains(behavior));
},
);
flameTester.test(
'onNewState replaces the current ball with a new ball',
(game) async {
final behavior = BallThemingBehavior();
await game.pump([
behavior,
ZCanvasComponent(),
Plunger.test(compressionDistance: 10),
Ball(),
]);
expect(game.descendants().whereType<Ball>(), isNotEmpty);
final dashBall = game.descendants().whereType<Ball>().single;
const dinoTheme = CharacterThemeState(theme.DinoTheme());
behavior.onNewState(dinoTheme);
await game.ready();
expect(game.descendants().whereType<Ball>(), isNotEmpty);
final dinoBall = game.descendants().whereType<Ball>().single;
expect(dinoBall != dashBall, isTrue);
},
);
},
);
}

@ -7,6 +7,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/components/flutter_forest/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart' as theme;
@ -29,8 +30,8 @@ class _TestGame extends Forge2DGame {
FlameBlocProvider<GameBloc, GameState>.value(
value: gameBloc,
children: [
FlameProvider<theme.CharacterTheme>.value(
const theme.DashTheme(),
FlameProvider<CharacterThemeCubit>.value(
CharacterThemeCubit(),
children: [
ZCanvasComponent(
children: [child],

@ -8,10 +8,10 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:leaderboard_repository/leaderboard_repository.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart' as theme;
class _TestGame extends Forge2DGame {
@override
@ -33,8 +33,8 @@ class _TestGame extends Forge2DGame {
FlameProvider<PinballPlayer>.value(
pinballPlayer ?? _MockPinballPlayer(),
),
FlameProvider<theme.CharacterTheme>.value(
const theme.DashTheme(),
FlameProvider<CharacterThemeCubit>.value(
CharacterThemeCubit(),
),
],
children: children,

@ -13,14 +13,14 @@ import 'package:leaderboard_repository/src/leaderboard_repository.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_audio/src/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_theme/pinball_theme.dart' as theme;
class _TestPinballGame extends PinballGame {
_TestPinballGame()
: super(
characterTheme: const theme.DashTheme(),
characterThemeBloc: _MockCharacterThemeCubit(),
leaderboardRepository: _MockLeaderboardRepository(),
gameBloc: GameBloc(),
l10n: _MockAppLocalizations(),
@ -39,7 +39,7 @@ class _TestPinballGame extends PinballGame {
class _TestDebugPinballGame extends DebugPinballGame {
_TestDebugPinballGame()
: super(
characterTheme: const theme.DashTheme(),
characterThemeBloc: _MockCharacterThemeCubit(),
leaderboardRepository: _MockLeaderboardRepository(),
gameBloc: GameBloc(),
l10n: _MockAppLocalizations(),
@ -57,6 +57,12 @@ class _TestDebugPinballGame extends DebugPinballGame {
class _MockGameBloc extends Mock implements GameBloc {}
class _MockCharacterThemeCubit extends Mock implements CharacterThemeCubit {
@override
Stream<CharacterThemeState> get stream =>
const Stream<CharacterThemeState>.empty();
}
class _MockAppLocalizations extends Mock implements AppLocalizations {}
class _MockEventPosition extends Mock implements EventPosition {}
@ -109,6 +115,17 @@ void main() {
},
);
flameTester.test(
'has only one BallThemingBehavior',
(game) async {
await game.ready();
expect(
game.descendants().whereType<BallThemingBehavior>().length,
equals(1),
);
},
);
flameTester.test(
'has only one Drain',
(game) async {

@ -12,14 +12,13 @@ import 'package:pinball/l10n/l10n.dart';
import 'package:pinball/select_character/select_character.dart';
import 'package:pinball/start_game/start_game.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_theme/pinball_theme.dart' as theme;
import '../../helpers/helpers.dart';
class _TestPinballGame extends PinballGame {
_TestPinballGame()
: super(
characterTheme: const theme.DashTheme(),
characterThemeBloc: _MockCharacterThemeCubit(),
leaderboardRepository: _MockLeaderboardRepository(),
gameBloc: GameBloc(),
l10n: _MockAppLocalizations(),

Loading…
Cancel
Save