mirror of https://github.com/flutter/pinball.git
parent
2bd3061ebf
commit
dee70426d5
@ -1,77 +0,0 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template multiball_group_component}
|
||||
/// A [SpriteGroupComponent] for the multiball over the board.
|
||||
/// {@endtemplate}
|
||||
class MultiballGroup extends Component
|
||||
with Controls<MultiballController>, HasGameRef<PinballGame> {
|
||||
/// {@macro multiball_group_component}
|
||||
MultiballGroup() : super() {
|
||||
controller = MultiballController(this);
|
||||
}
|
||||
|
||||
/// Bottom left multiball.
|
||||
late final Multiball multiballA;
|
||||
|
||||
/// Center left multiball.
|
||||
late final Multiball multiballB;
|
||||
|
||||
/// Center right multiball.
|
||||
late final Multiball multiballC;
|
||||
|
||||
/// Bottom right multiball.
|
||||
late final Multiball multiballD;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
multiballA = Multiball.a();
|
||||
multiballB = Multiball.b();
|
||||
multiballC = Multiball.c();
|
||||
multiballD = Multiball.d();
|
||||
|
||||
await addAll([
|
||||
multiballA,
|
||||
multiballB,
|
||||
multiballC,
|
||||
multiballD,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template multiball_controller}
|
||||
/// Controller attached to a [MultiballGroup] that handles its game related
|
||||
/// logic.
|
||||
/// {@endtemplate}
|
||||
@visibleForTesting
|
||||
class MultiballController extends ComponentController<MultiballGroup>
|
||||
with BlocComponent<GameBloc, GameState>, HasGameRef<PinballGame> {
|
||||
/// {@macro multiball_controller}
|
||||
MultiballController(MultiballGroup multiballGroup) : super(multiballGroup);
|
||||
|
||||
@override
|
||||
bool listenWhen(GameState? previousState, GameState newState) {
|
||||
return previousState?.bonusHistory != newState.bonusHistory;
|
||||
}
|
||||
|
||||
@override
|
||||
void onNewState(GameState state) {
|
||||
final hasMultiball = state.bonusHistory.contains(GameBonus.dashNest);
|
||||
|
||||
if (hasMultiball) {
|
||||
// TODO(ruimiguel): change to animate every children without different
|
||||
// properties using component.children.whereType<Multiball>().forEach
|
||||
// once able to mock the children ComponentSet.
|
||||
component.multiballA.animate();
|
||||
component.multiballB.animate();
|
||||
component.multiballC.animate();
|
||||
component.multiballD.animate();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export 'multiballs_behavior.dart';
|
@ -0,0 +1,24 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// Toggle each [Multiball] when there is a bonus ball.
|
||||
class MultiballsBehavior extends Component
|
||||
with HasGameRef<PinballGame>, ParentIsA<Multiballs> {
|
||||
@override
|
||||
void onMount() {
|
||||
super.onMount();
|
||||
|
||||
gameRef.read<GameBloc>().stream.listen((state) {
|
||||
final hasMultiball = state.bonusHistory.contains(GameBonus.dashNest);
|
||||
|
||||
if (hasMultiball) {
|
||||
final multiballs = parent.children.whereType<Multiball>();
|
||||
for (final multiball in multiballs) {
|
||||
multiball.bloc.animate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball/game/components/multiballs/behaviors/behaviors.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template multiballs_component}
|
||||
/// A [SpriteGroupComponent] for the multiball over the board.
|
||||
/// {@endtemplate}
|
||||
class Multiballs extends Component {
|
||||
/// {@macro multiballs_component}
|
||||
Multiballs()
|
||||
: super(
|
||||
children: [
|
||||
Multiball.a(),
|
||||
Multiball.b(),
|
||||
Multiball.c(),
|
||||
Multiball.d(),
|
||||
MultiballsBehavior(),
|
||||
],
|
||||
);
|
||||
|
||||
/// Creates a [Multiballs] without any children.
|
||||
///
|
||||
/// This can be used for testing [Multiballs]'s behaviors in isolation.
|
||||
@visibleForTesting
|
||||
Multiballs.test();
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final assets = [
|
||||
Assets.images.multiball.a.active.keyName,
|
||||
Assets.images.multiball.a.inactive.keyName,
|
||||
Assets.images.multiball.b.active.keyName,
|
||||
Assets.images.multiball.b.inactive.keyName,
|
||||
Assets.images.multiball.c.active.keyName,
|
||||
Assets.images.multiball.c.inactive.keyName,
|
||||
Assets.images.multiball.d.active.keyName,
|
||||
Assets.images.multiball.d.inactive.keyName,
|
||||
];
|
||||
final flameTester = FlameTester(() => EmptyPinballTestGame(assets));
|
||||
|
||||
group('MultiballGroup', () {
|
||||
flameTester.test(
|
||||
'loads correctly',
|
||||
(game) async {
|
||||
final multiballGroup = MultiballGroup();
|
||||
await game.ensureAdd(multiballGroup);
|
||||
|
||||
expect(game.contains(multiballGroup), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
group('loads', () {
|
||||
flameTester.test(
|
||||
'four Multiball',
|
||||
(game) async {
|
||||
final multiballGroup = MultiballGroup();
|
||||
await game.ensureAdd(multiballGroup);
|
||||
|
||||
expect(
|
||||
multiballGroup.descendants().whereType<Multiball>().length,
|
||||
equals(4),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
group('MultiballController', () {
|
||||
group('controller', () {
|
||||
group('listenWhen', () {
|
||||
flameTester.test(
|
||||
'listens when obtain a multiball bonus',
|
||||
(game) async {
|
||||
const previous = GameState.initial();
|
||||
final state = previous.copyWith(bonusHistory: [GameBonus.dashNest]);
|
||||
|
||||
final multiballGroup = MultiballGroup();
|
||||
await game.ensureAdd(multiballGroup);
|
||||
|
||||
expect(
|
||||
multiballGroup.controller.listenWhen(previous, state),
|
||||
isTrue,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
"doesn't listen when bonus is the same",
|
||||
(game) async {
|
||||
const previous = GameState.initial();
|
||||
|
||||
final multiballGroup = MultiballGroup();
|
||||
await game.ensureAdd(multiballGroup);
|
||||
|
||||
expect(
|
||||
multiballGroup.controller.listenWhen(previous, previous),
|
||||
isFalse,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group(
|
||||
'onNewState',
|
||||
() {
|
||||
flameTester.test(
|
||||
'blink multiballs when state changes',
|
||||
(game) async {
|
||||
// TODO(ruimiguel): search how to mock MultiballGroup children
|
||||
// ComponentSet to improve this test.
|
||||
final multiballGroup = MockMultiballGroup();
|
||||
final multiballA = MockMultiball();
|
||||
final multiballB = MockMultiball();
|
||||
final multiballC = MockMultiball();
|
||||
final multiballD = MockMultiball();
|
||||
final controller = MultiballController(multiballGroup);
|
||||
when(() => multiballGroup.multiballA).thenReturn(multiballA);
|
||||
when(() => multiballGroup.multiballB).thenReturn(multiballB);
|
||||
when(() => multiballGroup.multiballC).thenReturn(multiballC);
|
||||
when(() => multiballGroup.multiballD).thenReturn(multiballD);
|
||||
when(multiballA.animate).thenAnswer((_) async => () {});
|
||||
when(multiballB.animate).thenAnswer((_) async => () {});
|
||||
when(multiballC.animate).thenAnswer((_) async => () {});
|
||||
when(multiballD.animate).thenAnswer((_) async => () {});
|
||||
|
||||
controller.onNewState(
|
||||
const GameState.initial()
|
||||
.copyWith(bonusHistory: [GameBonus.dashNest]),
|
||||
);
|
||||
|
||||
verify(multiballA.animate).called(1);
|
||||
verify(multiballB.animate).called(1);
|
||||
verify(multiballC.animate).called(1);
|
||||
verify(multiballD.animate).called(1);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball/game/components/multiballs/behaviors/behaviors.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final assets = [
|
||||
Assets.images.multiball.a.lit.keyName,
|
||||
Assets.images.multiball.a.dimmed.keyName,
|
||||
Assets.images.multiball.b.lit.keyName,
|
||||
Assets.images.multiball.b.dimmed.keyName,
|
||||
Assets.images.multiball.c.lit.keyName,
|
||||
Assets.images.multiball.c.dimmed.keyName,
|
||||
Assets.images.multiball.d.lit.keyName,
|
||||
Assets.images.multiball.d.dimmed.keyName,
|
||||
];
|
||||
|
||||
group('MultiballsBehavior', () {
|
||||
late GameBloc gameBloc;
|
||||
|
||||
setUp(() {
|
||||
gameBloc = MockGameBloc();
|
||||
whenListen(
|
||||
gameBloc,
|
||||
const Stream<GameState>.empty(),
|
||||
initialState: const GameState.initial(),
|
||||
);
|
||||
});
|
||||
|
||||
final flameBlocTester = FlameBlocTester<PinballGame, GameBloc>(
|
||||
gameBuilder: EmptyPinballTestGame.new,
|
||||
blocBuilder: () => gameBloc,
|
||||
assets: assets,
|
||||
);
|
||||
});
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final assets = [
|
||||
Assets.images.multiball.a.lit.keyName,
|
||||
Assets.images.multiball.a.dimmed.keyName,
|
||||
Assets.images.multiball.b.lit.keyName,
|
||||
Assets.images.multiball.b.dimmed.keyName,
|
||||
Assets.images.multiball.c.lit.keyName,
|
||||
Assets.images.multiball.c.dimmed.keyName,
|
||||
Assets.images.multiball.d.lit.keyName,
|
||||
Assets.images.multiball.d.dimmed.keyName,
|
||||
];
|
||||
late GameBloc gameBloc;
|
||||
|
||||
setUp(() {
|
||||
gameBloc = GameBloc();
|
||||
});
|
||||
|
||||
final flameBlocTester = FlameBlocTester<PinballGame, GameBloc>(
|
||||
gameBuilder: EmptyPinballTestGame.new,
|
||||
blocBuilder: () => gameBloc,
|
||||
assets: assets,
|
||||
);
|
||||
|
||||
group('Multiballs', () {
|
||||
flameBlocTester.testGameWidget(
|
||||
'loads correctly',
|
||||
setUp: (game, tester) async {
|
||||
final multiballs = Multiballs();
|
||||
await game.ensureAdd(multiballs);
|
||||
|
||||
expect(game.contains(multiballs), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
group('loads', () {
|
||||
flameBlocTester.testGameWidget(
|
||||
'four Multiball',
|
||||
setUp: (game, tester) async {
|
||||
final multiballs = Multiballs();
|
||||
await game.ensureAdd(multiballs);
|
||||
|
||||
expect(
|
||||
multiballs.descendants().whereType<Multiball>().length,
|
||||
equals(4),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in new issue