diff --git a/lib/game/components/android_acres/behaviors/ramp_shot_behavior.dart b/lib/game/components/android_acres/behaviors/ramp_shot_behavior.dart index e4f23aa0..956e4ec4 100644 --- a/lib/game/components/android_acres/behaviors/ramp_shot_behavior.dart +++ b/lib/game/components/android_acres/behaviors/ramp_shot_behavior.dart @@ -21,8 +21,6 @@ class RampShotBehavior extends Component super.onMount(); parent.bloc.stream.listen((state) { - parent.progress(); - final achievedOneMillionPoints = state.hits % 10 == 0; if (!achievedOneMillionPoints) { diff --git a/packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit.dart b/packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit.dart index 53b6d710..d40ffb1e 100644 --- a/packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit.dart +++ b/packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit.dart @@ -9,9 +9,14 @@ class SpaceshipRampCubit extends Cubit { SpaceshipRampCubit() : super(const SpaceshipRampState.initial()); void onBallInside() { + final index = + SpaceshipRampArrowSpriteState.values.indexOf(state.arrowState); + emit( state.copyWith( hits: state.hits + 1, + arrowState: SpaceshipRampArrowSpriteState + .values[(index + 1) % SpaceshipRampArrowSpriteState.values.length], ), ); } diff --git a/packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_state.dart b/packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_state.dart index 7fae894f..3006f176 100644 --- a/packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_state.dart +++ b/packages/pinball_components/lib/src/components/spaceship_ramp/cubit/spaceship_ramp_state.dart @@ -2,23 +2,51 @@ 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 { const SpaceshipRampState({ required this.hits, + required this.arrowState, }) : 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 SpaceshipRampArrowSpriteState arrowState; SpaceshipRampState copyWith({ int? hits, + SpaceshipRampArrowSpriteState? arrowState, }) { return SpaceshipRampState( hits: hits ?? this.hits, + arrowState: arrowState ?? this.arrowState, ); } @override - List get props => [hits]; + List get props => [hits, arrowState]; } diff --git a/packages/pinball_components/lib/src/components/spaceship_ramp/spaceship_ramp.dart b/packages/pinball_components/lib/src/components/spaceship_ramp/spaceship_ramp.dart index 99d1b621..6e6f6c2a 100644 --- a/packages/pinball_components/lib/src/components/spaceship_ramp/spaceship_ramp.dart +++ b/packages/pinball_components/lib/src/components/spaceship_ramp/spaceship_ramp.dart @@ -17,8 +17,15 @@ class SpaceshipRamp extends Component { /// {@macro spaceship_ramp} SpaceshipRamp({ Iterable? children, - }) : bloc = SpaceshipRampCubit(), - super( + }) : this._( + children: children, + bloc: SpaceshipRampCubit(), + ); + + SpaceshipRamp._({ + Iterable? children, + required this.bloc, + }) : super( children: [ // TODO(ruimiguel): refactor RampSensor and RampOpening to be in // only one sensor. @@ -46,7 +53,9 @@ class SpaceshipRamp extends Component { _SpaceshipRampForegroundRailing(), _SpaceshipRampBase()..initialPosition = Vector2(1.7, -20), _SpaceshipRampBackgroundRailingSpriteComponent(), - _SpaceshipRampArrowSpriteComponent(), + _SpaceshipRampArrowSpriteComponent( + current: bloc.state.arrowState, + ), ...?children, ], ); @@ -69,34 +78,6 @@ class SpaceshipRamp extends Component { bloc.close(); 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 { @@ -116,11 +97,6 @@ extension on SpaceshipRampArrowSpriteState { return Assets.images.android.ramp.arrow.active5.keyName; } } - - SpaceshipRampArrowSpriteState get next { - return SpaceshipRampArrowSpriteState - .values[(index + 1) % SpaceshipRampArrowSpriteState.values.length]; - } } class _SpaceshipRampBackground extends BodyComponent @@ -228,22 +204,23 @@ class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent /// {@endtemplate} class _SpaceshipRampArrowSpriteComponent extends SpriteGroupComponent - with HasGameRef, ZIndex { + with HasGameRef, ParentIsA, ZIndex { /// {@macro spaceship_ramp_arrow_sprite_component} - _SpaceshipRampArrowSpriteComponent() - : super( + _SpaceshipRampArrowSpriteComponent({ + required SpaceshipRampArrowSpriteState current, + }) : super( anchor: Anchor.center, position: Vector2(-3.9, -56.5), + current: current, ) { zIndex = ZIndexes.spaceshipRampArrow; } - /// Changes arrow image to the next [Sprite]. - void progress() => current = current?.next; - @override Future onLoad() async { await super.onLoad(); + parent.bloc.stream.listen((state) => current = state.arrowState); + final sprites = {}; this.sprites = sprites; for (final spriteState in SpaceshipRampArrowSpriteState.values) { diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart index cabe4d54..468827ef 100644 --- a/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart @@ -54,7 +54,7 @@ class SpaceshipRampGame extends BallGame with KeyboardEvents { ) { if (event is RawKeyDownEvent && event.logicalKey == LogicalKeyboardKey.space) { - _spaceshipRamp.progress(); + //_spaceshipRamp.progress(); return KeyEventResult.handled; } diff --git a/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart index 4b05d428..85030d70 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_cubit_test.dart @@ -8,18 +8,90 @@ void main() { group('SpaceshipRampCubit', () { group('onBallInside', () { blocTest( - 'emits hits incremented', + 'emits hits incremented and arrow goes to the next value', build: SpaceshipRampCubit.new, act: (bloc) => bloc ..onBallInside() ..onBallInside() ..onBallInside(), expect: () => [ - SpaceshipRampState(hits: 1), - SpaceshipRampState(hits: 2), - SpaceshipRampState(hits: 3), + SpaceshipRampState( + hits: 1, + arrowState: SpaceshipRampArrowSpriteState.active1, + ), + SpaceshipRampState( + hits: 2, + arrowState: SpaceshipRampArrowSpriteState.active2, + ), + SpaceshipRampState( + hits: 3, + arrowState: SpaceshipRampArrowSpriteState.active3, + ), ], ); + + group('arrowState', () { + blocTest( + 'goes to the next value while less than max', + build: SpaceshipRampCubit.new, + act: (bloc) => bloc + ..onBallInside() + ..onBallInside() + ..onBallInside() + ..onBallInside() + ..onBallInside(), + expect: () => [ + isA() + ..having( + (state) => state.arrowState, + 'arrowState', + SpaceshipRampArrowSpriteState.active1, + ), + isA() + ..having( + (state) => state.arrowState, + 'arrowState', + SpaceshipRampArrowSpriteState.active2, + ), + isA() + ..having( + (state) => state.arrowState, + 'arrowState', + SpaceshipRampArrowSpriteState.active3, + ), + isA() + ..having( + (state) => state.arrowState, + 'arrowState', + SpaceshipRampArrowSpriteState.active4, + ), + isA() + ..having( + (state) => state.arrowState, + 'arrowState', + SpaceshipRampArrowSpriteState.active5, + ), + ], + ); + + blocTest( + 'goes to the initial value when is max', + build: SpaceshipRampCubit.new, + seed: () => SpaceshipRampState( + hits: 5, + arrowState: SpaceshipRampArrowSpriteState.active5, + ), + act: (bloc) => bloc..onBallInside(), + expect: () => [ + isA() + ..having( + (state) => state.arrowState, + 'arrowState', + SpaceshipRampArrowSpriteState.inactive, + ), + ], + ); + }); }); }); } diff --git a/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart index cbe3f9cd..c168842e 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp/cubit/spaceship_ramp_state_test.dart @@ -9,10 +9,12 @@ void main() { expect( SpaceshipRampState( hits: 0, + arrowState: SpaceshipRampArrowSpriteState.inactive, ), equals( SpaceshipRampState( hits: 0, + arrowState: SpaceshipRampArrowSpriteState.inactive, ), ), ); @@ -23,6 +25,7 @@ void main() { expect( SpaceshipRampState( hits: 0, + arrowState: SpaceshipRampArrowSpriteState.inactive, ), isNotNull, ); @@ -36,6 +39,7 @@ void main() { expect( () => SpaceshipRampState( hits: -1, + arrowState: SpaceshipRampArrowSpriteState.inactive, ), throwsAssertionError, ); @@ -49,6 +53,7 @@ void main() { () { const rampState = SpaceshipRampState( hits: 0, + arrowState: SpaceshipRampArrowSpriteState.inactive, ); expect( () => rampState.copyWith(hits: rampState.hits - 1), @@ -63,6 +68,7 @@ void main() { () { const rampState = SpaceshipRampState( hits: 0, + arrowState: SpaceshipRampArrowSpriteState.inactive, ); expect( rampState.copyWith(), @@ -77,15 +83,18 @@ void main() { () { const rampState = SpaceshipRampState( hits: 0, + arrowState: SpaceshipRampArrowSpriteState.inactive, ); final otherRampState = SpaceshipRampState( hits: rampState.hits + 1, + arrowState: SpaceshipRampArrowSpriteState.active1, ); expect(rampState, isNot(equals(otherRampState))); expect( rampState.copyWith( hits: rampState.hits + 1, + arrowState: SpaceshipRampArrowSpriteState.active1, ), equals(otherRampState), ); diff --git a/packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart b/packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart index 765bc055..fa613677 100644 --- a/packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart +++ b/packages/pinball_components/test/src/components/spaceship_ramp/spaceship_ramp_test.dart @@ -46,14 +46,14 @@ void main() { 'inactive sprite', setUp: (game, tester) async { await game.images.loadAll(assets); - final component = SpaceshipRamp(); - final canvas = ZCanvasComponent(children: [component]); + final ramp = SpaceshipRamp(); + final canvas = ZCanvasComponent(children: [ramp]); await game.ensureAdd(canvas); await tester.pump(); expect( - component.children.whereType().first.current, + ramp.children.whereType().first.current, SpaceshipRampArrowSpriteState.inactive, ); @@ -71,15 +71,15 @@ void main() { 'active1 sprite', setUp: (game, tester) async { await game.images.loadAll(assets); - final component = SpaceshipRamp(); - final canvas = ZCanvasComponent(children: [component]); + final ramp = SpaceshipRamp(); + final canvas = ZCanvasComponent(children: [ramp]); await game.ensureAdd(canvas); - component.progress(); + ramp.bloc.onBallInside(); await tester.pump(); expect( - component.children.whereType().first.current, + ramp.children.whereType().first.current, SpaceshipRampArrowSpriteState.active1, ); @@ -97,17 +97,18 @@ void main() { 'active2 sprite', setUp: (game, tester) async { await game.images.loadAll(assets); - final component = SpaceshipRamp(); - final canvas = ZCanvasComponent(children: [component]); + final ramp = SpaceshipRamp(); + final canvas = ZCanvasComponent(children: [ramp]); await game.ensureAdd(canvas); - component - ..progress() - ..progress(); + ramp.bloc + ..onBallInside() + ..onBallInside(); + await tester.pump(); expect( - component.children.whereType().first.current, + ramp.children.whereType().first.current, SpaceshipRampArrowSpriteState.active2, ); @@ -125,18 +126,19 @@ void main() { 'active3 sprite', setUp: (game, tester) async { await game.images.loadAll(assets); - final component = SpaceshipRamp(); - final canvas = ZCanvasComponent(children: [component]); + final ramp = SpaceshipRamp(); + final canvas = ZCanvasComponent(children: [ramp]); await game.ensureAdd(canvas); - component - ..progress() - ..progress() - ..progress(); + ramp.bloc + ..onBallInside() + ..onBallInside() + ..onBallInside(); + await tester.pump(); expect( - component.children.whereType().first.current, + ramp.children.whereType().first.current, SpaceshipRampArrowSpriteState.active3, ); @@ -154,19 +156,20 @@ void main() { 'active4 sprite', setUp: (game, tester) async { await game.images.loadAll(assets); - final component = SpaceshipRamp(); - final canvas = ZCanvasComponent(children: [component]); + final ramp = SpaceshipRamp(); + final canvas = ZCanvasComponent(children: [ramp]); await game.ensureAdd(canvas); - component - ..progress() - ..progress() - ..progress() - ..progress(); + ramp.bloc + ..onBallInside() + ..onBallInside() + ..onBallInside() + ..onBallInside(); + await tester.pump(); expect( - component.children.whereType().first.current, + ramp.children.whereType().first.current, SpaceshipRampArrowSpriteState.active4, ); @@ -184,20 +187,21 @@ void main() { 'active5 sprite', setUp: (game, tester) async { await game.images.loadAll(assets); - final component = SpaceshipRamp(); - final canvas = ZCanvasComponent(children: [component]); + final ramp = SpaceshipRamp(); + final canvas = ZCanvasComponent(children: [ramp]); await game.ensureAdd(canvas); - component - ..progress() - ..progress() - ..progress() - ..progress() - ..progress(); + ramp.bloc + ..onBallInside() + ..onBallInside() + ..onBallInside() + ..onBallInside() + ..onBallInside(); + await tester.pump(); expect( - component.children.whereType().first.current, + ramp.children.whereType().first.current, SpaceshipRampArrowSpriteState.active5, );