refactor: Flutter forest logic (#190)

* refactor: removed FlutterForest logic from Bloc

* refactor: removed ids from Bumpers

* refactor: improved casting

* feat: registered event

* refactor: removed unecessary properties

* feat: properly deactivated bumpers

* refactor: removed unused import

* feat: animated animatronic
pull/202/head
Alejandro Santiago 3 years ago committed by GitHub
parent 0dbd8f1600
commit 831d374705
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,7 +12,6 @@ class GameBloc extends Bloc<GameEvent, GameState> {
on<BallLost>(_onBallLost); on<BallLost>(_onBallLost);
on<Scored>(_onScored); on<Scored>(_onScored);
on<BonusActivated>(_onBonusActivated); on<BonusActivated>(_onBonusActivated);
on<DashNestActivated>(_onDashNestActivated);
on<SparkyTurboChargeActivated>(_onSparkyTurboChargeActivated); on<SparkyTurboChargeActivated>(_onSparkyTurboChargeActivated);
} }
@ -34,31 +33,6 @@ class GameBloc extends Bloc<GameEvent, GameState> {
); );
} }
void _onDashNestActivated(DashNestActivated event, Emitter emit) {
final newNests = {
...state.activatedDashNests,
event.nestId,
};
final achievedBonus = newNests.length == 3;
if (achievedBonus) {
emit(
state.copyWith(
balls: state.balls + 1,
activatedDashNests: {},
bonusHistory: [
...state.bonusHistory,
GameBonus.dashNest,
],
),
);
} else {
emit(
state.copyWith(activatedDashNests: newNests),
);
}
}
Future<void> _onSparkyTurboChargeActivated( Future<void> _onSparkyTurboChargeActivated(
SparkyTurboChargeActivated event, SparkyTurboChargeActivated event,
Emitter emit, Emitter emit,

@ -42,15 +42,6 @@ class BonusActivated extends GameEvent {
List<Object?> get props => [bonus]; List<Object?> get props => [bonus];
} }
class DashNestActivated extends GameEvent {
const DashNestActivated(this.nestId);
final String nestId;
@override
List<Object?> get props => [nestId];
}
class SparkyTurboChargeActivated extends GameEvent { class SparkyTurboChargeActivated extends GameEvent {
const SparkyTurboChargeActivated(); const SparkyTurboChargeActivated();

@ -23,14 +23,12 @@ class GameState extends Equatable {
required this.score, required this.score,
required this.balls, required this.balls,
required this.bonusHistory, required this.bonusHistory,
required this.activatedDashNests,
}) : assert(score >= 0, "Score can't be negative"), }) : assert(score >= 0, "Score can't be negative"),
assert(balls >= 0, "Number of balls can't be negative"); assert(balls >= 0, "Number of balls can't be negative");
const GameState.initial() const GameState.initial()
: score = 0, : score = 0,
balls = 3, balls = 3,
activatedDashNests = const {},
bonusHistory = const []; bonusHistory = const [];
/// The current score of the game. /// The current score of the game.
@ -41,9 +39,6 @@ class GameState extends Equatable {
/// When the number of balls is 0, the game is over. /// When the number of balls is 0, the game is over.
final int balls; final int balls;
/// Active dash nests.
final Set<String> activatedDashNests;
/// Holds the history of all the [GameBonus]es earned by the player during a /// Holds the history of all the [GameBonus]es earned by the player during a
/// PinballGame. /// PinballGame.
final List<GameBonus> bonusHistory; final List<GameBonus> bonusHistory;
@ -54,7 +49,6 @@ class GameState extends Equatable {
GameState copyWith({ GameState copyWith({
int? score, int? score,
int? balls, int? balls,
Set<String>? activatedDashNests,
List<GameBonus>? bonusHistory, List<GameBonus>? bonusHistory,
}) { }) {
assert( assert(
@ -65,7 +59,6 @@ class GameState extends Equatable {
return GameState( return GameState(
score: score ?? this.score, score: score ?? this.score,
balls: balls ?? this.balls, balls: balls ?? this.balls,
activatedDashNests: activatedDashNests ?? this.activatedDashNests,
bonusHistory: bonusHistory ?? this.bonusHistory, bonusHistory: bonusHistory ?? this.bonusHistory,
); );
} }
@ -74,7 +67,6 @@ class GameState extends Equatable {
List<Object?> get props => [ List<Object?> get props => [
score, score,
balls, balls,
activatedDashNests,
bonusHistory, bonusHistory,
]; ];
} }

@ -1,9 +1,7 @@
// ignore_for_file: avoid_renaming_method_parameters // ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
@ -15,9 +13,8 @@ import 'package:pinball_flame/pinball_flame.dart';
/// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest] /// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest]
/// is awarded, and the [BigDashNestBumper] releases a new [Ball]. /// is awarded, and the [BigDashNestBumper] releases a new [Ball].
/// {@endtemplate} /// {@endtemplate}
// TODO(alestiago): Make a [Blueprint] once [Blueprint] inherits from class FlutterForest extends Component
// [Component]. with Controls<_FlutterForestController>, HasGameRef<PinballGame> {
class FlutterForest extends Component with Controls<_FlutterForestController> {
/// {@macro flutter_forest} /// {@macro flutter_forest}
FlutterForest() { FlutterForest() {
controller = _FlutterForestController(this); controller = _FlutterForestController(this);
@ -26,17 +23,16 @@ class FlutterForest extends Component with Controls<_FlutterForestController> {
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
gameRef.addContactCallback(_DashNestBumperBallContactCallback());
final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, -58.3); final signPost = FlutterSignPost()..initialPosition = Vector2(8.35, -58.3);
final bigNest = _ControlledBigDashNestBumper( final bigNest = _BigDashNestBumper()
id: 'big_nest_bumper', ..initialPosition = Vector2(18.55, -59.35);
)..initialPosition = Vector2(18.55, -59.35); final smallLeftNest = _SmallDashNestBumper.a()
final smallLeftNest = _ControlledSmallDashNestBumper.a( ..initialPosition = Vector2(8.95, -51.95);
id: 'small_nest_bumper_a', final smallRightNest = _SmallDashNestBumper.b()
)..initialPosition = Vector2(8.95, -51.95); ..initialPosition = Vector2(23.3, -46.75);
final smallRightNest = _ControlledSmallDashNestBumper.b(
id: 'small_nest_bumper_b',
)..initialPosition = Vector2(23.3, -46.75);
final dashAnimatronic = DashAnimatronic()..position = Vector2(20, -66); final dashAnimatronic = DashAnimatronic()..position = Vector2(20, -66);
await addAll([ await addAll([
@ -50,31 +46,31 @@ class FlutterForest extends Component with Controls<_FlutterForestController> {
} }
class _FlutterForestController extends ComponentController<FlutterForest> class _FlutterForestController extends ComponentController<FlutterForest>
with BlocComponent<GameBloc, GameState>, HasGameRef<PinballGame> { with HasGameRef<PinballGame> {
_FlutterForestController(FlutterForest flutterForest) : super(flutterForest); _FlutterForestController(FlutterForest flutterForest) : super(flutterForest);
@override final _activatedBumpers = <DashNestBumper>{};
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(_ControlledDashNestBumperBallContactCallback());
}
@override void activateBumper(DashNestBumper dashNestBumper) {
bool listenWhen(GameState? previousState, GameState newState) { if (!_activatedBumpers.add(dashNestBumper)) return;
return (previousState?.bonusHistory.length ?? 0) <
newState.bonusHistory.length &&
newState.bonusHistory.last == GameBonus.dashNest;
}
@override dashNestBumper.activate();
void onNewState(GameState state) {
super.onNewState(state);
component.firstChild<DashAnimatronic>()?.playing = true; final activatedBonus = _activatedBumpers.length == 3;
if (activatedBonus) {
_addBonusBall(); _addBonusBall();
gameRef.read<GameBloc>().add(const BonusActivated(GameBonus.dashNest));
_activatedBumpers
..forEach((bumper) => bumper.deactivate())
..clear();
component.firstChild<DashAnimatronic>()?.playing = true;
}
} }
Future<void> _addBonusBall() async { Future<void> _addBonusBall() async {
// TODO(alestiago): Remove hardcoded duration.
await Future<void>.delayed(const Duration(milliseconds: 700)); await Future<void>.delayed(const Duration(milliseconds: 700));
await gameRef.add( await gameRef.add(
ControlledBall.bonus(theme: gameRef.theme) ControlledBall.bonus(theme: gameRef.theme)
@ -83,83 +79,29 @@ class _FlutterForestController extends ComponentController<FlutterForest>
} }
} }
class _ControlledBigDashNestBumper extends BigDashNestBumper // TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D
with Controls<DashNestBumperController>, ScorePoints { // ContactCallback process is enhanced.
_ControlledBigDashNestBumper({required String id}) : super() { class _BigDashNestBumper extends BigDashNestBumper with ScorePoints {
controller = DashNestBumperController(this, id: id);
}
@override @override
int get points => 20; int get points => 20;
} }
class _ControlledSmallDashNestBumper extends SmallDashNestBumper class _SmallDashNestBumper extends SmallDashNestBumper with ScorePoints {
with Controls<DashNestBumperController>, ScorePoints { _SmallDashNestBumper.a() : super.a();
_ControlledSmallDashNestBumper.a({required String id}) : super.a() {
controller = DashNestBumperController(this, id: id);
}
_ControlledSmallDashNestBumper.b({required String id}) : super.b() { _SmallDashNestBumper.b() : super.b();
controller = DashNestBumperController(this, id: id);
}
@override @override
int get points => 10; int get points => 20;
} }
/// {@template dash_nest_bumper_controller} class _DashNestBumperBallContactCallback
/// Controls a [DashNestBumper]. extends ContactCallback<DashNestBumper, Ball> {
/// {@endtemplate}
@visibleForTesting
class DashNestBumperController extends ComponentController<DashNestBumper>
with BlocComponent<GameBloc, GameState>, HasGameRef<PinballGame> {
/// {@macro dash_nest_bumper_controller}
DashNestBumperController(
DashNestBumper dashNestBumper, {
required this.id,
}) : super(dashNestBumper);
/// Unique identifier for the controlled [DashNestBumper].
///
/// Used to identify [DashNestBumper]s in [GameState.activatedDashNests].
final String id;
@override @override
bool listenWhen(GameState? previousState, GameState newState) { void begin(DashNestBumper dashNestBumper, _, __) {
final wasActive = previousState?.activatedDashNests.contains(id) ?? false; final parent = dashNestBumper.parent;
final isActive = newState.activatedDashNests.contains(id); if (parent is FlutterForest) {
parent.controller.activateBumper(dashNestBumper);
return wasActive != isActive;
} }
@override
void onNewState(GameState state) {
super.onNewState(state);
if (state.activatedDashNests.contains(id)) {
component.activate();
} else {
component.deactivate();
}
}
/// Registers when a [DashNestBumper] is hit by a [Ball].
///
/// Triggered by [_ControlledDashNestBumperBallContactCallback].
void hit() {
gameRef.read<GameBloc>().add(DashNestActivated(id));
}
}
/// Listens when a [Ball] bounces bounces against a [DashNestBumper].
class _ControlledDashNestBumperBallContactCallback
extends ContactCallback<Controls<DashNestBumperController>, Ball> {
@override
void begin(
Controls<DashNestBumperController> controlledDashNestBumper,
Ball _,
Contact __,
) {
controlledDashNestBumper.controller.hit();
} }
} }

@ -34,10 +34,7 @@ class BallScorePointsCallback extends ContactCallback<Ball, ScorePoints> {
ScorePoints scorePoints, ScorePoints scorePoints,
Contact __, Contact __,
) { ) {
_gameRef.read<GameBloc>().add( _gameRef.read<GameBloc>().add(Scored(points: scorePoints.points));
Scored(points: scorePoints.points),
);
_gameRef.audio.score(); _gameRef.audio.score();
} }
} }

@ -21,7 +21,6 @@ void main() {
const GameState( const GameState(
score: 0, score: 0,
balls: 2, balls: 2,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
), ),
], ],
@ -40,13 +39,11 @@ void main() {
const GameState( const GameState(
score: 2, score: 2,
balls: 3, balls: 3,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
), ),
const GameState( const GameState(
score: 5, score: 5,
balls: 3, balls: 3,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
), ),
], ],
@ -66,56 +63,22 @@ void main() {
const GameState( const GameState(
score: 0, score: 0,
balls: 2, balls: 2,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
), ),
const GameState( const GameState(
score: 0, score: 0,
balls: 1, balls: 1,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
), ),
const GameState( const GameState(
score: 0, score: 0,
balls: 0, balls: 0,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
), ),
], ],
); );
}); });
group('DashNestActivated', () {
blocTest<GameBloc, GameState>(
'adds the bonus when all nests are activated',
build: GameBloc.new,
act: (bloc) => bloc
..add(const DashNestActivated('0'))
..add(const DashNestActivated('1'))
..add(const DashNestActivated('2')),
expect: () => const [
GameState(
score: 0,
balls: 3,
activatedDashNests: {'0'},
bonusHistory: [],
),
GameState(
score: 0,
balls: 3,
activatedDashNests: {'0', '1'},
bonusHistory: [],
),
GameState(
score: 0,
balls: 4,
activatedDashNests: {},
bonusHistory: [GameBonus.dashNest],
),
],
);
});
group( group(
'BonusActivated', 'BonusActivated',
() { () {
@ -129,13 +92,11 @@ void main() {
GameState( GameState(
score: 0, score: 0,
balls: 3, balls: 3,
activatedDashNests: {},
bonusHistory: [GameBonus.googleWord], bonusHistory: [GameBonus.googleWord],
), ),
GameState( GameState(
score: 0, score: 0,
balls: 3, balls: 3,
activatedDashNests: {},
bonusHistory: [GameBonus.googleWord, GameBonus.dashNest], bonusHistory: [GameBonus.googleWord, GameBonus.dashNest],
), ),
], ],
@ -152,7 +113,6 @@ void main() {
GameState( GameState(
score: 0, score: 0,
balls: 3, balls: 3,
activatedDashNests: {},
bonusHistory: [GameBonus.sparkyTurboCharge], bonusHistory: [GameBonus.sparkyTurboCharge],
), ),
], ],

@ -59,23 +59,6 @@ void main() {
}); });
}); });
group('DashNestActivated', () {
test('can be instantiated', () {
expect(const DashNestActivated('0'), isNotNull);
});
test('supports value equality', () {
expect(
DashNestActivated('0'),
equals(DashNestActivated('0')),
);
expect(
DashNestActivated('0'),
isNot(equals(DashNestActivated('1'))),
);
});
});
group('SparkyTurboChargeActivated', () { group('SparkyTurboChargeActivated', () {
test('can be instantiated', () { test('can be instantiated', () {
expect(const SparkyTurboChargeActivated(), isNotNull); expect(const SparkyTurboChargeActivated(), isNotNull);

@ -10,14 +10,12 @@ void main() {
GameState( GameState(
score: 0, score: 0,
balls: 0, balls: 0,
activatedDashNests: const {},
bonusHistory: const [], bonusHistory: const [],
), ),
equals( equals(
const GameState( const GameState(
score: 0, score: 0,
balls: 0, balls: 0,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
), ),
), ),
@ -30,7 +28,6 @@ void main() {
const GameState( const GameState(
score: 0, score: 0,
balls: 0, balls: 0,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
), ),
isNotNull, isNotNull,
@ -46,7 +43,6 @@ void main() {
() => GameState( () => GameState(
balls: -1, balls: -1,
score: 0, score: 0,
activatedDashNests: const {},
bonusHistory: const [], bonusHistory: const [],
), ),
throwsAssertionError, throwsAssertionError,
@ -62,7 +58,6 @@ void main() {
() => GameState( () => GameState(
balls: 0, balls: 0,
score: -1, score: -1,
activatedDashNests: const {},
bonusHistory: const [], bonusHistory: const [],
), ),
throwsAssertionError, throwsAssertionError,
@ -77,7 +72,6 @@ void main() {
const gameState = GameState( const gameState = GameState(
balls: 0, balls: 0,
score: 0, score: 0,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
); );
expect(gameState.isGameOver, isTrue); expect(gameState.isGameOver, isTrue);
@ -89,7 +83,6 @@ void main() {
const gameState = GameState( const gameState = GameState(
balls: 1, balls: 1,
score: 0, score: 0,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
); );
expect(gameState.isGameOver, isFalse); expect(gameState.isGameOver, isFalse);
@ -104,7 +97,6 @@ void main() {
const gameState = GameState( const gameState = GameState(
balls: 0, balls: 0,
score: 2, score: 2,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
); );
expect( expect(
@ -121,7 +113,6 @@ void main() {
const gameState = GameState( const gameState = GameState(
balls: 0, balls: 0,
score: 2, score: 2,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
); );
expect( expect(
@ -138,13 +129,11 @@ void main() {
const gameState = GameState( const gameState = GameState(
score: 2, score: 2,
balls: 0, balls: 0,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
); );
final otherGameState = GameState( final otherGameState = GameState(
score: gameState.score + 1, score: gameState.score + 1,
balls: gameState.balls + 1, balls: gameState.balls + 1,
activatedDashNests: const {'1'},
bonusHistory: const [GameBonus.googleWord], bonusHistory: const [GameBonus.googleWord],
); );
expect(gameState, isNot(equals(otherGameState))); expect(gameState, isNot(equals(otherGameState)));
@ -153,7 +142,6 @@ void main() {
gameState.copyWith( gameState.copyWith(
score: otherGameState.score, score: otherGameState.score,
balls: otherGameState.balls, balls: otherGameState.balls,
activatedDashNests: otherGameState.activatedDashNests,
bonusHistory: otherGameState.bonusHistory, bonusHistory: otherGameState.bonusHistory,
), ),
equals(otherGameState), equals(otherGameState),

@ -21,7 +21,6 @@ void main() {
score: 0, score: 0,
balls: 0, balls: 0,
bonusHistory: [], bonusHistory: [],
activatedDashNests: {},
); );
whenListen(bloc, Stream.value(state), initialState: state); whenListen(bloc, Stream.value(state), initialState: state);
return bloc; return bloc;

@ -22,7 +22,6 @@ void main() {
score: 0, score: 0,
balls: 0, balls: 0,
bonusHistory: [], bonusHistory: [],
activatedDashNests: {},
); );
whenListen(bloc, Stream.value(state), initialState: state); whenListen(bloc, Stream.value(state), initialState: state);
return bloc; return bloc;

@ -79,105 +79,21 @@ void main() {
); );
}); });
group('controller', () {
group('listenWhen', () {
final gameBloc = MockGameBloc();
final flameBlocTester = FlameBlocTester<TestGame, GameBloc>(
gameBuilder: TestGame.new,
blocBuilder: () => gameBloc,
);
flameBlocTester.testGameWidget(
'listens when a Bonus.dashNest and a bonusBall is added',
verify: (game, tester) async {
final flutterForest = FlutterForest();
const state = GameState(
score: 0,
balls: 3,
activatedDashNests: {},
bonusHistory: [GameBonus.dashNest],
);
expect(
flutterForest.controller
.listenWhen(const GameState.initial(), state),
isTrue,
);
},
);
});
});
flameTester.test(
'onNewState adds a new ball after a duration',
(game) async {
final flutterForest = FlutterForest();
await game.ensureAdd(flutterForest);
final previousBalls = game.descendants().whereType<Ball>().length;
flutterForest.controller.onNewState(MockGameState());
await Future<void>.delayed(const Duration(milliseconds: 700));
await game.ready();
expect(
game.descendants().whereType<Ball>().length,
greaterThan(previousBalls),
);
},
);
flameTester.test(
'onNewState starts Dash animatronic',
(game) async {
final flutterForest = FlutterForest();
await game.ensureAdd(flutterForest);
flutterForest.controller.onNewState(MockGameState());
final dashAnimatronic =
game.descendants().whereType<DashAnimatronic>().single;
expect(dashAnimatronic.playing, isTrue);
},
);
group('bumpers', () { group('bumpers', () {
late Ball ball; late Ball ball;
late GameBloc gameBloc; late GameBloc gameBloc;
setUp(() { setUp(() {
ball = Ball(baseColor: const Color(0xFF00FFFF)); ball = Ball(baseColor: const Color(0xFF00FFFF));
gameBloc = MockGameBloc();
whenListen(
gameBloc,
const Stream<GameState>.empty(),
initialState: const GameState.initial(),
);
}); });
final flameBlocTester = FlameBlocTester<PinballGame, GameBloc>( final flameBlocTester = FlameBlocTester<PinballGame, GameBloc>(
gameBuilder: EmptyPinballTestGame.new, gameBuilder: EmptyPinballTestGame.new,
blocBuilder: () => gameBloc, blocBuilder: () {
); gameBloc = MockGameBloc();
const state = GameState.initial();
flameBlocTester.testGameWidget( whenListen(gameBloc, Stream.value(state), initialState: state);
'add DashNestActivated event', return gameBloc;
setUp: (game, tester) async {
final flutterForest = FlutterForest();
await game.ensureAdd(flutterForest);
await game.ensureAdd(ball);
final bumpers =
flutterForest.descendants().whereType<DashNestBumper>();
for (final bumper in bumpers) {
beginContact(game, bumper, ball);
final controller = bumper.firstChild<DashNestBumperController>()!;
verify(
() => gameBloc.add(DashNestActivated(controller.id)),
).called(1);
}
}, },
); );
@ -185,8 +101,10 @@ void main() {
'add Scored event', 'add Scored event',
setUp: (game, tester) async { setUp: (game, tester) async {
final flutterForest = FlutterForest(); final flutterForest = FlutterForest();
await game.ensureAdd(flutterForest); await game.ensureAddAll([
await game.ensureAdd(ball); flutterForest,
ball,
]);
game.addContactCallback(BallScorePointsCallback(game)); game.addContactCallback(BallScorePointsCallback(game));
final bumpers = flutterForest.descendants().whereType<ScorePoints>(); final bumpers = flutterForest.descendants().whereType<ScorePoints>();
@ -201,122 +119,58 @@ void main() {
} }
}, },
); );
});
});
group('DashNestBumperController', () { flameBlocTester.testGameWidget(
late DashNestBumper dashNestBumper; 'adds GameBonus.dashNest to the game when 3 bumpers are activated',
setUp: (game, _) async {
setUp(() { final ball = Ball(baseColor: const Color(0xFFFF0000));
dashNestBumper = MockDashNestBumper(); final flutterForest = FlutterForest();
}); await game.ensureAddAll([flutterForest, ball]);
group(
'listensWhen',
() {
late GameState previousState;
late GameState newState;
setUp(
() {
previousState = MockGameState();
newState = MockGameState();
},
);
test('listens when the id is added to activatedDashNests', () {
const id = '';
final controller = DashNestBumperController(
dashNestBumper,
id: id,
);
when(() => previousState.activatedDashNests).thenReturn({});
when(() => newState.activatedDashNests).thenReturn({id});
expect(controller.listenWhen(previousState, newState), isTrue);
});
test('listens when the id is removed from activatedDashNests', () {
const id = '';
final controller = DashNestBumperController(
dashNestBumper,
id: id,
);
when(() => previousState.activatedDashNests).thenReturn({id});
when(() => newState.activatedDashNests).thenReturn({});
expect(controller.listenWhen(previousState, newState), isTrue);
});
test("doesn't listen when the id is never in activatedDashNests", () {
final controller = DashNestBumperController(
dashNestBumper,
id: '',
);
when(() => previousState.activatedDashNests).thenReturn({});
when(() => newState.activatedDashNests).thenReturn({});
expect(controller.listenWhen(previousState, newState), isFalse); final bumpers = flutterForest.children.whereType<DashNestBumper>();
}); expect(bumpers.length, equals(3));
for (final bumper in bumpers) {
beginContact(game, bumper, ball);
await game.ready();
test("doesn't listen when the id still in activatedDashNests", () { if (bumper == bumpers.last) {
const id = ''; verify(
final controller = DashNestBumperController( () => gameBloc.add(const BonusActivated(GameBonus.dashNest)),
dashNestBumper, ).called(1);
id: id, } else {
verifyNever(
() => gameBloc.add(const BonusActivated(GameBonus.dashNest)),
); );
}
when(() => previousState.activatedDashNests).thenReturn({id}); }
when(() => newState.activatedDashNests).thenReturn({id});
expect(controller.listenWhen(previousState, newState), isFalse);
});
}, },
); );
group( flameBlocTester.testGameWidget(
'onNewState', 'deactivates bumpers when 3 are active',
() { setUp: (game, _) async {
late GameState state; final ball = Ball(baseColor: const Color(0xFFFF0000));
final flutterForest = FlutterForest();
setUp(() { await game.ensureAddAll([flutterForest, ball]);
state = MockGameState();
});
test(
'activates the bumper when id in activatedDashNests',
() {
const id = '';
final controller = DashNestBumperController(
dashNestBumper,
id: id,
);
when(() => state.activatedDashNests).thenReturn({id});
controller.onNewState(state);
verify(() => dashNestBumper.activate()).called(1);
},
);
test( final bumpers = [
'deactivates the bumper when id not in activatedDashNests', MockDashNestBumper(),
() { MockDashNestBumper(),
final controller = DashNestBumperController( MockDashNestBumper(),
dashNestBumper, ];
id: '',
);
when(() => state.activatedDashNests).thenReturn({}); for (final bumper in bumpers) {
controller.onNewState(state); flutterForest.controller.activateBumper(bumper);
await game.ready();
verify(() => dashNestBumper.deactivate()).called(1); if (bumper == bumpers.last) {
}, for (final bumper in bumpers) {
); verify(bumper.deactivate).called(1);
}
}
}
}, },
); );
}); });
});
} }

@ -16,7 +16,6 @@ void main() {
score: 10, score: 10,
balls: 0, balls: 0,
bonusHistory: const [], bonusHistory: const [],
activatedDashNests: const {},
); );
final previous = GameState.initial(); final previous = GameState.initial();
@ -66,7 +65,6 @@ void main() {
score: 10, score: 10,
balls: 0, balls: 0,
bonusHistory: const [], bonusHistory: const [],
activatedDashNests: const {},
), ),
); );

@ -31,7 +31,6 @@ void main() {
score: 10, score: 10,
balls: 3, balls: 3,
bonusHistory: [], bonusHistory: [],
activatedDashNests: {},
); );
expect(controller.listenWhen(previous, current), isTrue); expect(controller.listenWhen(previous, current), isTrue);
}); });
@ -44,7 +43,6 @@ void main() {
score: 10, score: 10,
balls: 3, balls: 3,
bonusHistory: [], bonusHistory: [],
activatedDashNests: {},
); );
expect(controller.listenWhen(null, current), isTrue); expect(controller.listenWhen(null, current), isTrue);
}, },
@ -69,7 +67,6 @@ void main() {
score: 10, score: 10,
balls: 3, balls: 3,
bonusHistory: [], bonusHistory: [],
activatedDashNests: {},
); );
controller.onNewState(state); controller.onNewState(state);
@ -87,7 +84,6 @@ void main() {
score: 10, score: 10,
balls: 3, balls: 3,
bonusHistory: [], bonusHistory: [],
activatedDashNests: {},
), ),
); );
@ -96,7 +92,6 @@ void main() {
score: 14, score: 14,
balls: 3, balls: 3,
bonusHistory: [], bonusHistory: [],
activatedDashNests: {},
), ),
); );

@ -12,7 +12,6 @@ void main() {
const initialState = GameState( const initialState = GameState(
score: 10, score: 10,
balls: 2, balls: 2,
activatedDashNests: {},
bonusHistory: [], bonusHistory: [],
); );

Loading…
Cancel
Save