feat: implemented `AndroidSpaceshipBonusBehavior` (#298)
* refactor: simplify ball entering spaceship * feat: add bonus logic and scoring * chore: add animatronic to spaceship sandbox * chore: remove renderBody change * chore: remove leftover children change * chore: test typo * refactor: re-add children property * refactor: use firstChild * refactor: PR suggestions * Update packages/pinball_components/test/src/components/android_animatronic_test.dart Co-authored-by: Alejandro Santiago <dev@alestiago.com> Co-authored-by: Alejandro Santiago <dev@alestiago.com>pull/305/head
@ -0,0 +1,27 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
/// Adds a [GameBonus.androidSpaceship] when [AndroidSpaceship] has a bonus.
|
||||||
|
class AndroidSpaceshipBonusBehavior extends Component
|
||||||
|
with HasGameRef<PinballGame>, ParentIsA<AndroidAcres> {
|
||||||
|
@override
|
||||||
|
void onMount() {
|
||||||
|
super.onMount();
|
||||||
|
final androidSpaceship = parent.firstChild<AndroidSpaceship>()!;
|
||||||
|
|
||||||
|
// TODO(alestiago): Refactor subscription management once the following is
|
||||||
|
// merged:
|
||||||
|
// https://github.com/flame-engine/flame/pull/1538
|
||||||
|
androidSpaceship.bloc.stream.listen((state) {
|
||||||
|
final listenWhen = state == AndroidSpaceshipState.withBonus;
|
||||||
|
if (!listenWhen) return;
|
||||||
|
|
||||||
|
gameRef
|
||||||
|
.read<GameBloc>()
|
||||||
|
.add(const BonusActivated(GameBonus.androidSpaceship));
|
||||||
|
androidSpaceship.bloc.onBonusAwarded();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export 'android_spaceship_bonus_behavior.dart';
|
@ -0,0 +1,71 @@
|
|||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
|
/// {@template android_animatronic}
|
||||||
|
/// Animated Android that sits on top of the [AndroidSpaceship].
|
||||||
|
/// {@endtemplate}
|
||||||
|
class AndroidAnimatronic extends BodyComponent
|
||||||
|
with InitialPosition, Layered, ZIndex {
|
||||||
|
/// {@macro android_animatronic}
|
||||||
|
AndroidAnimatronic({Iterable<Component>? children})
|
||||||
|
: super(
|
||||||
|
children: [
|
||||||
|
_AndroidAnimatronicSpriteAnimationComponent(),
|
||||||
|
...?children,
|
||||||
|
],
|
||||||
|
renderBody: false,
|
||||||
|
) {
|
||||||
|
layer = Layer.spaceship;
|
||||||
|
zIndex = ZIndexes.androidHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Body createBody() {
|
||||||
|
final shape = EllipseShape(
|
||||||
|
center: Vector2.zero(),
|
||||||
|
majorRadius: 3.1,
|
||||||
|
minorRadius: 2,
|
||||||
|
)..rotate(1.4);
|
||||||
|
final bodyDef = BodyDef(position: initialPosition);
|
||||||
|
|
||||||
|
return world.createBody(bodyDef)..createFixtureFromShape(shape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AndroidAnimatronicSpriteAnimationComponent
|
||||||
|
extends SpriteAnimationComponent with HasGameRef {
|
||||||
|
_AndroidAnimatronicSpriteAnimationComponent()
|
||||||
|
: super(
|
||||||
|
anchor: Anchor.center,
|
||||||
|
position: Vector2(-0.24, -2.6),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
await super.onLoad();
|
||||||
|
|
||||||
|
final spriteSheet = gameRef.images.fromCache(
|
||||||
|
Assets.images.android.spaceship.animatronic.keyName,
|
||||||
|
);
|
||||||
|
|
||||||
|
const amountPerRow = 18;
|
||||||
|
const amountPerColumn = 4;
|
||||||
|
final textureSize = Vector2(
|
||||||
|
spriteSheet.width / amountPerRow,
|
||||||
|
spriteSheet.height / amountPerColumn,
|
||||||
|
);
|
||||||
|
size = textureSize / 10;
|
||||||
|
|
||||||
|
animation = SpriteAnimation.fromFrameData(
|
||||||
|
spriteSheet,
|
||||||
|
SpriteAnimationData.sequenced(
|
||||||
|
amount: amountPerRow * amountPerColumn,
|
||||||
|
amountPerRow: amountPerRow,
|
||||||
|
stepTime: 1 / 24,
|
||||||
|
textureSize: textureSize,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
|
class AndroidSpaceshipEntranceBallContactBehavior
|
||||||
|
extends ContactBehavior<AndroidSpaceshipEntrance> {
|
||||||
|
@override
|
||||||
|
void beginContact(Object other, Contact contact) {
|
||||||
|
super.beginContact(other, contact);
|
||||||
|
if (other is! Ball) return;
|
||||||
|
|
||||||
|
parent.parent.bloc.onBallEntered();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export 'android_spaceship_entrance_ball_contact_behavior.dart.dart';
|
@ -0,0 +1,13 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
|
||||||
|
part 'android_spaceship_state.dart';
|
||||||
|
|
||||||
|
class AndroidSpaceshipCubit extends Cubit<AndroidSpaceshipState> {
|
||||||
|
AndroidSpaceshipCubit() : super(AndroidSpaceshipState.withoutBonus);
|
||||||
|
|
||||||
|
void onBallEntered() => emit(AndroidSpaceshipState.withBonus);
|
||||||
|
|
||||||
|
void onBonusAwarded() => emit(AndroidSpaceshipState.withoutBonus);
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
part of 'android_spaceship_cubit.dart';
|
||||||
|
|
||||||
|
enum AndroidSpaceshipState {
|
||||||
|
withoutBonus,
|
||||||
|
withBonus,
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
import '../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final asset = Assets.images.android.spaceship.animatronic.keyName;
|
||||||
|
final flameTester = FlameTester(() => TestGame([asset]));
|
||||||
|
|
||||||
|
group('AndroidAnimatronic', () {
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'renders correctly',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.load(asset);
|
||||||
|
await game.ensureAdd(AndroidAnimatronic());
|
||||||
|
game.camera.followVector2(Vector2.zero());
|
||||||
|
await tester.pump();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
final animationDuration = game
|
||||||
|
.firstChild<AndroidAnimatronic>()!
|
||||||
|
.firstChild<SpriteAnimationComponent>()!
|
||||||
|
.animation!
|
||||||
|
.totalDuration();
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('golden/android_animatronic/start.png'),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.update(animationDuration * 0.5);
|
||||||
|
await tester.pump();
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('golden/android_animatronic/middle.png'),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.update(animationDuration * 0.5);
|
||||||
|
await tester.pump();
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('golden/android_animatronic/end.png'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
'loads correctly',
|
||||||
|
(game) async {
|
||||||
|
final androidAnimatronic = AndroidAnimatronic();
|
||||||
|
await game.ensureAdd(androidAnimatronic);
|
||||||
|
expect(game.contains(androidAnimatronic), isTrue);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.test('adds new children', (game) async {
|
||||||
|
final component = Component();
|
||||||
|
final androidAnimatronic = AndroidAnimatronic(
|
||||||
|
children: [component],
|
||||||
|
);
|
||||||
|
await game.ensureAdd(androidAnimatronic);
|
||||||
|
expect(androidAnimatronic.children, contains(component));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_components/src/components/android_spaceship/behaviors/behaviors.dart';
|
||||||
|
import 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
|
import '../../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
class _MockAndroidSpaceshipCubit extends Mock implements AndroidSpaceshipCubit {
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('AndroidSpaceship', () {
|
||||||
|
final assets = [
|
||||||
|
Assets.images.android.spaceship.saucer.keyName,
|
||||||
|
Assets.images.android.spaceship.lightBeam.keyName,
|
||||||
|
];
|
||||||
|
final flameTester = FlameTester(() => TestGame(assets));
|
||||||
|
|
||||||
|
flameTester.test('loads correctly', (game) async {
|
||||||
|
final component = AndroidSpaceship(position: Vector2.zero());
|
||||||
|
await game.ensureAdd(component);
|
||||||
|
expect(game.contains(component), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'renders correctly',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
await game.images.loadAll(assets);
|
||||||
|
final canvas = ZCanvasComponent(
|
||||||
|
children: [AndroidSpaceship(position: Vector2.zero())],
|
||||||
|
);
|
||||||
|
await game.ensureAdd(canvas);
|
||||||
|
game.camera.followVector2(Vector2.zero());
|
||||||
|
await game.ready();
|
||||||
|
await tester.pump();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
const goldenFilePath = '../golden/android_spaceship/';
|
||||||
|
final animationDuration = game
|
||||||
|
.descendants()
|
||||||
|
.whereType<SpriteAnimationComponent>()
|
||||||
|
.single
|
||||||
|
.animation!
|
||||||
|
.totalDuration();
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('${goldenFilePath}start.png'),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.update(animationDuration * 0.5);
|
||||||
|
await tester.pump();
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('${goldenFilePath}middle.png'),
|
||||||
|
);
|
||||||
|
|
||||||
|
game.update(animationDuration * 0.5);
|
||||||
|
await tester.pump();
|
||||||
|
await expectLater(
|
||||||
|
find.byGame<TestGame>(),
|
||||||
|
matchesGoldenFile('${goldenFilePath}end.png'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO(alestiago): Consider refactoring once the following is merged:
|
||||||
|
// https://github.com/flame-engine/flame/pull/1538
|
||||||
|
// ignore: public_member_api_docs
|
||||||
|
flameTester.test('closes bloc when removed', (game) async {
|
||||||
|
final bloc = _MockAndroidSpaceshipCubit();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
const Stream<AndroidSpaceshipState>.empty(),
|
||||||
|
initialState: AndroidSpaceshipState.withoutBonus,
|
||||||
|
);
|
||||||
|
when(bloc.close).thenAnswer((_) async {});
|
||||||
|
final androidSpaceship = AndroidSpaceship.test(bloc: bloc);
|
||||||
|
|
||||||
|
await game.ensureAdd(androidSpaceship);
|
||||||
|
game.remove(androidSpaceship);
|
||||||
|
await game.ready();
|
||||||
|
|
||||||
|
verify(bloc.close).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
'AndroidSpaceshipEntrance has an '
|
||||||
|
'AndroidSpaceshipEntranceBallContactBehavior', (game) async {
|
||||||
|
final androidSpaceship = AndroidSpaceship(position: Vector2.zero());
|
||||||
|
await game.ensureAdd(androidSpaceship);
|
||||||
|
|
||||||
|
final androidSpaceshipEntrance =
|
||||||
|
androidSpaceship.firstChild<AndroidSpaceshipEntrance>();
|
||||||
|
expect(
|
||||||
|
androidSpaceshipEntrance!.children
|
||||||
|
.whereType<AndroidSpaceshipEntranceBallContactBehavior>()
|
||||||
|
.single,
|
||||||
|
isNotNull,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_components/src/components/android_spaceship/behaviors/behaviors.dart';
|
||||||
|
|
||||||
|
import '../../../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
class _MockAndroidSpaceshipCubit extends Mock implements AndroidSpaceshipCubit {
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final flameTester = FlameTester(TestGame.new);
|
||||||
|
|
||||||
|
group(
|
||||||
|
'AndroidSpaceshipEntranceBallContactBehavior',
|
||||||
|
() {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
AndroidSpaceshipEntranceBallContactBehavior(),
|
||||||
|
isA<AndroidSpaceshipEntranceBallContactBehavior>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
'beginContact calls onBallEntered when entrance contacts with a ball',
|
||||||
|
(game) async {
|
||||||
|
final behavior = AndroidSpaceshipEntranceBallContactBehavior();
|
||||||
|
final bloc = _MockAndroidSpaceshipCubit();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
const Stream<AndroidSpaceshipState>.empty(),
|
||||||
|
initialState: AndroidSpaceshipState.withoutBonus,
|
||||||
|
);
|
||||||
|
|
||||||
|
final entrance = AndroidSpaceshipEntrance();
|
||||||
|
final androidSpaceship = AndroidSpaceship.test(
|
||||||
|
bloc: bloc,
|
||||||
|
children: [entrance],
|
||||||
|
);
|
||||||
|
await entrance.add(behavior);
|
||||||
|
await game.ensureAdd(androidSpaceship);
|
||||||
|
|
||||||
|
behavior.beginContact(MockBall(), MockContact());
|
||||||
|
|
||||||
|
verify(androidSpaceship.bloc.onBallEntered).called(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group(
|
||||||
|
'AndroidSpaceshipCubit',
|
||||||
|
() {
|
||||||
|
blocTest<AndroidSpaceshipCubit, AndroidSpaceshipState>(
|
||||||
|
'onBallEntered emits withBonus',
|
||||||
|
build: AndroidSpaceshipCubit.new,
|
||||||
|
act: (bloc) => bloc.onBallEntered(),
|
||||||
|
expect: () => [AndroidSpaceshipState.withBonus],
|
||||||
|
);
|
||||||
|
|
||||||
|
blocTest<AndroidSpaceshipCubit, AndroidSpaceshipState>(
|
||||||
|
'onBonusAwarded emits withoutBonus',
|
||||||
|
build: AndroidSpaceshipCubit.new,
|
||||||
|
act: (bloc) => bloc.onBonusAwarded(),
|
||||||
|
expect: () => [AndroidSpaceshipState.withoutBonus],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
@ -1,67 +0,0 @@
|
|||||||
// ignore_for_file: cascade_invocations
|
|
||||||
|
|
||||||
import 'package:flame/components.dart';
|
|
||||||
import 'package:flame_test/flame_test.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:pinball_components/pinball_components.dart';
|
|
||||||
import 'package:pinball_flame/pinball_flame.dart';
|
|
||||||
|
|
||||||
import '../../helpers/helpers.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
group('AndroidSpaceship', () {
|
|
||||||
final assets = [
|
|
||||||
Assets.images.android.spaceship.saucer.keyName,
|
|
||||||
Assets.images.android.spaceship.animatronic.keyName,
|
|
||||||
Assets.images.android.spaceship.lightBeam.keyName,
|
|
||||||
];
|
|
||||||
final flameTester = FlameTester(() => TestGame(assets));
|
|
||||||
|
|
||||||
flameTester.test('loads correctly', (game) async {
|
|
||||||
final component = AndroidSpaceship(position: Vector2.zero());
|
|
||||||
await game.ensureAdd(component);
|
|
||||||
expect(game.contains(component), isTrue);
|
|
||||||
});
|
|
||||||
|
|
||||||
flameTester.testGameWidget(
|
|
||||||
'renders correctly',
|
|
||||||
setUp: (game, tester) async {
|
|
||||||
await game.images.loadAll(assets);
|
|
||||||
final canvas = ZCanvasComponent(
|
|
||||||
children: [AndroidSpaceship(position: Vector2.zero())],
|
|
||||||
);
|
|
||||||
await game.ensureAdd(canvas);
|
|
||||||
game.camera.followVector2(Vector2.zero());
|
|
||||||
await game.ready();
|
|
||||||
await tester.pump();
|
|
||||||
},
|
|
||||||
verify: (game, tester) async {
|
|
||||||
final animationDuration = game
|
|
||||||
.descendants()
|
|
||||||
.whereType<SpriteAnimationComponent>()
|
|
||||||
.last
|
|
||||||
.animation!
|
|
||||||
.totalDuration();
|
|
||||||
|
|
||||||
await expectLater(
|
|
||||||
find.byGame<TestGame>(),
|
|
||||||
matchesGoldenFile('golden/android_spaceship/start.png'),
|
|
||||||
);
|
|
||||||
|
|
||||||
game.update(animationDuration * 0.5);
|
|
||||||
await tester.pump();
|
|
||||||
await expectLater(
|
|
||||||
find.byGame<TestGame>(),
|
|
||||||
matchesGoldenFile('golden/android_spaceship/middle.png'),
|
|
||||||
);
|
|
||||||
|
|
||||||
game.update(animationDuration * 0.5);
|
|
||||||
await tester.pump();
|
|
||||||
await expectLater(
|
|
||||||
find.byGame<TestGame>(),
|
|
||||||
matchesGoldenFile('golden/android_spaceship/end.png'),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 114 KiB |
@ -0,0 +1,79 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flame/extensions.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:pinball/game/components/android_acres/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.android.spaceship.saucer.keyName,
|
||||||
|
Assets.images.android.spaceship.animatronic.keyName,
|
||||||
|
Assets.images.android.spaceship.lightBeam.keyName,
|
||||||
|
Assets.images.android.ramp.boardOpening.keyName,
|
||||||
|
Assets.images.android.ramp.railingForeground.keyName,
|
||||||
|
Assets.images.android.ramp.railingBackground.keyName,
|
||||||
|
Assets.images.android.ramp.main.keyName,
|
||||||
|
Assets.images.android.ramp.arrow.inactive.keyName,
|
||||||
|
Assets.images.android.ramp.arrow.active1.keyName,
|
||||||
|
Assets.images.android.ramp.arrow.active2.keyName,
|
||||||
|
Assets.images.android.ramp.arrow.active3.keyName,
|
||||||
|
Assets.images.android.ramp.arrow.active4.keyName,
|
||||||
|
Assets.images.android.ramp.arrow.active5.keyName,
|
||||||
|
Assets.images.android.rail.main.keyName,
|
||||||
|
Assets.images.android.rail.exit.keyName,
|
||||||
|
Assets.images.android.bumper.a.lit.keyName,
|
||||||
|
Assets.images.android.bumper.a.dimmed.keyName,
|
||||||
|
Assets.images.android.bumper.b.lit.keyName,
|
||||||
|
Assets.images.android.bumper.b.dimmed.keyName,
|
||||||
|
Assets.images.android.bumper.cow.lit.keyName,
|
||||||
|
Assets.images.android.bumper.cow.dimmed.keyName,
|
||||||
|
];
|
||||||
|
|
||||||
|
group('AndroidSpaceshipBonusBehavior', () {
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
|
||||||
|
flameBlocTester.testGameWidget(
|
||||||
|
'adds GameBonus.androidSpaceship to the game '
|
||||||
|
'when android spacehship has a bonus',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
final behavior = AndroidSpaceshipBonusBehavior();
|
||||||
|
final parent = AndroidAcres.test();
|
||||||
|
final androidSpaceship = AndroidSpaceship(position: Vector2.zero());
|
||||||
|
|
||||||
|
await parent.add(androidSpaceship);
|
||||||
|
await game.ensureAdd(parent);
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
|
||||||
|
androidSpaceship.bloc.onBallEntered();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
verify(
|
||||||
|
() => gameBloc.add(const BonusActivated(GameBonus.androidSpaceship)),
|
||||||
|
).called(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|