feat: added behaviors for arrow

pull/416/head
RuiAlonso 3 years ago
parent 75feeaae26
commit 221d055a1e

@ -1,3 +1,4 @@
export 'android_spaceship_bonus_behavior.dart';
export 'ramp_bonus_behavior.dart';
export 'ramp_progress_behavior.dart';
export 'ramp_shot_behavior.dart';

@ -0,0 +1,69 @@
import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template ramp_progress_behavior}
/// Increases the score when a [Ball] is shot into the [SpaceshipRamp].
/// {@endtemplate}
class RampProgressBehavior extends Component
with ParentIsA<SpaceshipRamp>, FlameBlocReader<GameBloc, GameState> {
/// {@macro ramp_shot_behavior}
RampProgressBehavior() : super();
/// Creates a [RampProgressBehavior].
///
/// This can be used for testing [RampProgressBehavior] in isolation.
@visibleForTesting
RampProgressBehavior.test({
required this.subscription,
}) : super();
/// Subscription to [SpaceshipRampState] at [SpaceshipRamp].
@visibleForTesting
StreamSubscription? subscription;
@override
void onMount() {
super.onMount();
final spaceshipRamp = parent.children.whereType<SpaceshipRamp>().first;
var previousState = const SpaceshipRampState.initial();
subscription = subscription ??
parent.bloc.stream.listen((state) {
final listenWhen =
previousState.hits != state.hits && state.hits != 0;
if (listenWhen) {
var fullArrowLit = spaceshipRamp.bloc.isFullyProgressed();
var isMaxMultiplier = bloc.state.multiplier == 6;
final canProgress =
!isMaxMultiplier || (isMaxMultiplier && !fullArrowLit);
if (canProgress) {
spaceshipRamp.bloc.onProgressed();
}
fullArrowLit = spaceshipRamp.bloc.isFullyProgressed();
isMaxMultiplier = bloc.state.multiplier == 6;
if (fullArrowLit && !isMaxMultiplier) {
spaceshipRamp.bloc.onAnimate();
}
previousState = state;
}
});
}
@override
void onRemove() {
subscription?.cancel();
super.onRemove();
}
}

@ -1 +1,2 @@
export 'ramp_arrow_blinking_behavior.dart';
export 'ramp_ball_ascending_contact_behavior.dart';

@ -0,0 +1,77 @@
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template ramp_arrow_blinking_behavior}
/// Makes a [SpaceshipRampArrowSpriteComponent] blink between [ArrowLightState.values].
/// {@endtemplate}
class RampArrowBlinkingBehavior extends TimerComponent
with ParentIsA<SpaceshipRamp> {
/// {@macro ramp_arrow_blinking_behavior}
RampArrowBlinkingBehavior() : super(period: 0.05);
final _maxBlinks = 15;
int _blinksCounter = 0;
bool _isAnimating = false;
void _onNewState(SpaceshipRampState state) {
final animationEnabled =
state.animationState == ArrowAnimationState.blinking;
final canBlink = _blinksCounter < _maxBlinks;
if (animationEnabled && canBlink) {
_start();
} else {
_stop();
}
}
void _start() {
if (!_isAnimating) {
_isAnimating = true;
timer
..reset()
..start();
_animate();
}
}
void _animate() {
parent.bloc.onBlink();
_blinksCounter++;
}
void _stop() {
if (_isAnimating) {
_isAnimating = false;
timer.stop();
_blinksCounter = 0;
parent.bloc.onStop();
}
}
@override
Future<void> onLoad() async {
await super.onLoad();
parent.bloc.stream.listen(_onNewState);
}
@override
void onTick() {
super.onTick();
if (!_isAnimating) {
timer.stop();
} else {
if (_blinksCounter < _maxBlinks) {
_animate();
timer
..reset()
..start();
} else {
timer.stop();
}
}
}
}

@ -11,4 +11,79 @@ class SpaceshipRampCubit extends Cubit<SpaceshipRampState> {
state.copyWith(hits: state.hits + 1),
);
}
void onProgressed() {
final index = ArrowLightState.values.indexOf(state.lightState);
emit(
state.copyWith(
lightState:
ArrowLightState.values[(index + 1) % ArrowLightState.values.length],
animationState: ArrowAnimationState.idle,
),
);
}
bool isFullyProgressed() =>
state.lightState == ArrowLightState.active5 &&
state.animationState == ArrowAnimationState.idle;
void onReset() {
emit(
state.copyWith(
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
),
);
}
void onAnimate() {
emit(
state.copyWith(animationState: ArrowAnimationState.blinking),
);
}
void onStop() {
emit(
state.copyWith(
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
),
);
}
void onBlink() {
switch (state.lightState) {
case ArrowLightState.inactive:
emit(
state.copyWith(lightState: ArrowLightState.active1),
);
break;
case ArrowLightState.active1:
emit(
state.copyWith(lightState: ArrowLightState.active2),
);
break;
case ArrowLightState.active2:
emit(
state.copyWith(lightState: ArrowLightState.active3),
);
break;
case ArrowLightState.active3:
emit(
state.copyWith(lightState: ArrowLightState.active4),
);
break;
case ArrowLightState.active4:
emit(
state.copyWith(lightState: ArrowLightState.active5),
);
break;
case ArrowLightState.active5:
emit(
state.copyWith(lightState: ArrowLightState.inactive),
);
break;
}
}
}

