feat: initial implementation

pull/341/head
Erick Zanardo 3 years ago
parent 2ad0196e44
commit a3e0e35b9c

@ -1,17 +1,23 @@
import 'dart:async'; import 'dart:async';
import 'package:flame/components.dart'; import 'package:flame/components.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:leaderboard_repository/leaderboard_repository.dart';
import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart';
import 'package:pinball/game/components/backbox/displays/displays.dart'; import 'package:pinball/game/components/backbox/displays/displays.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_flame/pinball_flame.dart';
import 'package:pinball_theme/pinball_theme.dart' hide Assets;
/// {@template backbox} /// {@template backbox}
/// The [Backbox] of the pinball machine. /// The [Backbox] of the pinball machine.
/// {@endtemplate} /// {@endtemplate}
class Backbox extends PositionComponent with HasGameRef, ZIndex { class Backbox extends PositionComponent with HasGameRef, ZIndex {
/// {@macro backbox} /// {@macro backbox}
Backbox() Backbox({
: super( LeaderboardRepository? leaderboardRepository,
}) : _leaderboardRepository = leaderboardRepository,
super(
position: Vector2(0, -87), position: Vector2(0, -87),
anchor: Anchor.bottomCenter, anchor: Anchor.bottomCenter,
children: [ children: [
@ -19,20 +25,66 @@ class Backbox extends PositionComponent with HasGameRef, ZIndex {
], ],
) { ) {
zIndex = ZIndexes.backbox; zIndex = ZIndexes.backbox;
add(_display = Component());
}
late final Component _display;
late final LeaderboardRepository? _leaderboardRepository;
late BackboxBloc _bloc;
late StreamSubscription<BackboxState> _subscription;
@override
Future<void> onLoad() async {
final repository = _leaderboardRepository ??
gameRef.buildContext!.read<LeaderboardRepository>();
_bloc = BackboxBloc(leaderboardRepository: repository);
_bloc.stream.listen((state) {
_display.children.removeWhere((_) => true);
_build(state);
});
}
@override
void onRemove() {
super.onRemove();
_subscription.cancel();
}
void _build(BackboxState state) {
if (state is LoadingState) {
_display.add(LoadingDisplay());
} else if (state is InitialsFormState) {
_display.add(
InitialsInputDisplay(
score: state.score,
characterIconPath: state.character.leaderboardIcon.keyName,
onSubmit: (initials) {
_bloc.add(
PlayerInitialsSubmited(
score: state.score,
initials: initials,
character: state.character,
),
);
},
),
);
} else if (state is InitialsSuccessState) {
_display.add(InitialsSubmissionSuccessDisplay());
} else if (state is InitialsFailureState) {
_display.add(InitialsSubmissionFailureDisplay());
}
} }
/// Puts [InitialsInputDisplay] on the [Backbox]. /// Puts [InitialsInputDisplay] on the [Backbox].
Future<void> initialsInput({ void requestInitials({
required int score, required int score,
required String characterIconPath, required CharacterTheme character,
InitialsOnSubmit? onSubmit, }) {
}) async { _bloc.add(
removeAll(children.where((child) => child is! _BackboxSpriteComponent)); PlayerInitialsRequested(
await add(
InitialsInputDisplay(
score: score, score: score,
characterIconPath: characterIconPath, character: character,
onSubmit: onSubmit,
), ),
); );
} }

@ -0,0 +1,56 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:leaderboard_repository/leaderboard_repository.dart';
import 'package:pinball/leaderboard/models/leader_board_entry.dart';
import 'package:pinball_theme/pinball_theme.dart';
part 'backbox_event.dart';
part 'backbox_state.dart';
/// {@template backbox_bloc}
/// Bloc which manages the Backbox display.
/// {@endtemplate}
class BackboxBloc extends Bloc<BackboxEvent, BackboxState> {
/// {@macro backbox_bloc}
BackboxBloc({
required LeaderboardRepository leaderboardRepository,
}) : _leaderboardRepository = leaderboardRepository,
super(LoadingState()) {
on<PlayerInitialsRequested>(_onPlayerInitialsRequested);
on<PlayerInitialsSubmited>(_onPlayerInitialsSubmited);
}
final LeaderboardRepository _leaderboardRepository;
void _onPlayerInitialsRequested(
PlayerInitialsRequested event,
Emitter<BackboxState> emit,
) {
emit(
InitialsFormState(
score: event.score,
character: event.character,
),
);
}
Future<void> _onPlayerInitialsSubmited(
PlayerInitialsSubmited event,
Emitter<BackboxState> emit,
) async {
try {
emit(LoadingState());
await _leaderboardRepository.addLeaderboardEntry(
LeaderboardEntryData(
playerInitials: event.initials,
score: event.score,
character: event.character.toType,
),
);
emit(InitialsSuccessState());
} catch (e, s) {
addError(e, s);
emit(InitialsFailureState());
}
}
}

@ -0,0 +1,50 @@
part of 'backbox_bloc.dart';
/// {@template backbox_event}
/// Base class for backbox events
/// {@endtemplate}
abstract class BackboxEvent extends Equatable {
/// {@macro backbox_event}
const BackboxEvent();
}
/// {@template player_initials_requested}
/// Event that triggers the user initials display
/// {@endtemplate}
class PlayerInitialsRequested extends BackboxEvent {
/// {@template player_initials_requested}
const PlayerInitialsRequested({
required this.score,
required this.character,
});
/// Player's score
final int score;
/// Player's character
final CharacterTheme character;
@override
List<Object?> get props => [props, character];
}
/// {@template player_initials_submited}
/// Event that submits the user score and initials
/// {@endtemplate}
class PlayerInitialsSubmited extends BackboxEvent {
/// {@template player_initials_requested}
const PlayerInitialsSubmited({
required this.score,
required this.initials,
required this.character,
});
/// Player's score
final int score;
/// Player's initials
final String initials;
/// Player's character
final CharacterTheme character;
@override
List<Object?> get props => [props, initials, character];
}

@ -0,0 +1,65 @@
part of 'backbox_bloc.dart';
/// {@template backbox_state}
/// The base state for all [BackboxState]
/// {@endtemplate backbox_state}
abstract class BackboxState extends Equatable {
/// {@macro backbox_state}
const BackboxState();
}
/// Loading state for the backbox
class LoadingState extends BackboxState {
@override
List<Object?> get props => [];
}
/// Failure state for the backbox
class FailureState extends BackboxState {
@override
List<Object?> get props => [];
}
/// State when the leaderboard was successfully loaded
class LeaderboardSuccessState extends BackboxState {
@override
List<Object?> get props => [];
}
/// State when the leaderboard was successfully loaded
class LeaderboardFailureState extends BackboxState {
@override
List<Object?> get props => [];
}
/// {@template initials_form_state}
/// State when the user is inputting their initials
/// {@endtemplate}
class InitialsFormState extends BackboxState {
/// {@macro initials_form_state}
const InitialsFormState({
required this.score,
required this.character,
}) : super();
/// Player's score
final int score;
/// Player's character
final CharacterTheme character;
@override
List<Object?> get props => [score, character];
}
/// State when the leaderboard was successfully loaded
class InitialsSuccessState extends BackboxState {
@override
List<Object?> get props => [];
}
/// State when the leaderboard was successfully loaded
class InitialsFailureState extends BackboxState {
@override
List<Object?> get props => [];
}

@ -1 +1,4 @@
export 'initials_input_display.dart'; export 'initials_input_display.dart';
export 'initials_submission_failure_display.dart';
export 'initials_submission_success_display.dart';
export 'loading_display.dart';

@ -0,0 +1,28 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_ui/pinball_ui.dart';
final _bodyTextPaint = TextPaint(
style: const TextStyle(
fontSize: 3,
color: PinballColors.white,
fontFamily: PinballFonts.pixeloidSans,
),
);
/// {@template initials_submission_failure_display}
/// Display when the initials failed to submitted
/// {@endtemplate}
class InitialsSubmissionFailureDisplay extends TextComponent
with HasGameRef<PinballGame> {
@override
Future<void> onLoad() async {
await super.onLoad();
position = Vector2(0, -10);
anchor = Anchor.center;
text = 'Failure!';
textRenderer = _bodyTextPaint;
}
}

@ -0,0 +1,28 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_ui/pinball_ui.dart';
final _bodyTextPaint = TextPaint(
style: const TextStyle(
fontSize: 3,
color: PinballColors.white,
fontFamily: PinballFonts.pixeloidSans,
),
);
/// {@template initials_submission_success_display}
/// Display when the initials were successfully submitted
/// {@endtemplate}
class InitialsSubmissionSuccessDisplay extends TextComponent
with HasGameRef<PinballGame> {
@override
Future<void> onLoad() async {
await super.onLoad();
position = Vector2(0, -10);
anchor = Anchor.center;
text = 'Success!';
textRenderer = _bodyTextPaint;
}
}

@ -0,0 +1,45 @@
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_ui/pinball_ui.dart';
final _bodyTextPaint = TextPaint(
style: const TextStyle(
fontSize: 3,
color: PinballColors.white,
fontFamily: PinballFonts.pixeloidSans,
),
);
/// {@template loading_display}
///
/// {@endtemplate}
class LoadingDisplay extends TextComponent with HasGameRef<PinballGame> {
late final String _label;
@override
Future<void> onLoad() async {
await super.onLoad();
position = Vector2(0, -10);
anchor = Anchor.center;
text = _label = gameRef.l10n.loading;
textRenderer = _bodyTextPaint;
await add(
TimerComponent(
period: 1,
repeat: true,
onTick: () {
final index = text.indexOf('.');
if (index != -1 && text.substring(index).length == 3) {
text = _label;
} else {
text = '$text.';
}
},
),
);
}
}

@ -22,9 +22,9 @@ class GameBlocStatusListener extends Component
gameRef.overlays.remove(PinballGame.playButtonOverlay); gameRef.overlays.remove(PinballGame.playButtonOverlay);
break; break;
case GameStatus.gameOver: case GameStatus.gameOver:
gameRef.descendants().whereType<Backbox>().first.initialsInput( gameRef.descendants().whereType<Backbox>().first.requestInitials(
score: state.displayScore, score: state.displayScore,
characterIconPath: gameRef.characterTheme.leaderboardIcon.keyName, character: gameRef.characterTheme,
); );
gameRef.firstChild<CameraController>()!.focusOnGameOverBackbox(); gameRef.firstChild<CameraController>()!.focusOnGameOverBackbox();
break; break;

@ -7,6 +7,7 @@ import 'package:flame/input.dart';
import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_bloc/flame_bloc.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/behaviors/behaviors.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball/l10n/l10n.dart'; import 'package:pinball/l10n/l10n.dart';
@ -203,6 +204,18 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector {
await add(PreviewLine()); await add(PreviewLine());
await add(_DebugInformation()); await add(_DebugInformation());
await add(
KeyboardInputController(
keyUp: {
LogicalKeyboardKey.escape: () {
read<GameBloc>().add(const RoundLost());
read<GameBloc>().add(const RoundLost());
read<GameBloc>().add(const RoundLost());
return true;
},
},
),
);
} }
@override @override

Loading…
Cancel
Save