refactor: moved arrow state to cubit inside ramp instead of propagate from behavior

pull/296/head
RuiAlonso 3 years ago
parent 6dd5c2c73f
commit c9d3db44b1

@ -21,8 +21,6 @@ class RampShotBehavior extends Component
super.onMount(); super.onMount();
parent.bloc.stream.listen((state) { parent.bloc.stream.listen((state) {
parent.progress();
final achievedOneMillionPoints = state.hits % 10 == 0; final achievedOneMillionPoints = state.hits % 10 == 0;
if (!achievedOneMillionPoints) { if (!achievedOneMillionPoints) {

@ -9,9 +9,14 @@ class SpaceshipRampCubit extends Cubit<SpaceshipRampState> {
SpaceshipRampCubit() : super(const SpaceshipRampState.initial()); SpaceshipRampCubit() : super(const SpaceshipRampState.initial());
void onBallInside() { void onBallInside() {
final index =
SpaceshipRampArrowSpriteState.values.indexOf(state.arrowState);
emit( emit(
state.copyWith( state.copyWith(
hits: state.hits + 1, hits: state.hits + 1,
arrowState: SpaceshipRampArrowSpriteState
.values[(index + 1) % SpaceshipRampArrowSpriteState.values.length],
), ),
); );
} }

@ -2,23 +2,51 @@
part of 'spaceship_ramp_cubit.dart'; part of 'spaceship_ramp_cubit.dart';
enum SpaceshipRampArrowSpriteState {
/// Arrow with no dashes 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,
}
class SpaceshipRampState extends Equatable { class SpaceshipRampState extends Equatable {
const SpaceshipRampState({ const SpaceshipRampState({
required this.hits, required this.hits,
required this.arrowState,
}) : assert(hits >= 0, "Hits can't be negative"); }) : assert(hits >= 0, "Hits can't be negative");
const SpaceshipRampState.initial() : this(hits: 0); const SpaceshipRampState.initial()
: this(
hits: 0,
arrowState: SpaceshipRampArrowSpriteState.inactive,
);
final int hits; final int hits;
final SpaceshipRampArrowSpriteState arrowState;
SpaceshipRampState copyWith({ SpaceshipRampState copyWith({
int? hits, int? hits,
SpaceshipRampArrowSpriteState? arrowState,
}) { }) {
return SpaceshipRampState( return SpaceshipRampState(
hits: hits ?? this.hits, hits: hits ?? this.hits,
arrowState: arrowState ?? this.arrowState,
); );
} }
@override @override
List<Object?> get props => [hits]; List<Object?> get props => [hits, arrowState];
} }

@ -17,8 +17,15 @@ class SpaceshipRamp extends Component {
/// {@macro spaceship_ramp} /// {@macro spaceship_ramp}
SpaceshipRamp({ SpaceshipRamp({
Iterable<Component>? children, Iterable<Component>? children,
}) : bloc = SpaceshipRampCubit(), }) : this._(
super( children: children,
bloc: SpaceshipRampCubit(),
);
SpaceshipRamp._({
Iterable<Component>? children,
required this.bloc,
}) : super(
children: [ children: [
// TODO(ruimiguel): refactor RampSensor and RampOpening to be in // TODO(ruimiguel): refactor RampSensor and RampOpening to be in
// only one sensor. // only one sensor.
@ -46,7 +53,9 @@ class SpaceshipRamp extends Component {
_SpaceshipRampForegroundRailing(), _SpaceshipRampForegroundRailing(),
_SpaceshipRampBase()..initialPosition = Vector2(1.7, -20), _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20),
_SpaceshipRampBackgroundRailingSpriteComponent(), _SpaceshipRampBackgroundRailingSpriteComponent(),
_SpaceshipRampArrowSpriteComponent(), _SpaceshipRampArrowSpriteComponent(
current: bloc.state.arrowState,
),
...?children, ...?children,
], ],
); );
@ -69,34 +78,6 @@ class SpaceshipRamp extends Component {
bloc.close(); bloc.close();
super.onRemove(); super.onRemove();
} }
/// Forwards the sprite to the next [SpaceshipRampArrowSpriteState].
///
/// If the current state is the last one it cycles back to the initial state.
void progress() =>
firstChild<_SpaceshipRampArrowSpriteComponent>()?.progress();
}
/// Indicates the state of the arrow on the [SpaceshipRamp].
@visibleForTesting
enum SpaceshipRampArrowSpriteState {
/// Arrow with no dashes 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,
} }
extension on SpaceshipRampArrowSpriteState { extension on SpaceshipRampArrowSpriteState {
@ -116,11 +97,6 @@ extension on SpaceshipRampArrowSpriteState {
return Assets.images.android.ramp.arrow.active5.keyName; return Assets.images.android.ramp.arrow.active5.keyName;
} }
} }
SpaceshipRampArrowSpriteState get next {
return SpaceshipRampArrowSpriteState
.values[(index + 1) % SpaceshipRampArrowSpriteState.values.length];
}
} }
class _SpaceshipRampBackground extends BodyComponent class _SpaceshipRampBackground extends BodyComponent
@ -228,22 +204,23 @@ class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent
/// {@endtemplate} /// {@endtemplate}
class _SpaceshipRampArrowSpriteComponent class _SpaceshipRampArrowSpriteComponent
extends SpriteGroupComponent<SpaceshipRampArrowSpriteState> extends SpriteGroupComponent<SpaceshipRampArrowSpriteState>
with HasGameRef, ZIndex { with HasGameRef, ParentIsA<SpaceshipRamp>, ZIndex {
/// {@macro spaceship_ramp_arrow_sprite_component} /// {@macro spaceship_ramp_arrow_sprite_component}
_SpaceshipRampArrowSpriteComponent() _SpaceshipRampArrowSpriteComponent({
: super( required SpaceshipRampArrowSpriteState current,
}) : super(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(-3.9, -56.5), position: Vector2(-3.9, -56.5),
current: current,
) { ) {
zIndex = ZIndexes.spaceshipRampArrow; zIndex = ZIndexes.spaceshipRampArrow;
} }
/// Changes arrow image to the next [Sprite].
void progress() => current = current?.next;
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
parent.bloc.stream.listen((state) => current = state.arrowState);
final sprites = <SpaceshipRampArrowSpriteState, Sprite>{}; final sprites = <SpaceshipRampArrowSpriteState, Sprite>{};
this.sprites = sprites; this.sprites = sprites;
for (final spriteState in SpaceshipRampArrowSpriteState.values) { for (final spriteState in SpaceshipRampArrowSpriteState.values) {

@ -54,7 +54,7 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents {
) { ) {
if (event is RawKeyDownEvent && if (event is RawKeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.space) { event.logicalKey == LogicalKeyboardKey.space) {
_spaceshipRamp.progress(); //_spaceshipRamp.progress();
return KeyEventResult.handled; return KeyEventResult.handled;
} }

@ -8,18 +8,90 @@ void main() {
group('SpaceshipRampCubit', () { group('SpaceshipRampCubit', () {
group('onBallInside', () { group('onBallInside', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>( blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'emits hits incremented', 'emits hits incremented and arrow goes to the next value',
build: SpaceshipRampCubit.new, build: SpaceshipRampCubit.new,
act: (bloc) => bloc act: (bloc) => bloc
..onBallInside() ..onBallInside()
..onBallInside() ..onBallInside()
..onBallInside(), ..onBallInside(),
expect: () => [ expect: () => [
SpaceshipRampState(hits: 1), SpaceshipRampState(
SpaceshipRampState(hits: 2), hits: 1,
SpaceshipRampState(hits: 3), arrowState: SpaceshipRampArrowSpriteState.active1,
),
SpaceshipRampState(
hits: 2,
arrowState: SpaceshipRampArrowSpriteState.active2,
),
SpaceshipRampState(
hits: 3,
arrowState: SpaceshipRampArrowSpriteState.active3,
),
], ],
); );
group('arrowState', () {
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'goes to the next value while less than max',
build: SpaceshipRampCubit.new,
act: (bloc) => bloc
..onBallInside()
..onBallInside()
..onBallInside()
..onBallInside()
..onBallInside(),
expect: () => [
isA<SpaceshipRampState>()
..having(
(state) => state.arrowState,
'arrowState',
SpaceshipRampArrowSpriteState.active1,
),
isA<SpaceshipRampState>()
..having(
(state) => state.arrowState,
'arrowState',
SpaceshipRampArrowSpriteState.active2,
),
isA<SpaceshipRampState>()
..having(
(state) => state.arrowState,
'arrowState',
SpaceshipRampArrowSpriteState.active3,
),
isA<SpaceshipRampState>()
..having(
(state) => state.arrowState,
'arrowState',
SpaceshipRampArrowSpriteState.active4,
),
isA<SpaceshipRampState>()
..having(
(state) => state.arrowState,
'arrowState',
SpaceshipRampArrowSpriteState.active5,
),
],
);
blocTest<SpaceshipRampCubit, SpaceshipRampState>(
'goes to the initial value when is max',
build: SpaceshipRampCubit.new,
seed: () => SpaceshipRampState(
hits: 5,
arrowState: SpaceshipRampArrowSpriteState.active5,
),
act: (bloc) => bloc..onBallInside(),
expect: () => [
isA<SpaceshipRampState>()
..having(
(state) => state.arrowState,
'arrowState',
SpaceshipRampArrowSpriteState.inactive,
),
],
);
});
}); });
}); });
} }

@ -9,10 +9,12 @@ void main() {
expect( expect(
SpaceshipRampState( SpaceshipRampState(
hits: 0, hits: 0,
arrowState: SpaceshipRampArrowSpriteState.inactive,
), ),
equals( equals(
SpaceshipRampState( SpaceshipRampState(
hits: 0, hits: 0,
arrowState: SpaceshipRampArrowSpriteState.inactive,
), ),
), ),
); );
@ -23,6 +25,7 @@ void main() {
expect( expect(
SpaceshipRampState( SpaceshipRampState(
hits: 0, hits: 0,
arrowState: SpaceshipRampArrowSpriteState.inactive,
), ),
isNotNull, isNotNull,
); );
@ -36,6 +39,7 @@ void main() {
expect( expect(
() => SpaceshipRampState( () => SpaceshipRampState(
hits: -1, hits: -1,
arrowState: SpaceshipRampArrowSpriteState.inactive,
), ),
throwsAssertionError, throwsAssertionError,
); );
@ -49,6 +53,7 @@ void main() {
() { () {
const rampState = SpaceshipRampState( const rampState = SpaceshipRampState(
hits: 0, hits: 0,
arrowState: SpaceshipRampArrowSpriteState.inactive,
); );
expect( expect(
() => rampState.copyWith(hits: rampState.hits - 1), () => rampState.copyWith(hits: rampState.hits - 1),
@ -63,6 +68,7 @@ void main() {
() { () {
const rampState = SpaceshipRampState( const rampState = SpaceshipRampState(
hits: 0, hits: 0,
arrowState: SpaceshipRampArrowSpriteState.inactive,
); );
expect( expect(
rampState.copyWith(), rampState.copyWith(),
@ -77,15 +83,18 @@ void main() {
() { () {
const rampState = SpaceshipRampState( const rampState = SpaceshipRampState(
hits: 0, hits: 0,
arrowState: SpaceshipRampArrowSpriteState.inactive,
); );
final otherRampState = SpaceshipRampState( final otherRampState = SpaceshipRampState(
hits: rampState.hits + 1, hits: rampState.hits + 1,
arrowState: SpaceshipRampArrowSpriteState.active1,
); );
expect(rampState, isNot(equals(otherRampState))); expect(rampState, isNot(equals(otherRampState)));
expect( expect(
rampState.copyWith( rampState.copyWith(
hits: rampState.hits + 1, hits: rampState.hits + 1,
arrowState: SpaceshipRampArrowSpriteState.active1,
), ),
equals(otherRampState), equals(otherRampState),
); );

@ -46,14 +46,14 @@ void main() {
'inactive sprite', 'inactive sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final component = SpaceshipRamp(); final ramp = SpaceshipRamp();
final canvas = ZCanvasComponent(children: [component]); final canvas = ZCanvasComponent(children: [ramp]);
await game.ensureAdd(canvas); await game.ensureAdd(canvas);
await tester.pump(); await tester.pump();
expect( expect(
component.children.whereType<SpriteGroupComponent>().first.current, ramp.children.whereType<SpriteGroupComponent>().first.current,
SpaceshipRampArrowSpriteState.inactive, SpaceshipRampArrowSpriteState.inactive,
); );
@ -71,15 +71,15 @@ void main() {
'active1 sprite', 'active1 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final component = SpaceshipRamp(); final ramp = SpaceshipRamp();
final canvas = ZCanvasComponent(children: [component]); final canvas = ZCanvasComponent(children: [ramp]);
await game.ensureAdd(canvas); await game.ensureAdd(canvas);
component.progress(); ramp.bloc.onBallInside();
await tester.pump(); await tester.pump();
expect( expect(
component.children.whereType<SpriteGroupComponent>().first.current, ramp.children.whereType<SpriteGroupComponent>().first.current,
SpaceshipRampArrowSpriteState.active1, SpaceshipRampArrowSpriteState.active1,
); );
@ -97,17 +97,18 @@ void main() {
'active2 sprite', 'active2 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final component = SpaceshipRamp(); final ramp = SpaceshipRamp();
final canvas = ZCanvasComponent(children: [component]); final canvas = ZCanvasComponent(children: [ramp]);
await game.ensureAdd(canvas); await game.ensureAdd(canvas);
component ramp.bloc
..progress() ..onBallInside()
..progress(); ..onBallInside();
await tester.pump(); await tester.pump();
expect( expect(
component.children.whereType<SpriteGroupComponent>().first.current, ramp.children.whereType<SpriteGroupComponent>().first.current,
SpaceshipRampArrowSpriteState.active2, SpaceshipRampArrowSpriteState.active2,
); );
@ -125,18 +126,19 @@ void main() {
'active3 sprite', 'active3 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final component = SpaceshipRamp(); final ramp = SpaceshipRamp();
final canvas = ZCanvasComponent(children: [component]); final canvas = ZCanvasComponent(children: [ramp]);
await game.ensureAdd(canvas); await game.ensureAdd(canvas);
component ramp.bloc
..progress() ..onBallInside()
..progress() ..onBallInside()
..progress(); ..onBallInside();
await tester.pump(); await tester.pump();
expect( expect(
component.children.whereType<SpriteGroupComponent>().first.current, ramp.children.whereType<SpriteGroupComponent>().first.current,
SpaceshipRampArrowSpriteState.active3, SpaceshipRampArrowSpriteState.active3,
); );
@ -154,19 +156,20 @@ void main() {
'active4 sprite', 'active4 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final component = SpaceshipRamp(); final ramp = SpaceshipRamp();
final canvas = ZCanvasComponent(children: [component]); final canvas = ZCanvasComponent(children: [ramp]);
await game.ensureAdd(canvas); await game.ensureAdd(canvas);
component ramp.bloc
..progress() ..onBallInside()
..progress() ..onBallInside()
..progress() ..onBallInside()
..progress(); ..onBallInside();
await tester.pump(); await tester.pump();
expect( expect(
component.children.whereType<SpriteGroupComponent>().first.current, ramp.children.whereType<SpriteGroupComponent>().first.current,
SpaceshipRampArrowSpriteState.active4, SpaceshipRampArrowSpriteState.active4,
); );
@ -184,20 +187,21 @@ void main() {
'active5 sprite', 'active5 sprite',
setUp: (game, tester) async { setUp: (game, tester) async {
await game.images.loadAll(assets); await game.images.loadAll(assets);
final component = SpaceshipRamp(); final ramp = SpaceshipRamp();
final canvas = ZCanvasComponent(children: [component]); final canvas = ZCanvasComponent(children: [ramp]);
await game.ensureAdd(canvas); await game.ensureAdd(canvas);
component ramp.bloc
..progress() ..onBallInside()
..progress() ..onBallInside()
..progress() ..onBallInside()
..progress() ..onBallInside()
..progress(); ..onBallInside();
await tester.pump(); await tester.pump();
expect( expect(
component.children.whereType<SpriteGroupComponent>().first.current, ramp.children.whereType<SpriteGroupComponent>().first.current,
SpaceshipRampArrowSpriteState.active5, SpaceshipRampArrowSpriteState.active5,
); );

Loading…
Cancel
Save