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