@ -1,22 +1,64 @@
// ignore_for_file: comment_references
part of 'spaceship_ramp_cubit.dart';
class SpaceshipRampState extends Equatable {
const SpaceshipRampState({
required this.hits,
required this.lightState,
required this.animationState,
}) : assert(hits >= 0, "Hits can't be negative");
const SpaceshipRampState.initial() : this(hits: 0);
const SpaceshipRampState.initial()
: this(
hits: 0,
lightState: ArrowLightState.inactive,
animationState: ArrowAnimationState.idle,
);
final int hits;
final ArrowLightState lightState;
final ArrowAnimationState animationState;
SpaceshipRampState copyWith({
int? hits,
ArrowLightState? lightState,
ArrowAnimationState? animationState,
}) {
return SpaceshipRampState(
hits: hits ?? this.hits,
lightState: lightState ?? this.lightState,
animationState: animationState ?? this.animationState,
);
}
@override
List<Object?> get props => [hits];
List<Object?> get props => [hits, lightState, animationState];
}
/// Indicates the state of the arrow on the [SpaceshipRamp].
enum ArrowLightState {
/// 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,
}
// Indicates if the blinking animation is running.
enum ArrowAnimationState {
idle,
blinking,
}

@ -40,8 +40,9 @@ class SpaceshipRamp extends Component {
SpaceshipRampBase()..initialPosition = Vector2(3.4, -42.5),
_SpaceshipRampBackgroundRailingSpriteComponent(),
SpaceshipRampArrowSpriteComponent(
current: bloc.state.hits,
current: bloc.state.lightState,
),
RampArrowBlinkingBehavior(),
...?children,
],
);
@ -167,11 +168,12 @@ class _SpaceshipRampBackgroundRampSpriteComponent extends SpriteComponent
/// Lights progressively whenever a [Ball] gets into [SpaceshipRamp].
/// {@endtemplate}
@visibleForTesting
class SpaceshipRampArrowSpriteComponent extends SpriteGroupComponent<int>
class SpaceshipRampArrowSpriteComponent
extends SpriteGroupComponent<ArrowLightState>
with HasGameRef, ParentIsA<SpaceshipRamp>, ZIndex {
/// {@macro spaceship_ramp_arrow_sprite_component}
SpaceshipRampArrowSpriteComponent({
required int current,
required ArrowLightState current,
}) : super(
anchor: Anchor.center,
position: Vector2(-3.9, -56.5),
@ -184,58 +186,38 @@ class SpaceshipRampArrowSpriteComponent extends SpriteGroupComponent<int>
Future<void> onLoad() async {
await super.onLoad();
parent.bloc.stream.listen((state) {
current = state.hits % SpaceshipRampArrowSpriteState.values.length;
print("STATE $state");
current = state.lightState;
});
final sprites = <int, Sprite>{};
final sprites = <ArrowLightState, Sprite>{};
this.sprites = sprites;
for (final spriteState in SpaceshipRampArrowSpriteState.values) {
sprites[spriteState.index] = Sprite(
for (final spriteState in ArrowLightState.values) {
print("SPRITE $spriteState");
sprites[spriteState] = Sprite(
gameRef.images.fromCache(spriteState.path),
);
}
current = 0;
current = ArrowLightState.inactive;
size = sprites[current]!.originalSize / 10;
}
}
/// 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 ArrowLightState {
String get path {
switch (this) {
case SpaceshipRampArrowSpriteState.inactive:
case ArrowLightState.inactive:
return Assets.images.android.ramp.arrow.inactive.keyName;
case SpaceshipRampArrowSpriteState.active1:
case ArrowLightState.active1:
return Assets.images.android.ramp.arrow.active1.keyName;
case SpaceshipRampArrowSpriteState.active2:
case ArrowLightState.active2:
return Assets.images.android.ramp.arrow.active2.keyName;
case SpaceshipRampArrowSpriteState.active3:
case ArrowLightState.active3:
return Assets.images.android.ramp.arrow.active3.keyName;
case SpaceshipRampArrowSpriteState.active4:
case ArrowLightState.active4:
return Assets.images.android.ramp.arrow.active4.keyName;
case SpaceshipRampArrowSpriteState.active5:
case ArrowLightState.active5:
return Assets.images.android.ramp.arrow.active5.keyName;
}
}

Loading…
Cancel
Save