feat: made LaunchedBallControler react to states

pull/116/head
alestiago 4 years ago
parent 5db0c83581
commit 1e61335ac4

@ -55,9 +55,6 @@ class GameState extends Equatable {
/// Determines when the game is over.
bool get isGameOver => balls == 0;
/// Determines when the player has only one ball left.
bool get isLastBall => balls == 1;
/// Shortcut method to check if the given [i]
/// is activated.
bool isLetterActivated(int i) => activatedBonusLetters.contains(i);

@ -1,4 +1,5 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/forge2d_game.dart';
import 'package:flutter/material.dart';
import 'package:pinball/flame/flame.dart';
@ -68,22 +69,34 @@ class BonusBallController extends BallController {
}
}
/// {@template launched_ball_controller}
/// {@macro ball_controller}
///
/// A [LaunchedBallController] changes the [GameState.balls] count.
/// {@endtemplate}
class LaunchedBallController extends BallController
with HasGameRef<PinballGame> {
/// {@macro ball_controller}
with HasGameRef<PinballGame>, BlocComponent<GameBloc, GameState> {
/// {@macro launched_ball_controller}
LaunchedBallController(Ball<Forge2DGame> ball) : super(ball);
@override
bool listenWhen(GameState? previousState, GameState newState) {
return (previousState?.balls ?? 0) < newState.balls;
}
@override
void onNewState(GameState state) {
super.onNewState(state);
component.shouldRemove = true;
if (state.balls > 0) gameRef.spawnBall();
}
/// Removes the [Ball] from a [PinballGame]; spawning a new [Ball] if
/// any are left.
///
/// {@macro ball_controller_lost}
@override
void lost() {
final bloc = gameRef.read<GameBloc>()..add(const BallLost());
// TODO(alestiago): Consider the use of onNewState instead.
final shouldBallRespwan = !bloc.state.isLastBall && !bloc.state.isGameOver;
if (shouldBallRespwan) gameRef.spawnBall();
gameRef.read<GameBloc>().add(const BallLost());
}
}

@ -39,99 +39,131 @@ void main() {
});
group('LaunchedBallController', () {
group('lost', () {
late GameBloc gameBloc;
late Ball ball;
setUp(() {
gameBloc = MockGameBloc();
ball = Ball(baseColor: const Color(0xFF00FFFF));
whenListen(
gameBloc,
const Stream<GameState>.empty(),
initialState: const GameState.initial(),
);
});
late Ball ball;
late GameBloc gameBloc;
final tester = flameBlocTester<PinballGame>(
game: PinballGameTest.create,
gameBloc: () => gameBloc,
setUp(() {
ball = Ball(baseColor: const Color(0xFF00FFFF));
gameBloc = MockGameBloc();
whenListen(
gameBloc,
const Stream<GameState>.empty(),
initialState: const GameState.initial(),
);
});
tester.testGameWidget(
'removes ball',
final tester = flameBlocTester<PinballGame>(
game: PinballGameTest.create,
gameBloc: () => gameBloc,
);
flameTester.testGameWidget(
'lost adds BallLost to GameBloc',
setUp: (game, tester) async {
final controller = LaunchedBallController(ball);
await ball.add(controller);
await game.add(ball);
await game.ready();
controller.lost();
},
verify: (game, tester) async {
verify(() => gameBloc.add(const BallLost())).called(1);
},
);
group('listenWhen', () {
flameTester.testGameWidget(
'listens when a ball has been lost',
verify: (game, tester) async {
await game.add(ball);
final controller = LaunchedBallController(ball);
await ball.add(controller);
await game.add(ball);
await game.ready();
controller.lost();
await game.ready();
final previousState = MockGameState();
final newState = MockGameState();
when(() => previousState.balls).thenReturn(3);
when(() => newState.balls).thenReturn(2);
expect(game.contains(ball), isFalse);
expect(controller.listenWhen(previousState, newState), isTrue);
},
);
tester.testGameWidget(
'adds BallLost to GameBloc',
flameTester.testGameWidget(
'does not listen when a ball has not been lost',
verify: (game, tester) async {
final controller = LaunchedBallController(ball);
await ball.add(controller);
await game.add(ball);
await game.ready();
controller.lost();
final previousState = MockGameState();
final newState = MockGameState();
when(() => previousState.balls).thenReturn(3);
when(() => newState.balls).thenReturn(3);
verify(() => gameBloc.add(const BallLost())).called(1);
expect(controller.listenWhen(previousState, newState), isTrue);
},
);
});
group('onNewState', () {
setUp(() {
whenListen(
gameBloc,
const Stream<GameState>.empty(),
initialState: const GameState.initial(),
);
});
tester.testGameWidget(
'adds a new ball if the game is not over',
'removes ball',
setUp: (game, _) => game.ready(),
verify: (game, tester) async {
final controller = LaunchedBallController(ball);
await ball.add(controller);
await game.add(ball);
await game.ready();
final previousBalls = game.descendants().whereType<Ball>().length;
controller.lost();
await game.ready();
final currentBalls = game.descendants().whereType<Ball>().length;
final state = MockGameState();
when(() => state.balls).thenReturn(any());
controller.onNewState(MockGameState());
expect(previousBalls, equals(currentBalls));
expect(game.contains(ball), isFalse);
},
);
tester.testGameWidget(
'no ball is added on game over',
verify: (game, tester) async {
whenListen(
gameBloc,
const Stream<GameState>.empty(),
initialState: const GameState(
score: 10,
balls: 1,
activatedBonusLetters: [],
activatedDashNests: {},
bonusHistory: [],
),
);
'spawns a new ball when the ball is not the last one',
setUp: (game, tester) async {
final controller = LaunchedBallController(ball);
await ball.add(controller);
await game.add(ball);
await game.ready();
final previousBalls = game.descendants().whereType<Ball>().toList();
controller.lost();
final state = MockGameState();
when(() => state.balls).thenReturn(2);
controller.onNewState(state);
},
verify: (game, tester) async {
expect(game.contains(ball), isFalse);
},
);
tester.testGameWidget(
'does not spawn a new ball is the last one',
setUp: (game, tester) async {
final controller = LaunchedBallController(ball);
await game.add(ball);
await game.ready();
final currentBalls = game.descendants().whereType<Ball>().length;
expect(
currentBalls,
equals((previousBalls..remove(ball)).length),
);
final state = MockGameState();
when(() => state.balls).thenReturn(1);
controller.onNewState(state);
},
verify: (game, tester) async {
expect(game.contains(ball), isFalse);
},
);
});

Loading…
Cancel
Save