@ -0,0 +1,11 @@
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_audio/pinball_audio.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
class KickerNoiseBehavior extends ContactBehavior {
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
readProvider<PinballAudioPlayer>().play(PinballAudio.kicker);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
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';
|
||||
|
||||
/// Adds a [GameBonus.googleWord] when all [GoogleLetter]s are activated.
|
||||
class GoogleWordBonusBehavior extends Component {
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
await add(
|
||||
FlameBlocListener<GoogleWordCubit, GoogleWordState>(
|
||||
listenWhen: (_, state) => state.letterSpriteStates.values
|
||||
.every((element) => element == GoogleLetterSpriteState.lit),
|
||||
onNewState: (state) {
|
||||
readBloc<GameBloc, GameState>()
|
||||
.add(const BonusActivated(GameBonus.googleWord));
|
||||
readBloc<GoogleWordCubit, GoogleWordState>().onBonusAwarded();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball/game/behaviors/behaviors.dart';
|
||||
import 'package:pinball/game/components/google_gallery/behaviors/behaviors.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template google_gallery}
|
||||
/// Middle section of the board containing the [GoogleWord] and the
|
||||
/// [GoogleRollover]s.
|
||||
/// {@endtemplate}
|
||||
class GoogleGallery extends Component with ZIndex {
|
||||
/// {@macro google_gallery}
|
||||
GoogleGallery()
|
||||
: super(
|
||||
children: [
|
||||
FlameBlocProvider<GoogleWordCubit, GoogleWordState>(
|
||||
create: GoogleWordCubit.new,
|
||||
children: [
|
||||
GoogleRollover(
|
||||
side: BoardSide.right,
|
||||
children: [
|
||||
ScoringContactBehavior(points: Points.fiveThousand),
|
||||
],
|
||||
),
|
||||
GoogleRollover(
|
||||
side: BoardSide.left,
|
||||
children: [
|
||||
ScoringContactBehavior(points: Points.fiveThousand),
|
||||
],
|
||||
),
|
||||
GoogleWord(position: Vector2(-4.45, 1.8)),
|
||||
GoogleWordBonusBehavior(),
|
||||
],
|
||||
),
|
||||
],
|
||||
) {
|
||||
zIndex = ZIndexes.decal;
|
||||
}
|
||||
|
||||
/// Creates a [GoogleGallery] without any children.
|
||||
///
|
||||
/// This can be used for testing [GoogleGallery]'s behaviors in isolation.
|
||||
@visibleForTesting
|
||||
GoogleGallery.test();
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
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';
|
||||
|
||||
/// Adds a [GameBonus.googleWord] when all [GoogleLetter]s are activated.
|
||||
class GoogleWordBonusBehavior extends Component
|
||||
with ParentIsA<GoogleWord>, FlameBlocReader<GameBloc, GameState> {
|
||||
@override
|
||||
void onMount() {
|
||||
super.onMount();
|
||||
|
||||
final googleLetters = parent.children.whereType<GoogleLetter>();
|
||||
for (final letter in googleLetters) {
|
||||
letter.bloc.stream.listen((_) {
|
||||
final achievedBonus = googleLetters
|
||||
.every((letter) => letter.bloc.state == GoogleLetterState.lit);
|
||||
|
||||
if (achievedBonus) {
|
||||
bloc.add(const BonusActivated(GameBonus.googleWord));
|
||||
for (final letter in googleLetters) {
|
||||
letter.bloc.onReset();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball/game/behaviors/scoring_behavior.dart';
|
||||
import 'package:pinball/game/components/google_word/behaviors/behaviors.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template google_word}
|
||||
/// Loads all [GoogleLetter]s to compose a [GoogleWord].
|
||||
/// {@endtemplate}
|
||||
class GoogleWord extends Component with ZIndex {
|
||||
/// {@macro google_word}
|
||||
GoogleWord({
|
||||
required Vector2 position,
|
||||
}) : super(
|
||||
children: [
|
||||
GoogleLetter(
|
||||
0,
|
||||
children: [ScoringContactBehavior(points: Points.fiveThousand)],
|
||||
)..initialPosition = position + Vector2(-13.1, 1.72),
|
||||
GoogleLetter(
|
||||
1,
|
||||
children: [ScoringContactBehavior(points: Points.fiveThousand)],
|
||||
)..initialPosition = position + Vector2(-8.33, -0.75),
|
||||
GoogleLetter(
|
||||
2,
|
||||
children: [ScoringContactBehavior(points: Points.fiveThousand)],
|
||||
)..initialPosition = position + Vector2(-2.88, -1.85),
|
||||
GoogleLetter(
|
||||
3,
|
||||
children: [ScoringContactBehavior(points: Points.fiveThousand)],
|
||||
)..initialPosition = position + Vector2(2.88, -1.85),
|
||||
GoogleLetter(
|
||||
4,
|
||||
children: [ScoringContactBehavior(points: Points.fiveThousand)],
|
||||
)..initialPosition = position + Vector2(8.33, -0.75),
|
||||
GoogleLetter(
|
||||
5,
|
||||
children: [ScoringContactBehavior(points: Points.fiveThousand)],
|
||||
)..initialPosition = position + Vector2(13.1, 1.72),
|
||||
GoogleWordBonusBehavior(),
|
||||
],
|
||||
) {
|
||||
zIndex = ZIndexes.decal;
|
||||
}
|
||||
|
||||
/// Creates a [GoogleWord] without any children.
|
||||
///
|
||||
/// This can be used for testing [GoogleWord]'s behaviors in isolation.
|
||||
@visibleForTesting
|
||||
GoogleWord.test();
|
||||
}
|
Before Width: | Height: | Size: 637 KiB After Width: | Height: | Size: 544 KiB |
Before Width: | Height: | Size: 390 KiB After Width: | Height: | Size: 334 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 257 KiB |
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 1012 KiB After Width: | Height: | Size: 245 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 180 KiB |
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 471 KiB |
Before Width: | Height: | Size: 481 KiB After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 866 B |
After Width: | Height: | Size: 842 B |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 968 B |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 374 KiB After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 3.4 KiB |
@ -0,0 +1,49 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// enum with the available directions for an [ArrowIcon].
|
||||
enum ArrowIconDirection {
|
||||
/// Left.
|
||||
left,
|
||||
|
||||
/// Right.
|
||||
right,
|
||||
}
|
||||
|
||||
/// {@template arrow_icon}
|
||||
/// A [SpriteComponent] that renders a simple arrow icon.
|
||||
/// {@endtemplate}
|
||||
class ArrowIcon extends SpriteComponent with Tappable, HasGameRef {
|
||||
/// {@macro arrow_icon}
|
||||
ArrowIcon({
|
||||
required Vector2 position,
|
||||
required this.direction,
|
||||
required this.onTap,
|
||||
}) : super(position: position);
|
||||
|
||||
final ArrowIconDirection direction;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
anchor = Anchor.center;
|
||||
final sprite = Sprite(
|
||||
gameRef.images.fromCache(
|
||||
direction == ArrowIconDirection.left
|
||||
? Assets.images.displayArrows.arrowLeft.keyName
|
||||
: Assets.images.displayArrows.arrowRight.keyName,
|
||||
),
|
||||
);
|
||||
|
||||
size = sprite.originalSize / 20;
|
||||
this.sprite = sprite;
|
||||
}
|
||||
|
||||
@override
|
||||
bool onTapUp(TapUpInfo info) {
|
||||
onTap();
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
enum GoogleLetterSpriteState {
|
||||
lit,
|
||||
dimmed,
|
||||
}
|
||||
|
||||
/// {@template google_letter}
|
||||
/// Circular decal that represents a letter in "GOOGLE" for a given index.
|
||||
/// {@endtemplate}
|
||||
class GoogleLetter extends SpriteGroupComponent<GoogleLetterSpriteState>
|
||||
with HasGameRef, FlameBlocListenable<GoogleWordCubit, GoogleWordState> {
|
||||
/// {@macro google_letter}
|
||||
GoogleLetter(int index)
|
||||
: _litAssetPath = _spritePaths[index][GoogleLetterSpriteState.lit]!,
|
||||
_dimmedAssetPath = _spritePaths[index][GoogleLetterSpriteState.dimmed]!,
|
||||
_index = index,
|
||||
super(anchor: Anchor.center);
|
||||
|
||||
final String _litAssetPath;
|
||||
final String _dimmedAssetPath;
|
||||
final int _index;
|
||||
|
||||
@override
|
||||
bool listenWhen(GoogleWordState previousState, GoogleWordState newState) {
|
||||
return previousState.letterSpriteStates[_index] !=
|
||||
newState.letterSpriteStates[_index];
|
||||
}
|
||||
|
||||
@override
|
||||
void onNewState(GoogleWordState state) =>
|
||||
current = state.letterSpriteStates[_index];
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final sprites = {
|
||||
GoogleLetterSpriteState.lit: Sprite(
|
||||
gameRef.images.fromCache(_litAssetPath),
|
||||
),
|
||||
GoogleLetterSpriteState.dimmed: Sprite(
|
||||
gameRef.images.fromCache(_dimmedAssetPath),
|
||||
),
|
||||
};
|
||||
this.sprites = sprites;
|
||||
current = readBloc<GoogleWordCubit, GoogleWordState>()
|
||||
.state
|
||||
.letterSpriteStates[_index];
|
||||
size = sprites[current]!.originalSize / 10;
|
||||
}
|
||||
}
|
||||
|
||||
final _spritePaths = <Map<GoogleLetterSpriteState, String>>[
|
||||
{
|
||||
GoogleLetterSpriteState.lit: Assets.images.googleWord.letter1.lit.keyName,
|
||||
GoogleLetterSpriteState.dimmed:
|
||||
Assets.images.googleWord.letter1.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterSpriteState.lit: Assets.images.googleWord.letter2.lit.keyName,
|
||||
GoogleLetterSpriteState.dimmed:
|
||||
Assets.images.googleWord.letter2.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterSpriteState.lit: Assets.images.googleWord.letter3.lit.keyName,
|
||||
GoogleLetterSpriteState.dimmed:
|
||||
Assets.images.googleWord.letter3.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterSpriteState.lit: Assets.images.googleWord.letter4.lit.keyName,
|
||||
GoogleLetterSpriteState.dimmed:
|
||||
Assets.images.googleWord.letter4.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterSpriteState.lit: Assets.images.googleWord.letter5.lit.keyName,
|
||||
GoogleLetterSpriteState.dimmed:
|
||||
Assets.images.googleWord.letter5.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterSpriteState.lit: Assets.images.googleWord.letter6.lit.keyName,
|
||||
GoogleLetterSpriteState.dimmed:
|
||||
Assets.images.googleWord.letter6.dimmed.keyName,
|
||||
},
|
||||
];
|
@ -1 +0,0 @@
|
||||
export 'google_letter_ball_contact_behavior.dart';
|
@ -1,15 +0,0 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
|
||||
part 'google_letter_state.dart';
|
||||
|
||||
class GoogleLetterCubit extends Cubit<GoogleLetterState> {
|
||||
GoogleLetterCubit() : super(GoogleLetterState.dimmed);
|
||||
|
||||
void onBallContacted() {
|
||||
emit(GoogleLetterState.lit);
|
||||
}
|
||||
|
||||
void onReset() {
|
||||
emit(GoogleLetterState.dimmed);
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
part of 'google_letter_cubit.dart';
|
||||
|
||||
enum GoogleLetterState {
|
||||
lit,
|
||||
dimmed,
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_components/src/components/google_letter/behaviors/behaviors.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
export 'cubit/google_letter_cubit.dart';
|
||||
|
||||
final _spritePaths = <Map<GoogleLetterState, String>>[
|
||||
{
|
||||
GoogleLetterState.lit: Assets.images.googleWord.letter1.lit.keyName,
|
||||
GoogleLetterState.dimmed: Assets.images.googleWord.letter1.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterState.lit: Assets.images.googleWord.letter2.lit.keyName,
|
||||
GoogleLetterState.dimmed: Assets.images.googleWord.letter2.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterState.lit: Assets.images.googleWord.letter3.lit.keyName,
|
||||
GoogleLetterState.dimmed: Assets.images.googleWord.letter3.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterState.lit: Assets.images.googleWord.letter4.lit.keyName,
|
||||
GoogleLetterState.dimmed: Assets.images.googleWord.letter4.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterState.lit: Assets.images.googleWord.letter5.lit.keyName,
|
||||
GoogleLetterState.dimmed: Assets.images.googleWord.letter5.dimmed.keyName,
|
||||
},
|
||||
{
|
||||
GoogleLetterState.lit: Assets.images.googleWord.letter6.lit.keyName,
|
||||
GoogleLetterState.dimmed: Assets.images.googleWord.letter6.dimmed.keyName,
|
||||
},
|
||||
];
|
||||
|
||||
/// {@template google_letter}
|
||||
/// Circular sensor that represents a letter in "GOOGLE" for a given index.
|
||||
/// {@endtemplate}
|
||||
class GoogleLetter extends BodyComponent with InitialPosition {
|
||||
/// {@macro google_letter}
|
||||
GoogleLetter(
|
||||
int index, {
|
||||
Iterable<Component>? children,
|
||||
}) : this._(
|
||||
index,
|
||||
bloc: GoogleLetterCubit(),
|
||||
children: children,
|
||||
);
|
||||
|
||||
GoogleLetter._(
|
||||
int index, {
|
||||
required this.bloc,
|
||||
Iterable<Component>? children,
|
||||
}) : super(
|
||||
children: [
|
||||
_GoogleLetterSpriteGroupComponent(
|
||||
litAssetPath: _spritePaths[index][GoogleLetterState.lit]!,
|
||||
dimmedAssetPath: _spritePaths[index][GoogleLetterState.dimmed]!,
|
||||
current: bloc.state,
|
||||
),
|
||||
GoogleLetterBallContactBehavior(),
|
||||
...?children,
|
||||
],
|
||||
renderBody: false,
|
||||
);
|
||||
|
||||
/// Creates a [GoogleLetter] without any children.
|
||||
///
|
||||
/// This can be used for testing [GoogleLetter]'s behaviors in isolation.
|
||||
@visibleForTesting
|
||||
GoogleLetter.test({
|
||||
required this.bloc,
|
||||
});
|
||||
|
||||
final GoogleLetterCubit bloc;
|
||||
|
||||
@override
|
||||
void onRemove() {
|
||||
bloc.close();
|
||||
super.onRemove();
|
||||
}
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = CircleShape()..radius = 1.85;
|
||||
final fixtureDef = FixtureDef(
|
||||
shape,
|
||||
isSensor: true,
|
||||
);
|
||||
final bodyDef = BodyDef(
|
||||
position: initialPosition,
|
||||
userData: this,
|
||||
);
|
||||
|
||||
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||
}
|
||||
}
|
||||
|
||||
class _GoogleLetterSpriteGroupComponent
|
||||
extends SpriteGroupComponent<GoogleLetterState>
|
||||
with HasGameRef, ParentIsA<GoogleLetter> {
|
||||
_GoogleLetterSpriteGroupComponent({
|
||||
required String litAssetPath,
|
||||
required String dimmedAssetPath,
|
||||
required GoogleLetterState current,
|
||||
}) : _litAssetPath = litAssetPath,
|
||||
_dimmedAssetPath = dimmedAssetPath,
|
||||
super(
|
||||
anchor: Anchor.center,
|
||||
current: current,
|
||||
);
|
||||
|
||||
final String _litAssetPath;
|
||||
final String _dimmedAssetPath;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
parent.bloc.stream.listen((state) => current = state);
|
||||
|
||||
final sprites = {
|
||||
GoogleLetterState.lit: Sprite(
|
||||
gameRef.images.fromCache(_litAssetPath),
|
||||
),
|
||||
GoogleLetterState.dimmed: Sprite(
|
||||
gameRef.images.fromCache(_dimmedAssetPath),
|
||||
),
|
||||
};
|
||||
this.sprites = sprites;
|
||||
size = sprites[current]!.originalSize / 10;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export 'google_rollover_ball_contact_behavior.dart';
|
@ -1,12 +1,15 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
class GoogleLetterBallContactBehavior extends ContactBehavior<GoogleLetter> {
|
||||
class GoogleRolloverBallContactBehavior
|
||||
extends ContactBehavior<GoogleRollover> {
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
if (other is! Ball) return;
|
||||
parent.bloc.onBallContacted();
|
||||
readBloc<GoogleWordCubit, GoogleWordState>().onRolloverContacted();
|
||||
parent.firstChild<SpriteAnimationComponent>()?.playing = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_components/src/components/google_rollover/behaviors/behaviors.dart';
|
||||
|
||||
/// {@template google_rollover}
|
||||
/// Rollover that lights up [GoogleLetter]s.
|
||||
/// {@endtemplate}
|
||||
class GoogleRollover extends BodyComponent {
|
||||
/// {@macro google_rollover}
|
||||
GoogleRollover({
|
||||
required BoardSide side,
|
||||
Iterable<Component>? children,
|
||||
}) : _side = side,
|
||||
super(
|
||||
renderBody: false,
|
||||
children: [
|
||||
GoogleRolloverBallContactBehavior(),
|
||||
_RolloverDecalSpriteComponent(side: side),
|
||||
_PinSpriteAnimationComponent(side: side),
|
||||
...?children,
|
||||
],
|
||||
);
|
||||
|
||||
final BoardSide _side;
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = PolygonShape()
|
||||
..setAsBox(
|
||||
0.1,
|
||||
3.4,
|
||||
Vector2(_side.isLeft ? -14.8 : 5.9, -11),
|
||||
0.19 * _side.direction,
|
||||
);
|
||||
final fixtureDef = FixtureDef(shape, isSensor: true);
|
||||
return world.createBody(BodyDef())..createFixture(fixtureDef);
|
||||
}
|
||||
}
|
||||
|
||||
class _RolloverDecalSpriteComponent extends SpriteComponent with HasGameRef {
|
||||
_RolloverDecalSpriteComponent({required BoardSide side})
|
||||
: _side = side,
|
||||
super(
|
||||
anchor: Anchor.center,
|
||||
position: Vector2(side.isLeft ? -14.8 : 5.9, -11),
|
||||
angle: 0.18 * side.direction,
|
||||
);
|
||||
|
||||
final BoardSide _side;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final sprite = Sprite(
|
||||
gameRef.images.fromCache(
|
||||
(_side.isLeft)
|
||||
? Assets.images.googleRollover.left.decal.keyName
|
||||
: Assets.images.googleRollover.right.decal.keyName,
|
||||
),
|
||||
);
|
||||
this.sprite = sprite;
|
||||
size = sprite.originalSize / 20;
|
||||
}
|
||||
}
|
||||
|
||||
class _PinSpriteAnimationComponent extends SpriteAnimationComponent
|
||||
with HasGameRef {
|
||||
_PinSpriteAnimationComponent({required BoardSide side})
|
||||
: _side = side,
|
||||
super(
|
||||
anchor: Anchor.center,
|
||||
position: Vector2(side.isLeft ? -14.9 : 5.95, -11),
|
||||
angle: 0,
|
||||
playing: false,
|
||||
);
|
||||
|
||||
final BoardSide _side;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final spriteSheet = gameRef.images.fromCache(
|
||||
_side.isLeft
|
||||
? Assets.images.googleRollover.left.pin.keyName
|
||||
: Assets.images.googleRollover.right.pin.keyName,
|
||||
);
|
||||
|
||||
const amountPerRow = 3;
|
||||
const amountPerColumn = 1;
|
||||
final textureSize = Vector2(
|
||||
spriteSheet.width / amountPerRow,
|
||||
spriteSheet.height / amountPerColumn,
|
||||
);
|
||||
size = textureSize / 10;
|
||||
|
||||
animation = SpriteAnimation.fromFrameData(
|
||||
spriteSheet,
|
||||
SpriteAnimationData.sequenced(
|
||||
amount: amountPerRow * amountPerColumn,
|
||||
amountPerRow: amountPerRow,
|
||||
stepTime: 1 / 24,
|
||||
textureSize: textureSize,
|
||||
loop: false,
|
||||
),
|
||||
)..onComplete = () {
|
||||
animation?.reset();
|
||||
playing = false;
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
part 'google_word_state.dart';
|
||||
|
||||
class GoogleWordCubit extends Cubit<GoogleWordState> {
|
||||
GoogleWordCubit() : super(GoogleWordState.initial());
|
||||
|
||||
static const _lettersInGoogle = 6;
|
||||
|
||||
int _lastLitLetter = 0;
|
||||
|
||||
void onRolloverContacted() {
|
||||
final spriteStatesMap = {...state.letterSpriteStates};
|
||||
if (_lastLitLetter < _lettersInGoogle) {
|
||||
spriteStatesMap.update(
|
||||
_lastLitLetter,
|
||||
(_) => GoogleLetterSpriteState.lit,
|
||||
);
|
||||
emit(GoogleWordState(letterSpriteStates: spriteStatesMap));
|
||||
_lastLitLetter++;
|
||||
}
|
||||
}
|
||||
|
||||
void onBonusAwarded() {
|
||||
emit(GoogleWordState.initial());
|
||||
_lastLitLetter = 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
part of 'google_word_cubit.dart';
|
||||
|
||||
class GoogleWordState extends Equatable {
|
||||
const GoogleWordState({required this.letterSpriteStates});
|
||||
|
||||
GoogleWordState.initial()
|
||||
: this(
|
||||
letterSpriteStates: {
|
||||
for (var i = 0; i <= 5; i++) i: GoogleLetterSpriteState.dimmed
|
||||
},
|
||||
);
|
||||
|
||||
final Map<int, GoogleLetterSpriteState> letterSpriteStates;
|
||||
|
||||
@override
|
||||
List<Object> get props => [...letterSpriteStates.values];
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
export 'cubit/google_word_cubit.dart';
|
||||
|
||||
/// {@template google_word}
|
||||
/// Loads all [GoogleLetter]s to compose a [GoogleWord].
|
||||
/// {@endtemplate}
|
||||
class GoogleWord extends PositionComponent {
|
||||
/// {@macro google_word}
|
||||
GoogleWord({
|
||||
required Vector2 position,
|
||||
}) : super(
|
||||
position: position,
|
||||
children: [
|
||||
GoogleLetter(0)..position = Vector2(-13.1, 1.72),
|
||||
GoogleLetter(1)..position = Vector2(-8.33, -0.75),
|
||||
GoogleLetter(2)..position = Vector2(-2.88, -1.85),
|
||||
GoogleLetter(3)..position = Vector2(2.88, -1.85),
|
||||
GoogleLetter(4)..position = Vector2(8.33, -0.75),
|
||||
GoogleLetter(5)..position = Vector2(13.1, 1.72),
|
||||
],
|
||||
);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/common/games.dart';
|
||||
|
||||
class ArrowIconGame extends AssetsGame with HasTappables {
|
||||
ArrowIconGame()
|
||||
: super(
|
||||
imagesFileNames: [
|
||||
Assets.images.displayArrows.arrowLeft.keyName,
|
||||
Assets.images.displayArrows.arrowRight.keyName,
|
||||
],
|
||||
);
|
||||
|
||||
static const description = 'Shows how ArrowIcons are rendered.';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
camera.followVector2(Vector2.zero());
|
||||
|
||||
await add(
|
||||
ArrowIcon(
|
||||
position: Vector2.zero(),
|
||||
direction: ArrowIconDirection.left,
|
||||
onTap: () {},
|
||||
),
|
||||
);
|
||||
|
||||
await add(
|
||||
ArrowIcon(
|
||||
position: Vector2(0, 20),
|
||||
direction: ArrowIconDirection.right,
|
||||
onTap: () {},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/arrow_icon/arrow_icon_game.dart';
|
||||
|
||||
void addArrowIconStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('ArrowIcon').addGame(
|
||||
title: 'Basic',
|
||||
description: ArrowIconGame.description,
|
||||
gameBuilder: (context) => ArrowIconGame(),
|
||||
);
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
// ignore_for_file: cascade_invocations, one_member_abstracts
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
abstract class _VoidCallbackStubBase {
|
||||
void onCall();
|
||||
}
|
||||
|
||||
class _VoidCallbackStub extends Mock implements _VoidCallbackStubBase {}
|
||||
|
||||
void main() {
|
||||
group('ArrowIcon', () {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final assets = [
|
||||
Assets.images.displayArrows.arrowLeft.keyName,
|
||||
Assets.images.displayArrows.arrowRight.keyName,
|
||||
];
|
||||
final flameTester = FlameTester(() => TappablesTestGame(assets));
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'is tappable',
|
||||
setUp: (game, tester) async {
|
||||
final stub = _VoidCallbackStub();
|
||||
await game.images.loadAll(assets);
|
||||
await game.ensureAdd(
|
||||
ArrowIcon(
|
||||
position: Vector2.zero(),
|
||||
direction: ArrowIconDirection.left,
|
||||
onTap: stub.onCall,
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.tapAt(Offset.zero);
|
||||
await tester.pump();
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
final icon = game.descendants().whereType<ArrowIcon>().single;
|
||||
verify(icon.onTap).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
group('left', () {
|
||||
flameTester.testGameWidget(
|
||||
'renders correctly',
|
||||
setUp: (game, tester) async {
|
||||
await game.images.loadAll(assets);
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
await game.add(
|
||||
ArrowIcon(
|
||||
position: Vector2.zero(),
|
||||
direction: ArrowIconDirection.left,
|
||||
onTap: () {},
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/arrow_icon_left.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('right', () {
|
||||
flameTester.testGameWidget(
|
||||
'renders correctly',
|
||||
setUp: (game, tester) async {
|
||||
await game.images.loadAll(assets);
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
await game.add(
|
||||
ArrowIcon(
|
||||
position: Vector2.zero(),
|
||||
direction: ArrowIconDirection.right,
|
||||
onTap: () {},
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
await expectLater(
|
||||
find.byGame<TestGame>(),
|
||||
matchesGoldenFile('golden/arrow_icon_right.png'),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 277 KiB After Width: | Height: | Size: 212 KiB |
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 915 KiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 850 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 174 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |