mirror of https://github.com/flutter/pinball.git
feat: `SpaceshipRamp` shot logic (#296)
* feat: spaceship ramp added cubit and behavior to sensor * refactor: changed spaceshipt ramp sensor, cubit and behavior names * refactor: added behaviors to AndroidAcres * refactor: connect rampsensors with android acres bonus * refactor: move ramp sensor to spaceship ramp children * test: fixed some tests for ramp * chore: removed unused imports * chore: removed unused import * Update lib/game/components/android_acres/behaviors/ramp_bonus_behavior.dart Co-authored-by: Alejandro Santiago <dev@alestiago.com> * refactor: search sensors from parent children * refactor: moved ramp sensor cubit to spaceship ramp * refactor: modified ramp behaviors * refactor: fixed ramp behaviors tests * refactor: changed ramp behaviors * chore: analysis errors * test: fixed ramp contact test * test: coverage for spaceshipramp * Update packages/pinball_components/lib/src/components/spaceship_ramp/spaceship_ramp.dart Co-authored-by: Alejandro Santiago <dev@alestiago.com> * refactor: fixed test when removing children from spaceship test constructor * refactor: moved arrow state to cubit inside ramp instead of propagate from behavior * refactor: sandbox for spaceshipramp modified * refactor: removed arrow value from spaceship ramp state to sprite logic * test: golden tests for ramp arrow * test: coverage * test: coverage * refactor: changed name for RampBallAscendingContactBehavior * refactor: added ScoringBehavior on shot and bonus behaviors * feat: cancel subscription on ramp behavior remove * chore: unused import Co-authored-by: Alejandro Santiago <dev@alestiago.com>pull/338/head
parent
03f60fbffe
commit
79624f07f1
@ -1 +1,3 @@
|
|||||||
export 'android_spaceship_bonus_behavior.dart';
|
export 'android_spaceship_bonus_behavior.dart';
|
||||||
|
export 'ramp_bonus_behavior.dart';
|
||||||
|
export 'ramp_shot_behavior.dart';
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flutter/material.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';
|
||||||
|
|
||||||
|
/// {@template ramp_bonus_behavior}
|
||||||
|
/// Increases the score when a [Ball] is shot 10 times into the [SpaceshipRamp].
|
||||||
|
/// {@endtemplate}
|
||||||
|
class RampBonusBehavior extends Component
|
||||||
|
with ParentIsA<SpaceshipRamp>, HasGameRef<PinballGame> {
|
||||||
|
/// {@macro ramp_bonus_behavior}
|
||||||
|
RampBonusBehavior({
|
||||||
|
required Points points,
|
||||||
|
}) : _points = points,
|
||||||
|
super();
|
||||||
|
|
||||||
|
/// Creates a [RampBonusBehavior].
|
||||||
|
///
|
||||||
|
/// This can be used for testing [RampBonusBehavior] in isolation.
|
||||||
|
@visibleForTesting
|
||||||
|
RampBonusBehavior.test({
|
||||||
|
required Points points,
|
||||||
|
required this.subscription,
|
||||||
|
}) : _points = points,
|
||||||
|
super();
|
||||||
|
|
||||||
|
final Points _points;
|
||||||
|
|
||||||
|
/// Subscription to [SpaceshipRampState] at [SpaceshipRamp].
|
||||||
|
@visibleForTesting
|
||||||
|
StreamSubscription? subscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onMount() {
|
||||||
|
super.onMount();
|
||||||
|
|
||||||
|
subscription = subscription ??
|
||||||
|
parent.bloc.stream.listen((state) {
|
||||||
|
final achievedOneMillionPoints = state.hits % 10 == 0;
|
||||||
|
|
||||||
|
if (achievedOneMillionPoints) {
|
||||||
|
parent.add(
|
||||||
|
ScoringBehavior(
|
||||||
|
points: _points,
|
||||||
|
position: Vector2(0, -60),
|
||||||
|
duration: 2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onRemove() {
|
||||||
|
subscription?.cancel();
|
||||||
|
super.onRemove();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flutter/cupertino.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';
|
||||||
|
|
||||||
|
/// {@template ramp_shot_behavior}
|
||||||
|
/// Increases the score when a [Ball] is shot into the [SpaceshipRamp].
|
||||||
|
/// {@endtemplate}
|
||||||
|
class RampShotBehavior extends Component
|
||||||
|
with ParentIsA<SpaceshipRamp>, HasGameRef<PinballGame> {
|
||||||
|
/// {@macro ramp_shot_behavior}
|
||||||
|
RampShotBehavior({
|
||||||
|
required Points points,
|
||||||
|
}) : _points = points,
|
||||||
|
super();
|
||||||
|
|
||||||
|
/// Creates a [RampShotBehavior].
|
||||||
|
///
|
||||||
|
/// This can be used for testing [RampShotBehavior] in isolation.
|
||||||
|
@visibleForTesting
|
||||||
|
RampShotBehavior.test({
|
||||||
|
required Points points,
|
||||||
|
required this.subscription,
|
||||||
|
}) : _points = points,
|
||||||
|
super();
|
||||||
|
|
||||||
|
final Points _points;
|
||||||
|
|
||||||
|
/// Subscription to [SpaceshipRampState] at [SpaceshipRamp].
|
||||||
|
@visibleForTesting
|
||||||
|
StreamSubscription? subscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onMount() {
|
||||||
|
super.onMount();
|
||||||
|
|
||||||
|
subscription = subscription ??
|
||||||
|
parent.bloc.stream.listen((state) {
|
||||||
|
final achievedOneMillionPoints = state.hits % 10 == 0;
|
||||||
|
|
||||||
|
if (!achievedOneMillionPoints) {
|
||||||
|
gameRef.read<GameBloc>().add(const MultiplierIncreased());
|
||||||
|
|
||||||
|
parent.add(
|
||||||
|
ScoringBehavior(
|
||||||
|
points: _points,
|
||||||
|
position: Vector2(0, -45),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onRemove() {
|
||||||
|
subscription?.cancel();
|
||||||
|
super.onRemove();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export 'ramp_ball_ascending_contact_behavior.dart';
|
@ -0,0 +1,24 @@
|
|||||||
|
// 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';
|
||||||
|
|
||||||
|
/// {@template ramp_ball_ascending_contact_behavior}
|
||||||
|
/// Detects an ascending [Ball] that enters into the [SpaceshipRamp].
|
||||||
|
///
|
||||||
|
/// The [Ball] can hit with sensor to recognize if a [Ball] goes into or out of
|
||||||
|
/// the [SpaceshipRamp].
|
||||||
|
/// {@endtemplate}
|
||||||
|
class RampBallAscendingContactBehavior
|
||||||
|
extends ContactBehavior<RampScoringSensor> {
|
||||||
|
@override
|
||||||
|
void beginContact(Object other, Contact contact) {
|
||||||
|
super.beginContact(other, contact);
|
||||||
|
if (other is! Ball) return;
|
||||||
|
|
||||||
|
if (other.body.linearVelocity.y < 0) {
|
||||||
|
parent.parent.bloc.onAscendingBallEntered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
part 'spaceship_ramp_state.dart';
|
||||||
|
|
||||||
|
class SpaceshipRampCubit extends Cubit<SpaceshipRampState> {
|
||||||
|
SpaceshipRampCubit() : super(const SpaceshipRampState.initial());
|
||||||
|
|
||||||
|
void onAscendingBallEntered() {
|
||||||
|
emit(
|
||||||
|
state.copyWith(hits: state.hits + 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
|
part of 'spaceship_ramp_cubit.dart';
|
||||||
|
|
||||||
|
class SpaceshipRampState extends Equatable {
|
||||||
|
const SpaceshipRampState({
|
||||||
|
required this.hits,
|
||||||
|
}) : assert(hits >= 0, "Hits can't be negative");
|
||||||
|
|
||||||
|
const SpaceshipRampState.initial() : this(hits: 0);
|
||||||
|
|
||||||
|
final int hits;
|
||||||
|
|
||||||
|
SpaceshipRampState copyWith({
|
||||||
|
int? hits,
|
||||||
|
}) {
|
||||||
|
return SpaceshipRampState(
|
||||||
|
hits: hits ?? this.hits,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [hits];
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
// 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_components/pinball_components.dart';
|
||||||
|
import 'package:pinball_components/src/components/spaceship_ramp/behavior/behavior.dart';
|
||||||
|
|
||||||
|
import '../../../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
|
||||||
|
|
||||||
|
class _MockBall extends Mock implements Ball {}
|
||||||
|
|
||||||
|
class _MockBody extends Mock implements Body {}
|
||||||
|
|
||||||
|
class _MockContact extends Mock implements Contact {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final assets = [
|
||||||
|
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,
|
||||||
|
];
|
||||||
|
|
||||||
|
final flameTester = FlameTester(() => TestGame(assets));
|
||||||
|
|
||||||
|
group(
|
||||||
|
'RampBallAscendingContactBehavior',
|
||||||
|
() {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
RampBallAscendingContactBehavior(),
|
||||||
|
isA<RampBallAscendingContactBehavior>(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('beginContact', () {
|
||||||
|
late Ball ball;
|
||||||
|
late Body body;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
ball = _MockBall();
|
||||||
|
body = _MockBody();
|
||||||
|
|
||||||
|
when(() => ball.body).thenReturn(body);
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
"calls 'onAscendingBallEntered' when a ball enters into the ramp",
|
||||||
|
(game) async {
|
||||||
|
final behavior = RampBallAscendingContactBehavior();
|
||||||
|
final bloc = _MockSpaceshipRampCubit();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
const Stream<SpaceshipRampState>.empty(),
|
||||||
|
initialState: const SpaceshipRampState.initial(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final rampSensor = RampScoringSensor.test();
|
||||||
|
final spaceshipRamp = SpaceshipRamp.test(
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => body.linearVelocity).thenReturn(Vector2(0, -1));
|
||||||
|
|
||||||
|
await spaceshipRamp.add(rampSensor);
|
||||||
|
await game.ensureAddAll([spaceshipRamp, ball]);
|
||||||
|
await rampSensor.add(behavior);
|
||||||
|
|
||||||
|
behavior.beginContact(ball, _MockContact());
|
||||||
|
|
||||||
|
verify(bloc.onAscendingBallEntered).called(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
"doesn't call 'onAscendingBallEntered' when a ball goes out the ramp",
|
||||||
|
(game) async {
|
||||||
|
final behavior = RampBallAscendingContactBehavior();
|
||||||
|
final bloc = _MockSpaceshipRampCubit();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
const Stream<SpaceshipRampState>.empty(),
|
||||||
|
initialState: const SpaceshipRampState.initial(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final rampSensor = RampScoringSensor.test();
|
||||||
|
final spaceshipRamp = SpaceshipRamp.test(
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
when(() => body.linearVelocity).thenReturn(Vector2(0, 1));
|
||||||
|
|
||||||
|
await spaceshipRamp.add(rampSensor);
|
||||||
|
await game.ensureAddAll([spaceshipRamp, ball]);
|
||||||
|
await rampSensor.add(behavior);
|
||||||
|
|
||||||
|
behavior.beginContact(ball, _MockContact());
|
||||||
|
|
||||||
|
verifyNever(bloc.onAscendingBallEntered);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SpaceshipRampCubit', () {
|
||||||
|
group('onAscendingBallEntered', () {
|
||||||
|
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
|
||||||
|
'emits hits incremented and arrow goes to the next value',
|
||||||
|
build: SpaceshipRampCubit.new,
|
||||||
|
act: (bloc) => bloc
|
||||||
|
..onAscendingBallEntered()
|
||||||
|
..onAscendingBallEntered()
|
||||||
|
..onAscendingBallEntered(),
|
||||||
|
expect: () => [
|
||||||
|
SpaceshipRampState(hits: 1),
|
||||||
|
SpaceshipRampState(hits: 2),
|
||||||
|
SpaceshipRampState(hits: 3),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/src/components/components.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SpaceshipRampState', () {
|
||||||
|
test('supports value equality', () {
|
||||||
|
expect(
|
||||||
|
SpaceshipRampState(hits: 0),
|
||||||
|
equals(
|
||||||
|
SpaceshipRampState(hits: 0),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('constructor', () {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
SpaceshipRampState(hits: 0),
|
||||||
|
isNotNull,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'throws AssertionError '
|
||||||
|
'when hits is negative',
|
||||||
|
() {
|
||||||
|
expect(
|
||||||
|
() => SpaceshipRampState(hits: -1),
|
||||||
|
throwsAssertionError,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
group('copyWith', () {
|
||||||
|
test(
|
||||||
|
'throws AssertionError '
|
||||||
|
'when hits is decreased',
|
||||||
|
() {
|
||||||
|
const rampState = SpaceshipRampState(hits: 0);
|
||||||
|
expect(
|
||||||
|
() => rampState.copyWith(hits: rampState.hits - 1),
|
||||||
|
throwsAssertionError,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'copies correctly '
|
||||||
|
'when no argument specified',
|
||||||
|
() {
|
||||||
|
const rampState = SpaceshipRampState(hits: 0);
|
||||||
|
expect(
|
||||||
|
rampState.copyWith(),
|
||||||
|
equals(rampState),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'copies correctly '
|
||||||
|
'when all arguments specified',
|
||||||
|
() {
|
||||||
|
const rampState = SpaceshipRampState(hits: 0);
|
||||||
|
final otherRampState = SpaceshipRampState(hits: rampState.hits + 1);
|
||||||
|
expect(rampState, isNot(equals(otherRampState)));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rampState.copyWith(hits: rampState.hits + 1),
|
||||||
|
equals(otherRampState),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
// ignore_for_file: cascade_invocations, prefer_const_constructors
|
||||||
|
|
||||||
|
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:mocktail/mocktail.dart';
|
||||||
|
import 'package:pinball/game/behaviors/behaviors.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 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
|
import '../../../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
class _MockGameBloc extends Mock implements GameBloc {}
|
||||||
|
|
||||||
|
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
|
||||||
|
|
||||||
|
class _MockStreamSubscription extends Mock
|
||||||
|
implements StreamSubscription<SpaceshipRampState> {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final assets = [
|
||||||
|
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.score.oneMillion.keyName,
|
||||||
|
];
|
||||||
|
|
||||||
|
group('RampBonusBehavior', () {
|
||||||
|
const shotPoints = Points.oneMillion;
|
||||||
|
|
||||||
|
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(
|
||||||
|
'when hits are multiples of 10 times adds a ScoringBehavior',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
final bloc = _MockSpaceshipRampCubit();
|
||||||
|
final streamController = StreamController<SpaceshipRampState>();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
streamController.stream,
|
||||||
|
initialState: SpaceshipRampState(hits: 9),
|
||||||
|
);
|
||||||
|
final behavior = RampBonusBehavior(
|
||||||
|
points: shotPoints,
|
||||||
|
);
|
||||||
|
final parent = SpaceshipRamp.test(
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
await game.ensureAdd(ZCanvasComponent(children: [parent]));
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
|
||||||
|
streamController.add(SpaceshipRampState(hits: 10));
|
||||||
|
|
||||||
|
final scores = game.descendants().whereType<ScoringBehavior>();
|
||||||
|
await game.ready();
|
||||||
|
|
||||||
|
expect(scores.length, 1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameBlocTester.testGameWidget(
|
||||||
|
"when hits are not multiple of 10 times doesn't add any ScoringBehavior",
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
final bloc = _MockSpaceshipRampCubit();
|
||||||
|
final streamController = StreamController<SpaceshipRampState>();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
streamController.stream,
|
||||||
|
initialState: SpaceshipRampState.initial(),
|
||||||
|
);
|
||||||
|
final behavior = RampBonusBehavior(
|
||||||
|
points: shotPoints,
|
||||||
|
);
|
||||||
|
final parent = SpaceshipRamp.test(
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
await game.ensureAdd(ZCanvasComponent(children: [parent]));
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
|
||||||
|
streamController.add(SpaceshipRampState(hits: 1));
|
||||||
|
|
||||||
|
final scores = game.descendants().whereType<ScoringBehavior>();
|
||||||
|
await game.ready();
|
||||||
|
|
||||||
|
expect(scores.length, 0);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameBlocTester.testGameWidget(
|
||||||
|
'closes subscription when removed',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
final bloc = _MockSpaceshipRampCubit();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
const Stream<SpaceshipRampState>.empty(),
|
||||||
|
initialState: SpaceshipRampState.initial(),
|
||||||
|
);
|
||||||
|
when(bloc.close).thenAnswer((_) async {});
|
||||||
|
|
||||||
|
final subscription = _MockStreamSubscription();
|
||||||
|
when(subscription.cancel).thenAnswer((_) async {});
|
||||||
|
|
||||||
|
final behavior = RampBonusBehavior.test(
|
||||||
|
points: shotPoints,
|
||||||
|
subscription: subscription,
|
||||||
|
);
|
||||||
|
final parent = SpaceshipRamp.test(
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
await game.ensureAdd(ZCanvasComponent(children: [parent]));
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
|
||||||
|
parent.remove(behavior);
|
||||||
|
await game.ready();
|
||||||
|
|
||||||
|
verify(subscription.cancel).called(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,156 @@
|
|||||||
|
// ignore_for_file: cascade_invocations, prefer_const_constructors
|
||||||
|
|
||||||
|
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:mocktail/mocktail.dart';
|
||||||
|
import 'package:pinball/game/behaviors/behaviors.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 'package:pinball_flame/pinball_flame.dart';
|
||||||
|
|
||||||
|
import '../../../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
class _MockGameBloc extends Mock implements GameBloc {}
|
||||||
|
|
||||||
|
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
|
||||||
|
|
||||||
|
class _MockStreamSubscription extends Mock
|
||||||
|
implements StreamSubscription<SpaceshipRampState> {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final assets = [
|
||||||
|
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.score.fiveThousand.keyName,
|
||||||
|
];
|
||||||
|
|
||||||
|
group('RampShotBehavior', () {
|
||||||
|
const shotPoints = Points.fiveThousand;
|
||||||
|
|
||||||
|
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(
|
||||||
|
'when hits are not multiple of 10 times '
|
||||||
|
'increases multiplier and adds a ScoringBehavior',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
final bloc = _MockSpaceshipRampCubit();
|
||||||
|
final streamController = StreamController<SpaceshipRampState>();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
streamController.stream,
|
||||||
|
initialState: SpaceshipRampState.initial(),
|
||||||
|
);
|
||||||
|
final behavior = RampShotBehavior(
|
||||||
|
points: shotPoints,
|
||||||
|
);
|
||||||
|
final parent = SpaceshipRamp.test(
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
await game.ensureAdd(ZCanvasComponent(children: [parent]));
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
|
||||||
|
streamController.add(SpaceshipRampState(hits: 1));
|
||||||
|
|
||||||
|
final scores = game.descendants().whereType<ScoringBehavior>();
|
||||||
|
await game.ready();
|
||||||
|
|
||||||
|
verify(() => gameBloc.add(MultiplierIncreased())).called(1);
|
||||||
|
expect(scores.length, 1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameBlocTester.testGameWidget(
|
||||||
|
'when hits multiple of 10 times '
|
||||||
|
"doesn't increase multiplier, neither ScoringBehavior",
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
final bloc = _MockSpaceshipRampCubit();
|
||||||
|
final streamController = StreamController<SpaceshipRampState>();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
streamController.stream,
|
||||||
|
initialState: SpaceshipRampState(hits: 9),
|
||||||
|
);
|
||||||
|
final behavior = RampShotBehavior(
|
||||||
|
points: shotPoints,
|
||||||
|
);
|
||||||
|
final parent = SpaceshipRamp.test(
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
await game.ensureAdd(ZCanvasComponent(children: [parent]));
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
|
||||||
|
streamController.add(SpaceshipRampState(hits: 10));
|
||||||
|
|
||||||
|
final scores = game.children.whereType<ScoringBehavior>();
|
||||||
|
await game.ready();
|
||||||
|
|
||||||
|
verifyNever(() => gameBloc.add(MultiplierIncreased()));
|
||||||
|
expect(scores.length, 0);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameBlocTester.testGameWidget(
|
||||||
|
'closes subscription when removed',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
final bloc = _MockSpaceshipRampCubit();
|
||||||
|
whenListen(
|
||||||
|
bloc,
|
||||||
|
const Stream<SpaceshipRampState>.empty(),
|
||||||
|
initialState: SpaceshipRampState.initial(),
|
||||||
|
);
|
||||||
|
when(bloc.close).thenAnswer((_) async {});
|
||||||
|
|
||||||
|
final subscription = _MockStreamSubscription();
|
||||||
|
when(subscription.cancel).thenAnswer((_) async {});
|
||||||
|
|
||||||
|
final behavior = RampShotBehavior.test(
|
||||||
|
points: shotPoints,
|
||||||
|
subscription: subscription,
|
||||||
|
);
|
||||||
|
final parent = SpaceshipRamp.test(
|
||||||
|
bloc: bloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
await game.ensureAdd(ZCanvasComponent(children: [parent]));
|
||||||
|
await parent.ensureAdd(behavior);
|
||||||
|
|
||||||
|
parent.remove(behavior);
|
||||||
|
await game.ready();
|
||||||
|
|
||||||
|
verify(subscription.cancel).called(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue