mirror of https://github.com/flutter/pinball.git
fix: `SpaceshipRamp` logic (#416)
* feat: added behaviors for arrow * feat: arrow behaviors * refactor: changed ramp behaviors to new flame bloc * feat: reset and blinking on ramp * fix: added behaviors to ramp and some tests * test: coverage * refactor: spaceship ramp sandbox * chore: clean prints * refactor: unnecessary test methods * chore: removed unused files * refactor: remove arrow blinking from this pr * test: coverage * refactor: ramp bonus listen when refactored * refactor: moved FlameBlocProvider inside SpaceshipRamp * refactor: moved FlameBlocProvider inside SpaceshipRamp and refactor tests * chore: removed tests failures * test: removed golden tests for spaceship * chore: failure tests images * test: refactor test * test: coverage * Update test/game/components/android_acres/behaviors/ramp_progress_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/android_acres/behaviors/ramp_reset_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/android_acres/behaviors/ramp_shot_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/android_acres/behaviors/ramp_reset_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/android_acres/behaviors/ramp_progress_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/android_acres/behaviors/ramp_progress_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/android_acres/behaviors/ramp_progress_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/android_acres/behaviors/ramp_progress_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update test/game/components/android_acres/behaviors/ramp_progress_behavior_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: flamebloclistenable * Update packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * Update packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> * refactor: changed listenWhen conditions * chore: formatting * test: coverage * test: multiblocprovider * Update packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit.dart Co-authored-by: Alejandro Santiago <dev@alestiago.com> * Update packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_state.dart Co-authored-by: Alejandro Santiago <dev@alestiago.com> * test: refactored tests * refactor: max multiplier * test: fixed error in test * refactor: removed trailing commas * test: used ensureAdd * test: awaited zero duration to trigger next event loop * test: triggered next event loop * test: triggered next event loop Co-authored-by: Allison Ryan <77211884+allisonryan0002@users.noreply.github.com> Co-authored-by: Alejandro Santiago <dev@alestiago.com>pull/451/head
parent
394e1fe724
commit
32e3e8d641
@ -1,3 +1,6 @@
|
||||
export 'android_spaceship_bonus_behavior.dart';
|
||||
export 'ramp_bonus_behavior.dart';
|
||||
export 'ramp_multiplier_behavior.dart';
|
||||
export 'ramp_progress_behavior.dart';
|
||||
export 'ramp_reset_behavior.dart';
|
||||
export 'ramp_shot_behavior.dart';
|
||||
|
@ -1,60 +1,40 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:pinball/game/behaviors/behaviors.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> {
|
||||
class RampBonusBehavior extends Component
|
||||
with FlameBlocListenable<SpaceshipRampCubit, SpaceshipRampState> {
|
||||
/// {@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,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
bool listenWhen(
|
||||
SpaceshipRampState previousState,
|
||||
SpaceshipRampState newState,
|
||||
) {
|
||||
final hitsIncreased = previousState.hits < newState.hits;
|
||||
final achievedOneMillionPoints = newState.hits % 10 == 0;
|
||||
|
||||
return hitsIncreased && achievedOneMillionPoints;
|
||||
}
|
||||
|
||||
@override
|
||||
void onRemove() {
|
||||
subscription?.cancel();
|
||||
super.onRemove();
|
||||
void onNewState(SpaceshipRampState state) {
|
||||
parent!.add(
|
||||
ScoringBehavior(
|
||||
points: _points,
|
||||
position: Vector2(0, -60),
|
||||
duration: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// Increases the multiplier when a [Ball] is shot 5 times into the
|
||||
/// [SpaceshipRamp].
|
||||
class RampMultiplierBehavior extends Component
|
||||
with FlameBlocListenable<SpaceshipRampCubit, SpaceshipRampState> {
|
||||
@override
|
||||
bool listenWhen(
|
||||
SpaceshipRampState previousState,
|
||||
SpaceshipRampState newState,
|
||||
) {
|
||||
final hitsIncreased = previousState.hits < newState.hits;
|
||||
final achievedFiveShots = newState.hits % 5 == 0;
|
||||
final notMaxMultiplier =
|
||||
!readBloc<GameBloc, GameState>().state.isMaxMultiplier;
|
||||
return hitsIncreased & achievedFiveShots && notMaxMultiplier;
|
||||
}
|
||||
|
||||
@override
|
||||
void onNewState(SpaceshipRampState state) {
|
||||
readBloc<GameBloc, GameState>().add(const MultiplierIncreased());
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// Changes arrow lit when a [Ball] is shot into the [SpaceshipRamp].
|
||||
class RampProgressBehavior extends Component
|
||||
with FlameBlocListenable<SpaceshipRampCubit, SpaceshipRampState> {
|
||||
@override
|
||||
bool listenWhen(
|
||||
SpaceshipRampState previousState,
|
||||
SpaceshipRampState newState,
|
||||
) {
|
||||
return previousState.hits < newState.hits;
|
||||
}
|
||||
|
||||
@override
|
||||
void onNewState(SpaceshipRampState state) {
|
||||
final gameBloc = readBloc<GameBloc, GameState>();
|
||||
final spaceshipCubit = readBloc<SpaceshipRampCubit, SpaceshipRampState>();
|
||||
|
||||
final canProgress = !gameBloc.state.isMaxMultiplier ||
|
||||
(gameBloc.state.isMaxMultiplier && !state.arrowFullyLit);
|
||||
|
||||
if (canProgress) {
|
||||
spaceshipCubit.onProgressed();
|
||||
}
|
||||
|
||||
if (spaceshipCubit.state.arrowFullyLit && !gameBloc.state.isMaxMultiplier) {
|
||||
spaceshipCubit.onProgressed();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// Reset [SpaceshipRamp] state when GameState.rounds changes.
|
||||
class RampResetBehavior extends Component
|
||||
with FlameBlocListenable<GameBloc, GameState> {
|
||||
@override
|
||||
bool listenWhen(GameState previousState, GameState newState) {
|
||||
return previousState.rounds != newState.rounds;
|
||||
}
|
||||
|
||||
@override
|
||||
void onNewState(GameState state) {
|
||||
readBloc<SpaceshipRampCubit, SpaceshipRampState>().onReset();
|
||||
}
|
||||
}
|
@ -1,64 +1,36 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.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>, FlameBlocReader<GameBloc, GameState> {
|
||||
with FlameBlocListenable<SpaceshipRampCubit, SpaceshipRampState> {
|
||||
/// {@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) {
|
||||
bloc.add(const MultiplierIncreased());
|
||||
|
||||
parent.add(
|
||||
ScoringBehavior(
|
||||
points: _points,
|
||||
position: Vector2(0, -45),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
bool listenWhen(
|
||||
SpaceshipRampState previousState,
|
||||
SpaceshipRampState newState,
|
||||
) {
|
||||
return previousState.hits < newState.hits;
|
||||
}
|
||||
|
||||
@override
|
||||
void onRemove() {
|
||||
subscription?.cancel();
|
||||
super.onRemove();
|
||||
void onNewState(SpaceshipRampState state) {
|
||||
parent!.add(
|
||||
ScoringBehavior(
|
||||
points: _points,
|
||||
position: Vector2(0, -45),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,55 @@
|
||||
// ignore_for_file: comment_references
|
||||
|
||||
part of 'spaceship_ramp_cubit.dart';
|
||||
|
||||
class SpaceshipRampState extends Equatable {
|
||||
const SpaceshipRampState({
|
||||
required this.hits,
|
||||
required this.lightState,
|
||||
}) : assert(hits >= 0, "Hits can't be negative");
|
||||
|
||||
const SpaceshipRampState.initial() : this(hits: 0);
|
||||
const SpaceshipRampState.initial()
|
||||
: this(
|
||||
hits: 0,
|
||||
lightState: ArrowLightState.inactive,
|
||||
);
|
||||
|
||||
final int hits;
|
||||
final ArrowLightState lightState;
|
||||
|
||||
bool get arrowFullyLit => lightState == ArrowLightState.active5;
|
||||
|
||||
SpaceshipRampState copyWith({
|
||||
int? hits,
|
||||
ArrowLightState? lightState,
|
||||
}) {
|
||||
return SpaceshipRampState(
|
||||
hits: hits ?? this.hits,
|
||||
lightState: lightState ?? this.lightState,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [hits];
|
||||
List<Object?> get props => [hits, lightState];
|
||||
}
|
||||
|
||||
/// Indicates the state of the arrow on the [SpaceshipRamp].
|
||||
enum ArrowLightState {
|
||||
/// Arrow with no lights lit up.
|
||||
inactive,
|
||||
|
||||
/// Arrow with 1 light lit up.
|
||||
active1,
|
||||
|
||||
/// Arrow with 2 lights lit up.
|
||||
active2,
|
||||
|
||||
/// Arrow with 3 lights lit up.
|
||||
active3,
|
||||
|
||||
/// Arrow with 4 lights lit up.
|
||||
active4,
|
||||
|
||||
/// Arrow with all 5 lights lit up.
|
||||
active5,
|
||||
}
|
||||
|
@ -0,0 +1,183 @@
|
||||
// ignore_for_file: cascade_invocations, prefer_const_constructors
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flame_bloc/flame_bloc.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/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';
|
||||
|
||||
class _TestGame extends Forge2DGame {
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
images.prefix = '';
|
||||
await images.loadAll([
|
||||
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,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> pump(
|
||||
SpaceshipRamp child, {
|
||||
required GameBloc gameBloc,
|
||||
required SpaceshipRampCubit bloc,
|
||||
}) async {
|
||||
await ensureAdd(
|
||||
FlameMultiBlocProvider(
|
||||
providers: [
|
||||
FlameBlocProvider<GameBloc, GameState>.value(
|
||||
value: gameBloc,
|
||||
),
|
||||
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.value(
|
||||
value: bloc,
|
||||
),
|
||||
],
|
||||
children: [
|
||||
ZCanvasComponent(children: [child]),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MockGameBloc extends Mock implements GameBloc {}
|
||||
|
||||
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
|
||||
|
||||
class _FakeGameEvent extends Fake implements GameEvent {}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('RampMultiplierBehavior', () {
|
||||
late GameBloc gameBloc;
|
||||
|
||||
setUp(() {
|
||||
registerFallbackValue(_FakeGameEvent());
|
||||
gameBloc = _MockGameBloc();
|
||||
});
|
||||
|
||||
final flameTester = FlameTester(_TestGame.new);
|
||||
|
||||
flameTester.test(
|
||||
'adds MultiplierIncreased '
|
||||
'when hits are multiples of 5 times and multiplier is less than 6',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 5,
|
||||
),
|
||||
);
|
||||
when(() => gameBloc.add(any())).thenAnswer((_) async {});
|
||||
|
||||
final behavior = RampMultiplierBehavior();
|
||||
final parent = SpaceshipRamp.test(children: [behavior]);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(state.copyWith(hits: 5));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verify(() => gameBloc.add(const MultiplierIncreased())).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
"doesn't add MultiplierIncreased "
|
||||
'when hits are multiples of 5 times but multiplier is 6',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 6,
|
||||
),
|
||||
);
|
||||
|
||||
final behavior = RampMultiplierBehavior();
|
||||
final parent = SpaceshipRamp.test(children: [behavior]);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(state.copyWith(hits: 5));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verifyNever(() => gameBloc.add(const MultiplierIncreased()));
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
"doesn't add MultiplierIncreased "
|
||||
"when hits aren't multiples of 5 times",
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 5,
|
||||
),
|
||||
);
|
||||
|
||||
final behavior = RampMultiplierBehavior();
|
||||
final parent = SpaceshipRamp.test(children: [behavior]);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(state.copyWith(hits: 1));
|
||||
|
||||
await game.ready();
|
||||
|
||||
verifyNever(() => gameBloc.add(const MultiplierIncreased()));
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -0,0 +1,309 @@
|
||||
// ignore_for_file: cascade_invocations, prefer_const_constructors
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flame_bloc/flame_bloc.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/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';
|
||||
|
||||
class _TestGame extends Forge2DGame {
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
images.prefix = '';
|
||||
await images.loadAll([
|
||||
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,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> pump(
|
||||
SpaceshipRamp child, {
|
||||
required GameBloc gameBloc,
|
||||
required SpaceshipRampCubit bloc,
|
||||
}) async {
|
||||
await ensureAdd(
|
||||
FlameMultiBlocProvider(
|
||||
providers: [
|
||||
FlameBlocProvider<GameBloc, GameState>.value(
|
||||
value: gameBloc,
|
||||
),
|
||||
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.value(
|
||||
value: bloc,
|
||||
),
|
||||
],
|
||||
children: [
|
||||
ZCanvasComponent(children: [child]),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MockGameBloc extends Mock implements GameBloc {}
|
||||
|
||||
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
|
||||
|
||||
class _FakeGameEvent extends Fake implements GameEvent {}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('RampProgressBehavior', () {
|
||||
late GameBloc gameBloc;
|
||||
|
||||
setUp(() {
|
||||
registerFallbackValue(_FakeGameEvent());
|
||||
gameBloc = _MockGameBloc();
|
||||
});
|
||||
|
||||
final flameTester = FlameTester(_TestGame.new);
|
||||
|
||||
flameTester.test(
|
||||
'adds onProgressed '
|
||||
'when hits and multiplier are less than 6',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 1,
|
||||
),
|
||||
);
|
||||
|
||||
final behavior = RampProgressBehavior();
|
||||
final parent = SpaceshipRamp.test(
|
||||
children: [behavior],
|
||||
);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(state.copyWith(hits: 5));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verify(bloc.onProgressed).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'adds onProgressed '
|
||||
'when hits and multiplier are 6 but arrow is not fully lit',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 6,
|
||||
),
|
||||
);
|
||||
|
||||
final behavior = RampProgressBehavior();
|
||||
final parent = SpaceshipRamp.test(
|
||||
children: [behavior],
|
||||
);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(state.copyWith(hits: 5));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verify(bloc.onProgressed).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
"doesn't add onProgressed "
|
||||
'when hits and multiplier are 6 and arrow is fully lit',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 6,
|
||||
),
|
||||
);
|
||||
|
||||
final behavior = RampProgressBehavior();
|
||||
final parent = SpaceshipRamp.test(
|
||||
children: [behavior],
|
||||
);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(
|
||||
state.copyWith(
|
||||
hits: 5,
|
||||
lightState: ArrowLightState.active5,
|
||||
),
|
||||
);
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verifyNever(bloc.onProgressed);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'adds onProgressed to dim arrow '
|
||||
'when arrow is fully lit after hit and multiplier is less than 6',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 5,
|
||||
),
|
||||
);
|
||||
|
||||
final behavior = RampProgressBehavior();
|
||||
final parent = SpaceshipRamp.test(
|
||||
children: [behavior],
|
||||
);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(
|
||||
state.copyWith(
|
||||
hits: 5,
|
||||
lightState: ArrowLightState.active5,
|
||||
),
|
||||
);
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verify(bloc.onProgressed).called(2);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
"doesn't add onProgressed to dim arrow "
|
||||
'when arrow is not fully lit after hit',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 5,
|
||||
),
|
||||
);
|
||||
|
||||
final behavior = RampProgressBehavior();
|
||||
final parent = SpaceshipRamp.test(
|
||||
children: [behavior],
|
||||
);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(
|
||||
state.copyWith(
|
||||
hits: 4,
|
||||
lightState: ArrowLightState.active4,
|
||||
),
|
||||
);
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verify(bloc.onProgressed).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
"doesn't add onProgressed to dim arrow "
|
||||
'when multiplier is 6 after hit',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = SpaceshipRampState.initial();
|
||||
final streamController = StreamController<SpaceshipRampState>();
|
||||
whenListen(
|
||||
bloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
when(() => gameBloc.state).thenReturn(
|
||||
GameState.initial().copyWith(
|
||||
multiplier: 6,
|
||||
),
|
||||
);
|
||||
|
||||
final behavior = RampProgressBehavior();
|
||||
final parent = SpaceshipRamp.test(
|
||||
children: [behavior],
|
||||
);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(
|
||||
state.copyWith(hits: 4),
|
||||
);
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verify(bloc.onProgressed).called(1);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
// ignore_for_file: cascade_invocations, prefer_const_constructors
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flame_bloc/flame_bloc.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/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';
|
||||
|
||||
class _TestGame extends Forge2DGame {
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
images.prefix = '';
|
||||
await images.loadAll([
|
||||
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,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> pump(
|
||||
SpaceshipRamp child, {
|
||||
required GameBloc gameBloc,
|
||||
required SpaceshipRampCubit bloc,
|
||||
}) async {
|
||||
await ensureAdd(
|
||||
FlameMultiBlocProvider(
|
||||
providers: [
|
||||
FlameBlocProvider<GameBloc, GameState>.value(
|
||||
value: gameBloc,
|
||||
),
|
||||
FlameBlocProvider<SpaceshipRampCubit, SpaceshipRampState>.value(
|
||||
value: bloc,
|
||||
),
|
||||
],
|
||||
children: [
|
||||
ZCanvasComponent(children: [child]),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MockGameBloc extends Mock implements GameBloc {}
|
||||
|
||||
class _MockSpaceshipRampCubit extends Mock implements SpaceshipRampCubit {}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('RampResetBehavior', () {
|
||||
late GameBloc gameBloc;
|
||||
|
||||
setUp(() {
|
||||
gameBloc = _MockGameBloc();
|
||||
});
|
||||
|
||||
final flameTester = FlameTester(_TestGame.new);
|
||||
|
||||
flameTester.test(
|
||||
'calls onReset when round lost',
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = GameState.initial();
|
||||
final streamController = StreamController<GameState>();
|
||||
whenListen(
|
||||
gameBloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
final behavior = RampResetBehavior();
|
||||
final parent = SpaceshipRamp.test(
|
||||
children: [behavior],
|
||||
);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController.add(state.copyWith(rounds: state.rounds - 1));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verify(bloc.onReset).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
"doesn't call onReset when round stays the same",
|
||||
(game) async {
|
||||
final bloc = _MockSpaceshipRampCubit();
|
||||
final state = GameState.initial();
|
||||
final streamController = StreamController<GameState>();
|
||||
whenListen(
|
||||
gameBloc,
|
||||
streamController.stream,
|
||||
initialState: state,
|
||||
);
|
||||
final behavior = RampResetBehavior();
|
||||
final parent = SpaceshipRamp.test(
|
||||
children: [behavior],
|
||||
);
|
||||
|
||||
await game.pump(
|
||||
parent,
|
||||
gameBloc: gameBloc,
|
||||
bloc: bloc,
|
||||
);
|
||||
|
||||
streamController
|
||||
.add(state.copyWith(roundScore: state.roundScore + 100));
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
verifyNever(bloc.onReset);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
Loading…
Reference in new issue