@ -0,0 +1,2 @@
|
||||
export 'bumper_noisy_behavior.dart';
|
||||
export 'scoring_behavior.dart';
|
@ -0,0 +1,14 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball/game/pinball_game.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
class BumperNoisyBehavior extends ContactBehavior with HasGameRef<PinballGame> {
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
gameRef.audio.bumper();
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template scoring_behavior}
|
||||
/// Adds [_points] to the score and shows a text effect.
|
||||
///
|
||||
/// The behavior removes itself after the duration.
|
||||
/// {@endtemplate}
|
||||
class ScoringBehavior extends Component with HasGameRef<PinballGame> {
|
||||
/// {@macto scoring_behavior}
|
||||
ScoringBehavior({
|
||||
required Points points,
|
||||
required Vector2 position,
|
||||
double duration = 1,
|
||||
}) : _points = points,
|
||||
_position = position,
|
||||
_effectController = EffectController(
|
||||
duration: duration,
|
||||
);
|
||||
|
||||
final Points _points;
|
||||
final Vector2 _position;
|
||||
|
||||
final EffectController _effectController;
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
if (_effectController.completed) {
|
||||
removeFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
gameRef.read<GameBloc>().add(Scored(points: _points.value));
|
||||
await gameRef.firstChild<ZCanvasComponent>()!.add(
|
||||
ScoreComponent(
|
||||
points: _points,
|
||||
position: _position,
|
||||
effectController: _effectController,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template scoring_contact_behavior}
|
||||
/// Adds points to the score when the [Ball] contacts the [parent].
|
||||
/// {@endtemplate}
|
||||
class ScoringContactBehavior extends ContactBehavior
|
||||
with HasGameRef<PinballGame> {
|
||||
/// {@macro scoring_contact_behavior}
|
||||
ScoringContactBehavior({
|
||||
required Points points,
|
||||
}) : _points = points;
|
||||
|
||||
final Points _points;
|
||||
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
if (other is! Ball) return;
|
||||
|
||||
parent.add(
|
||||
ScoringBehavior(
|
||||
points: _points,
|
||||
position: other.body.position,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template scoring_behavior}
|
||||
/// Adds points to the score when the ball contacts the [parent].
|
||||
/// {@endtemplate}
|
||||
class ScoringBehavior extends ContactBehavior with HasGameRef<PinballGame> {
|
||||
/// {@macro scoring_behavior}
|
||||
ScoringBehavior({
|
||||
required Points points,
|
||||
}) : _points = points;
|
||||
|
||||
final Points _points;
|
||||
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
if (other is! Ball) return;
|
||||
|
||||
gameRef.read<GameBloc>().add(Scored(points: _points.value));
|
||||
gameRef.firstChild<ZCanvasComponent>()!.add(
|
||||
ScoreComponent(
|
||||
points: _points,
|
||||
position: other.body.position,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template bumper_scoring_behavior}
|
||||
/// A specific [ScoringBehavior] used for Bumpers.
|
||||
/// In addition to its parent logic, also plays the
|
||||
/// SFX for bumpers
|
||||
/// {@endtemplate}
|
||||
class BumperScoringBehavior extends ScoringBehavior {
|
||||
/// {@macro bumper_scoring_behavior}
|
||||
BumperScoringBehavior({
|
||||
required Points points,
|
||||
}) : super(points: points);
|
||||
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
if (other is! Ball) return;
|
||||
|
||||
gameRef.audio.bumper();
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
@ -0,0 +1,81 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template ball_turbo_charging_behavior}
|
||||
/// Puts the [Ball] in flames and [_impulse]s it.
|
||||
/// {@endtemplate}
|
||||
class BallTurboChargingBehavior extends TimerComponent with ParentIsA<Ball> {
|
||||
/// {@macro ball_turbo_charging_behavior}
|
||||
BallTurboChargingBehavior({
|
||||
required Vector2 impulse,
|
||||
}) : _impulse = impulse,
|
||||
super(period: 5, removeOnFinish: true);
|
||||
|
||||
final Vector2 _impulse;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
parent.body.linearVelocity = _impulse;
|
||||
await parent.add(_TurboChargeSpriteAnimationComponent());
|
||||
}
|
||||
|
||||
@override
|
||||
void onRemove() {
|
||||
parent
|
||||
.firstChild<_TurboChargeSpriteAnimationComponent>()!
|
||||
.removeFromParent();
|
||||
super.onRemove();
|
||||
}
|
||||
}
|
||||
|
||||
class _TurboChargeSpriteAnimationComponent extends SpriteAnimationComponent
|
||||
with HasGameRef, ZIndex, ParentIsA<Ball> {
|
||||
_TurboChargeSpriteAnimationComponent()
|
||||
: super(
|
||||
anchor: const Anchor(0.53, 0.72),
|
||||
) {
|
||||
zIndex = ZIndexes.turboChargeFlame;
|
||||
}
|
||||
|
||||
late final Vector2 _textureSize;
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
final direction = -parent.body.linearVelocity.normalized();
|
||||
angle = math.atan2(direction.x, -direction.y);
|
||||
size = (_textureSize / 45) * parent.body.fixtures.first.shape.radius;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final spriteSheet = await gameRef.images.load(
|
||||
Assets.images.ball.flameEffect.keyName,
|
||||
);
|
||||
|
||||
const amountPerRow = 8;
|
||||
const amountPerColumn = 4;
|
||||
_textureSize = Vector2(
|
||||
spriteSheet.width / amountPerRow,
|
||||
spriteSheet.height / amountPerColumn,
|
||||
);
|
||||
|
||||
animation = SpriteAnimation.fromFrameData(
|
||||
spriteSheet,
|
||||
SpriteAnimationData.sequenced(
|
||||
amount: amountPerRow * amountPerColumn,
|
||||
amountPerRow: amountPerRow,
|
||||
stepTime: 1 / 24,
|
||||
textureSize: _textureSize,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
export 'ball_gravitating_behavior.dart';
|
||||
export 'ball_scaling_behavior.dart';
|
||||
export 'ball_turbo_charging_behavior.dart';
|
||||
|
@ -0,0 +1,94 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group(
|
||||
'BallTurboChargingBehavior',
|
||||
() {
|
||||
final assets = [Assets.images.ball.ball.keyName];
|
||||
final flameTester = FlameTester(() => TestGame(assets));
|
||||
const baseColor = Color(0xFFFFFFFF);
|
||||
|
||||
test('can be instantiated', () {
|
||||
expect(
|
||||
BallTurboChargingBehavior(impulse: Vector2.zero()),
|
||||
isA<BallTurboChargingBehavior>(),
|
||||
);
|
||||
});
|
||||
|
||||
flameTester.test('can be loaded', (game) async {
|
||||
final ball = Ball.test(baseColor: baseColor);
|
||||
final behavior = BallTurboChargingBehavior(impulse: Vector2.zero());
|
||||
await ball.add(behavior);
|
||||
await game.ensureAdd(ball);
|
||||
expect(
|
||||
ball.firstChild<BallTurboChargingBehavior>(),
|
||||
equals(behavior),
|
||||
);
|
||||
});
|
||||
|
||||
flameTester.test(
|
||||
'impulses the ball velocity when loaded',
|
||||
(game) async {
|
||||
final ball = Ball.test(baseColor: baseColor);
|
||||
await game.ensureAdd(ball);
|
||||
final impulse = Vector2.all(1);
|
||||
final behavior = BallTurboChargingBehavior(impulse: impulse);
|
||||
await ball.ensureAdd(behavior);
|
||||
|
||||
expect(
|
||||
ball.body.linearVelocity.x,
|
||||
equals(impulse.x),
|
||||
);
|
||||
expect(
|
||||
ball.body.linearVelocity.y,
|
||||
equals(impulse.y),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test('adds sprite', (game) async {
|
||||
final ball = Ball(baseColor: baseColor);
|
||||
await game.ensureAdd(ball);
|
||||
|
||||
await ball.ensureAdd(
|
||||
BallTurboChargingBehavior(impulse: Vector2.zero()),
|
||||
);
|
||||
|
||||
expect(
|
||||
ball.children.whereType<SpriteAnimationComponent>().single,
|
||||
isNotNull,
|
||||
);
|
||||
});
|
||||
|
||||
flameTester.test('removes sprite after it finishes', (game) async {
|
||||
final ball = Ball(baseColor: baseColor);
|
||||
await game.ensureAdd(ball);
|
||||
|
||||
final behavior = BallTurboChargingBehavior(impulse: Vector2.zero());
|
||||
await ball.ensureAdd(behavior);
|
||||
|
||||
final turboChargeSpriteAnimation =
|
||||
ball.children.whereType<SpriteAnimationComponent>().single;
|
||||
|
||||
expect(ball.contains(turboChargeSpriteAnimation), isTrue);
|
||||
|
||||
game.update(behavior.timer.limit);
|
||||
game.update(0.1);
|
||||
|
||||
expect(ball.contains(turboChargeSpriteAnimation), isFalse);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 80 KiB |
@ -0,0 +1,50 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball/game/behaviors/behaviors.dart';
|
||||
import 'package:pinball_audio/pinball_audio.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
class _TestBodyComponent extends BodyComponent {
|
||||
@override
|
||||
Body createBody() {
|
||||
return world.createBody(BodyDef());
|
||||
}
|
||||
}
|
||||
|
||||
class _MockPinballAudio extends Mock implements PinballAudio {}
|
||||
|
||||
class _MockContact extends Mock implements Contact {}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('BumperNoisyBehavior', () {});
|
||||
|
||||
late PinballAudio audio;
|
||||
final flameTester = FlameTester(
|
||||
() => EmptyPinballTestGame(audio: audio),
|
||||
);
|
||||
|
||||
setUp(() {
|
||||
audio = _MockPinballAudio();
|
||||
});
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'plays bumper sound',
|
||||
setUp: (game, _) async {
|
||||
final behavior = BumperNoisyBehavior();
|
||||
final parent = _TestBodyComponent();
|
||||
await game.ensureAdd(parent);
|
||||
await parent.ensureAdd(behavior);
|
||||
behavior.beginContact(Object(), _MockContact());
|
||||
},
|
||||
verify: (_, __) async {
|
||||
verify(audio.bumper).called(1);
|
||||
},
|
||||
);
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball/game/behaviors/behaviors.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
class _TestBodyComponent extends BodyComponent {
|
||||
@override
|
||||
Body createBody() => world.createBody(BodyDef());
|
||||
}
|
||||
|
||||
class _MockBall extends Mock implements Ball {}
|
||||
|
||||
class _MockBody extends Mock implements Body {}
|
||||
|
||||
class _MockGameBloc extends Mock implements GameBloc {}
|
||||
|
||||
class _MockContact extends Mock implements Contact {}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final assets = [
|
||||
Assets.images.score.fiveThousand.keyName,
|
||||
Assets.images.score.twentyThousand.keyName,
|
||||
Assets.images.score.twoHundredThousand.keyName,
|
||||
Assets.images.score.oneMillion.keyName,
|
||||
];
|
||||
|
||||
late GameBloc bloc;
|
||||
late Ball ball;
|
||||
late BodyComponent parent;
|
||||
|
||||
setUp(() {
|
||||
ball = _MockBall();
|
||||
final ballBody = _MockBody();
|
||||
when(() => ball.body).thenReturn(ballBody);
|
||||
when(() => ballBody.position).thenReturn(Vector2.all(4));
|
||||
|
||||
parent = _TestBodyComponent();
|
||||
});
|
||||
|
||||
final flameBlocTester = FlameBlocTester<EmptyPinballTestGame, GameBloc>(
|
||||
gameBuilder: EmptyPinballTestGame.new,
|
||||
blocBuilder: () {
|
||||
bloc = _MockGameBloc();
|
||||
const state = GameState(
|
||||
score: 0,
|
||||
multiplier: 1,
|
||||
rounds: 3,
|
||||
bonusHistory: [],
|
||||
);
|
||||
whenListen(bloc, Stream.value(state), initialState: state);
|
||||
return bloc;
|
||||
},
|
||||
assets: assets,
|
||||
);
|
||||
|
||||
group('ScoringBehavior', () {
|
||||
test('can be instantiated', () {
|
||||
expect(
|
||||
ScoringBehavior(
|
||||
points: Points.fiveThousand,
|
||||
position: Vector2.zero(),
|
||||
),
|
||||
isA<ScoringBehavior>(),
|
||||
);
|
||||
});
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'can be loaded',
|
||||
setUp: (game, tester) async {
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
final behavior = ScoringBehavior(
|
||||
points: Points.fiveThousand,
|
||||
position: Vector2.zero(),
|
||||
);
|
||||
await parent.add(behavior);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
expect(
|
||||
parent.firstChild<ScoringBehavior>(),
|
||||
equals(behavior),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'emits Scored event with points when added',
|
||||
setUp: (game, tester) async {
|
||||
const points = Points.oneMillion;
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
final behavior = ScoringBehavior(
|
||||
points: points,
|
||||
position: Vector2(0, 0),
|
||||
);
|
||||
await parent.ensureAdd(behavior);
|
||||
|
||||
verify(
|
||||
() => bloc.add(
|
||||
Scored(points: points.value),
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'correctly renders text',
|
||||
setUp: (game, tester) async {
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
const points = Points.oneMillion;
|
||||
final position = Vector2.all(1);
|
||||
final behavior = ScoringBehavior(
|
||||
points: points,
|
||||
position: position,
|
||||
);
|
||||
await parent.ensureAdd(behavior);
|
||||
|
||||
final scoreText = game.descendants().whereType<ScoreComponent>();
|
||||
expect(scoreText.length, equals(1));
|
||||
expect(
|
||||
scoreText.first.points,
|
||||
equals(points),
|
||||
);
|
||||
expect(
|
||||
scoreText.first.position,
|
||||
equals(position),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'is removed after duration',
|
||||
setUp: (game, tester) async {
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
const duration = 2.0;
|
||||
final behavior = ScoringBehavior(
|
||||
points: Points.oneMillion,
|
||||
position: Vector2(0, 0),
|
||||
duration: duration,
|
||||
);
|
||||
await parent.ensureAdd(behavior);
|
||||
|
||||
game.update(duration);
|
||||
game.update(0);
|
||||
await tester.pump();
|
||||
},
|
||||
verify: (game, _) async {
|
||||
expect(
|
||||
game.descendants().whereType<ScoringBehavior>(),
|
||||
isEmpty,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('ScoringContactBehavior', () {
|
||||
flameBlocTester.testGameWidget(
|
||||
'beginContact adds a ScoringBehavior',
|
||||
setUp: (game, tester) async {
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
final behavior = ScoringContactBehavior(points: Points.oneMillion);
|
||||
await parent.ensureAdd(behavior);
|
||||
|
||||
behavior.beginContact(ball, _MockContact());
|
||||
await game.ready();
|
||||
|
||||
expect(
|
||||
parent.firstChild<ScoringBehavior>(),
|
||||
isNotNull,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
"beginContact positions text at contact's position",
|
||||
setUp: (game, tester) async {
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
final behavior = ScoringContactBehavior(points: Points.oneMillion);
|
||||
await parent.ensureAdd(behavior);
|
||||
|
||||
behavior.beginContact(ball, _MockContact());
|
||||
await game.ready();
|
||||
|
||||
final scoreText = game.descendants().whereType<ScoreComponent>();
|
||||
expect(
|
||||
scoreText.first.position,
|
||||
equals(ball.body.position),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
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_audio/pinball_audio.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
class _TestBodyComponent extends BodyComponent {
|
||||
@override
|
||||
Body createBody() => world.createBody(BodyDef());
|
||||
}
|
||||
|
||||
class _MockPinballAudio extends Mock implements PinballAudio {}
|
||||
|
||||
class _MockBall extends Mock implements Ball {}
|
||||
|
||||
class _MockBody extends Mock implements Body {}
|
||||
|
||||
class _MockGameBloc extends Mock implements GameBloc {}
|
||||
|
||||
class _MockContact extends Mock implements Contact {}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final assets = [
|
||||
Assets.images.score.fiveThousand.keyName,
|
||||
Assets.images.score.twentyThousand.keyName,
|
||||
Assets.images.score.twoHundredThousand.keyName,
|
||||
Assets.images.score.oneMillion.keyName,
|
||||
];
|
||||
|
||||
group('ScoringBehavior', () {
|
||||
group('beginContact', () {
|
||||
late GameBloc bloc;
|
||||
late PinballAudio audio;
|
||||
late Ball ball;
|
||||
late BodyComponent parent;
|
||||
|
||||
setUp(() {
|
||||
audio = _MockPinballAudio();
|
||||
ball = _MockBall();
|
||||
final ballBody = _MockBody();
|
||||
when(() => ball.body).thenReturn(ballBody);
|
||||
when(() => ballBody.position).thenReturn(Vector2.all(4));
|
||||
|
||||
parent = _TestBodyComponent();
|
||||
});
|
||||
|
||||
final flameBlocTester = FlameBlocTester<EmptyPinballTestGame, GameBloc>(
|
||||
gameBuilder: () => EmptyPinballTestGame(
|
||||
audio: audio,
|
||||
),
|
||||
blocBuilder: () {
|
||||
bloc = _MockGameBloc();
|
||||
const state = GameState(
|
||||
score: 0,
|
||||
multiplier: 1,
|
||||
rounds: 3,
|
||||
bonusHistory: [],
|
||||
);
|
||||
whenListen(bloc, Stream.value(state), initialState: state);
|
||||
return bloc;
|
||||
},
|
||||
assets: assets,
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'emits Scored event with points',
|
||||
setUp: (game, tester) async {
|
||||
const points = Points.oneMillion;
|
||||
final scoringBehavior = ScoringBehavior(points: points);
|
||||
await parent.add(scoringBehavior);
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
scoringBehavior.beginContact(ball, _MockContact());
|
||||
|
||||
verify(
|
||||
() => bloc.add(
|
||||
Scored(points: points.value),
|
||||
),
|
||||
).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
"adds a ScoreComponent at Ball's position with points",
|
||||
setUp: (game, tester) async {
|
||||
const points = Points.oneMillion;
|
||||
final scoringBehavior = ScoringBehavior(points: points);
|
||||
await parent.add(scoringBehavior);
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
scoringBehavior.beginContact(ball, _MockContact());
|
||||
await game.ready();
|
||||
|
||||
final scoreText = game.descendants().whereType<ScoreComponent>();
|
||||
expect(scoreText.length, equals(1));
|
||||
expect(
|
||||
scoreText.first.points,
|
||||
equals(points),
|
||||
);
|
||||
expect(
|
||||
scoreText.first.position,
|
||||
equals(ball.body.position),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('BumperScoringBehavior', () {
|
||||
group('beginContact', () {
|
||||
late GameBloc bloc;
|
||||
late PinballAudio audio;
|
||||
late Ball ball;
|
||||
late BodyComponent parent;
|
||||
|
||||
setUp(() {
|
||||
audio = _MockPinballAudio();
|
||||
ball = _MockBall();
|
||||
final ballBody = _MockBody();
|
||||
when(() => ball.body).thenReturn(ballBody);
|
||||
when(() => ballBody.position).thenReturn(Vector2.all(4));
|
||||
|
||||
parent = _TestBodyComponent();
|
||||
});
|
||||
|
||||
final flameBlocTester = FlameBlocTester<EmptyPinballTestGame, GameBloc>(
|
||||
gameBuilder: () => EmptyPinballTestGame(
|
||||
audio: audio,
|
||||
),
|
||||
blocBuilder: () {
|
||||
bloc = _MockGameBloc();
|
||||
const state = GameState(
|
||||
score: 0,
|
||||
multiplier: 1,
|
||||
rounds: 3,
|
||||
bonusHistory: [],
|
||||
);
|
||||
whenListen(bloc, Stream.value(state), initialState: state);
|
||||
return bloc;
|
||||
},
|
||||
assets: assets,
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'plays bumper sound',
|
||||
setUp: (game, tester) async {
|
||||
final scoringBehavior = BumperScoringBehavior(
|
||||
points: Points.oneMillion,
|
||||
);
|
||||
await parent.add(scoringBehavior);
|
||||
final canvas = ZCanvasComponent(children: [parent]);
|
||||
await game.ensureAdd(canvas);
|
||||
|
||||
scoringBehavior.beginContact(ball, _MockContact());
|
||||
|
||||
verify(audio.bumper).called(1);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|