@ -0,0 +1,95 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball/flame/flame.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template alien_zone}
|
||||
/// Area positioned below [Spaceship] where the [Ball]
|
||||
/// can bounce off [AlienBumper]s.
|
||||
///
|
||||
/// When a [Ball] hits [AlienBumper]s, they toggle between activated and
|
||||
/// deactivated states.
|
||||
/// {@endtemplate}
|
||||
class AlienZone extends Component with HasGameRef<PinballGame> {
|
||||
/// {@macro alien_zone}
|
||||
AlienZone();
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
gameRef.addContactCallback(_ControlledAlienBumperBallContactCallback());
|
||||
|
||||
final lowerBumper = ControlledAlienBumper.a()
|
||||
..initialPosition = Vector2(-32.52, 9.34);
|
||||
final upperBumper = ControlledAlienBumper.b()
|
||||
..initialPosition = Vector2(-22.89, 17.43);
|
||||
|
||||
await addAll([
|
||||
lowerBumper,
|
||||
upperBumper,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template controlled_alien_bumper}
|
||||
/// [AlienBumper] with [_AlienBumperController] attached.
|
||||
/// {@endtemplate}
|
||||
@visibleForTesting
|
||||
class ControlledAlienBumper extends AlienBumper
|
||||
with Controls<_AlienBumperController>, ScorePoints {
|
||||
/// {@macro controlled_alien_bumper}
|
||||
ControlledAlienBumper.a() : super.a() {
|
||||
controller = _AlienBumperController(this);
|
||||
}
|
||||
|
||||
/// {@macro controlled_alien_bumper}
|
||||
ControlledAlienBumper.b() : super.b() {
|
||||
controller = _AlienBumperController(this);
|
||||
}
|
||||
|
||||
@override
|
||||
// TODO(ruimiguel): change points when get final points map.
|
||||
int get points => 20;
|
||||
}
|
||||
|
||||
/// {@template alien_bumper_controller}
|
||||
/// Controls a [AlienBumper].
|
||||
/// {@endtemplate}
|
||||
class _AlienBumperController extends ComponentController<AlienBumper>
|
||||
with HasGameRef<PinballGame> {
|
||||
/// {@macro alien_bumper_controller}
|
||||
_AlienBumperController(AlienBumper alienBumper) : super(alienBumper);
|
||||
|
||||
/// Flag for activated state of the [AlienBumper].
|
||||
///
|
||||
/// Used to toggle [AlienBumper]s' state between activated and deactivated.
|
||||
bool isActivated = false;
|
||||
|
||||
/// Registers when a [AlienBumper] is hit by a [Ball].
|
||||
void hit() {
|
||||
if (isActivated) {
|
||||
component.deactivate();
|
||||
} else {
|
||||
component.activate();
|
||||
}
|
||||
isActivated = !isActivated;
|
||||
}
|
||||
}
|
||||
|
||||
/// Listens when a [Ball] bounces bounces against a [AlienBumper].
|
||||
class _ControlledAlienBumperBallContactCallback
|
||||
extends ContactCallback<Controls<_AlienBumperController>, Ball> {
|
||||
@override
|
||||
void begin(
|
||||
Controls<_AlienBumperController> controlledAlienBumper,
|
||||
Ball _,
|
||||
Contact __,
|
||||
) {
|
||||
controlledAlienBumper.controller.hit();
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
export 'alien_zone.dart';
|
||||
export 'board.dart';
|
||||
export 'bonus_word.dart';
|
||||
export 'camera_controller.dart';
|
||||
export 'controlled_ball.dart';
|
||||
export 'controlled_flipper.dart';
|
||||
export 'controlled_plunger.dart';
|
||||
export 'controlled_sparky_computer.dart';
|
||||
export 'flutter_forest.dart';
|
||||
export 'game_flow_controller.dart';
|
||||
export 'plunger.dart';
|
||||
export 'score_effect_controller.dart';
|
||||
export 'score_points.dart';
|
||||
export 'sparky_fire_zone.dart';
|
||||
export 'wall.dart';
|
||||
|
@ -0,0 +1,49 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:pinball/flame/flame.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template controlled_plunger}
|
||||
/// A [Plunger] with a [PlungerController] attached.
|
||||
/// {@endtemplate}
|
||||
class ControlledPlunger extends Plunger with Controls<PlungerController> {
|
||||
/// {@macro controlled_plunger}
|
||||
ControlledPlunger({required double compressionDistance})
|
||||
: super(compressionDistance: compressionDistance) {
|
||||
controller = PlungerController(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template plunger_controller}
|
||||
/// A [ComponentController] that controls a [Plunger]s movement.
|
||||
/// {@endtemplate}
|
||||
class PlungerController extends ComponentController<Plunger>
|
||||
with KeyboardHandler {
|
||||
/// {@macro plunger_controller}
|
||||
PlungerController(Plunger plunger) : super(plunger);
|
||||
|
||||
/// The [LogicalKeyboardKey]s that will control the [Flipper].
|
||||
///
|
||||
/// [onKeyEvent] method listens to when one of these keys is pressed.
|
||||
static const List<LogicalKeyboardKey> _keys = [
|
||||
LogicalKeyboardKey.arrowDown,
|
||||
LogicalKeyboardKey.space,
|
||||
LogicalKeyboardKey.keyS,
|
||||
];
|
||||
|
||||
@override
|
||||
bool onKeyEvent(
|
||||
RawKeyEvent event,
|
||||
Set<LogicalKeyboardKey> keysPressed,
|
||||
) {
|
||||
if (!_keys.contains(event.logicalKey)) return true;
|
||||
|
||||
if (event is RawKeyDownEvent) {
|
||||
component.pull();
|
||||
} else if (event is RawKeyUpEvent) {
|
||||
component.release();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:pinball/flame/flame.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template score_effect_controller}
|
||||
/// A [ComponentController] responsible for adding [ScoreText]s
|
||||
/// on the game screen when the user earns points.
|
||||
/// {@endtemplate}
|
||||
class ScoreEffectController extends ComponentController<PinballGame>
|
||||
with BlocComponent<GameBloc, GameState> {
|
||||
/// {@macro score_effect_controller}
|
||||
ScoreEffectController(PinballGame component) : super(component);
|
||||
|
||||
int _lastScore = 0;
|
||||
final _random = Random();
|
||||
|
||||
double _noise() {
|
||||
return _random.nextDouble() * 5 * (_random.nextBool() ? -1 : 1);
|
||||
}
|
||||
|
||||
@override
|
||||
bool listenWhen(GameState? previousState, GameState newState) {
|
||||
return previousState?.score != newState.score;
|
||||
}
|
||||
|
||||
@override
|
||||
void onNewState(GameState state) {
|
||||
final newScore = state.score - _lastScore;
|
||||
_lastScore = state.score;
|
||||
|
||||
component.add(
|
||||
ScoreText(
|
||||
text: newScore.toString(),
|
||||
position: Vector2(
|
||||
_noise(),
|
||||
_noise() + (-BoardDimensions.bounds.topCenter.dy + 10),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 886 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 313 B |
After Width: | Height: | Size: 343 B |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,16 @@
|
||||
/// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
/// *****************************************************
|
||||
/// FlutterGen
|
||||
/// *****************************************************
|
||||
|
||||
// ignore_for_file: directives_ordering,unnecessary_import
|
||||
|
||||
class FontFamily {
|
||||
FontFamily._();
|
||||
|
||||
/// Font family: PixeloidMono
|
||||
static const String pixeloidMono = 'PixeloidMono';
|
||||
|
||||
/// Font family: PixeloidSans
|
||||
static const String pixeloidSans = 'PixeloidSans';
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import 'package:pinball_components/gen/fonts.gen.dart';
|
||||
|
||||
String _prefixFont(String font) {
|
||||
return 'packages/pinball_components/$font';
|
||||
}
|
||||
|
||||
/// Class with the fonts available on the pinball game
|
||||
class PinballFonts {
|
||||
PinballFonts._();
|
||||
|
||||
/// Mono variation of the Pixeloid font
|
||||
static final String pixeloidMono = _prefixFont(FontFamily.pixeloidMono);
|
||||
|
||||
/// Sans variation of the Pixeloid font
|
||||
static final String pixeloidSans = _prefixFont(FontFamily.pixeloidMono);
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
library pinball_components;
|
||||
|
||||
export 'gen/assets.gen.dart';
|
||||
export 'gen/pinball_fonts.dart';
|
||||
export 'src/pinball_components.dart';
|
||||
|
@ -0,0 +1,109 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template alien_bumper}
|
||||
/// Bumper for Alien area.
|
||||
/// {@endtemplate}
|
||||
// TODO(ruimiguel): refactor later to unify with DashBumpers.
|
||||
class AlienBumper extends BodyComponent with InitialPosition {
|
||||
/// {@macro alien_bumper}
|
||||
AlienBumper._({
|
||||
required double majorRadius,
|
||||
required double minorRadius,
|
||||
required String activeAssetPath,
|
||||
required String inactiveAssetPath,
|
||||
required SpriteComponent spriteComponent,
|
||||
}) : _majorRadius = majorRadius,
|
||||
_minorRadius = minorRadius,
|
||||
_activeAssetPath = activeAssetPath,
|
||||
_inactiveAssetPath = inactiveAssetPath,
|
||||
_spriteComponent = spriteComponent;
|
||||
|
||||
/// {@macro alien_bumper}
|
||||
AlienBumper.a()
|
||||
: this._(
|
||||
majorRadius: 3.52,
|
||||
minorRadius: 2.97,
|
||||
activeAssetPath: Assets.images.alienBumper.a.active.keyName,
|
||||
inactiveAssetPath: Assets.images.alienBumper.a.inactive.keyName,
|
||||
spriteComponent: SpriteComponent(
|
||||
anchor: Anchor.center,
|
||||
position: Vector2(0, -0.1),
|
||||
),
|
||||
);
|
||||
|
||||
/// {@macro alien_bumper}
|
||||
AlienBumper.b()
|
||||
: this._(
|
||||
majorRadius: 3.19,
|
||||
minorRadius: 2.79,
|
||||
activeAssetPath: Assets.images.alienBumper.b.active.keyName,
|
||||
inactiveAssetPath: Assets.images.alienBumper.b.inactive.keyName,
|
||||
spriteComponent: SpriteComponent(
|
||||
anchor: Anchor.center,
|
||||
position: Vector2(0, -0.1),
|
||||
),
|
||||
);
|
||||
|
||||
final double _majorRadius;
|
||||
final double _minorRadius;
|
||||
final String _activeAssetPath;
|
||||
late final Sprite _activeSprite;
|
||||
final String _inactiveAssetPath;
|
||||
late final Sprite _inactiveSprite;
|
||||
final SpriteComponent _spriteComponent;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
renderBody = false;
|
||||
|
||||
await _loadSprites();
|
||||
|
||||
deactivate();
|
||||
await add(_spriteComponent);
|
||||
}
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = EllipseShape(
|
||||
center: Vector2.zero(),
|
||||
majorRadius: _majorRadius,
|
||||
minorRadius: _minorRadius,
|
||||
)..rotate(15.9 * math.pi / 180);
|
||||
final fixtureDef = FixtureDef(shape)
|
||||
..friction = 0
|
||||
..restitution = 4;
|
||||
|
||||
final bodyDef = BodyDef()
|
||||
..position = initialPosition
|
||||
..userData = this;
|
||||
|
||||
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||
}
|
||||
|
||||
Future<void> _loadSprites() async {
|
||||
// TODO(alestiago): I think ideally we would like to do:
|
||||
// Sprite(path).load so we don't require to store the activeAssetPath and
|
||||
// the inactive assetPath.
|
||||
_inactiveSprite = await gameRef.loadSprite(_inactiveAssetPath);
|
||||
_activeSprite = await gameRef.loadSprite(_activeAssetPath);
|
||||
}
|
||||
|
||||
/// Activates the [AlienBumper].
|
||||
void activate() {
|
||||
_spriteComponent
|
||||
..sprite = _activeSprite
|
||||
..size = _activeSprite.originalSize / 10;
|
||||
}
|
||||
|
||||
/// Deactivates the [AlienBumper].
|
||||
void deactivate() {
|
||||
_spriteComponent
|
||||
..sprite = _inactiveSprite
|
||||
..size = _inactiveSprite.originalSize / 10;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template dash_animatronic}
|
||||
/// Animated Dash that sits on top of the [BigDashNestBumper].
|
||||
/// {@endtemplate}
|
||||
class DashAnimatronic extends SpriteAnimationComponent with HasGameRef {
|
||||
/// {@macro dash_animatronic}
|
||||
DashAnimatronic()
|
||||
: super(
|
||||
anchor: Anchor.center,
|
||||
playing: false,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final spriteSheet = await gameRef.images.load(
|
||||
Assets.images.dash.animatronic.keyName,
|
||||
);
|
||||
|
||||
const amountPerRow = 12;
|
||||
const amountPerColumn = 8;
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
if (animation != null) {
|
||||
if (animation!.isLastFrame) {
|
||||
animation!.reset();
|
||||
playing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@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)
|
||||
: _sprite = _GoogleLetterSprite(
|
||||
_GoogleLetterSprite.spritePaths[index],
|
||||
);
|
||||
|
||||
final _GoogleLetterSprite _sprite;
|
||||
|
||||
/// Activates this [GoogleLetter].
|
||||
// TODO(alestiago): Improve doc comment once activate and deactivate
|
||||
// are implemented with the actual assets.
|
||||
Future<void> activate() => _sprite.activate();
|
||||
|
||||
/// Deactivates this [GoogleLetter].
|
||||
Future<void> deactivate() => _sprite.deactivate();
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
await add(_sprite);
|
||||
}
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = CircleShape()..radius = 1.85;
|
||||
final fixtureDef = FixtureDef(shape)..isSensor = true;
|
||||
|
||||
final bodyDef = BodyDef()
|
||||
..position = initialPosition
|
||||
..userData = this
|
||||
..type = BodyType.static;
|
||||
|
||||
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||
}
|
||||
}
|
||||
|
||||
class _GoogleLetterSprite extends SpriteComponent with HasGameRef {
|
||||
_GoogleLetterSprite(String path) : _path = path;
|
||||
|
||||
static final spritePaths = [
|
||||
Assets.images.googleWord.letter1.keyName,
|
||||
Assets.images.googleWord.letter2.keyName,
|
||||
Assets.images.googleWord.letter3.keyName,
|
||||
Assets.images.googleWord.letter4.keyName,
|
||||
Assets.images.googleWord.letter5.keyName,
|
||||
Assets.images.googleWord.letter6.keyName,
|
||||
];
|
||||
|
||||
final String _path;
|
||||
|
||||
// TODO(alestiago): Correctly implement activate and deactivate once the
|
||||
// assets are provided.
|
||||
Future<void> activate() async {
|
||||
await add(
|
||||
_GoogleLetterColorEffect(color: Colors.green),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> deactivate() async {
|
||||
await add(
|
||||
_GoogleLetterColorEffect(color: Colors.red),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final sprite = await gameRef.loadSprite(_path);
|
||||
this.sprite = sprite;
|
||||
// TODO(alestiago): Size correctly once the assets are provided.
|
||||
size = sprite.originalSize / 5;
|
||||
anchor = Anchor.center;
|
||||
}
|
||||
}
|
||||
|
||||
class _GoogleLetterColorEffect extends ColorEffect {
|
||||
_GoogleLetterColorEffect({
|
||||
required Color color,
|
||||
}) : super(
|
||||
color,
|
||||
const Offset(0, 1),
|
||||
EffectController(duration: 0.25),
|
||||
);
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template score_text}
|
||||
/// A [TextComponent] that spawns at a given [position] with a moving animation.
|
||||
/// {@endtemplate}
|
||||
class ScoreText extends TextComponent {
|
||||
/// {@macro score_text}
|
||||
ScoreText({
|
||||
required String text,
|
||||
required Vector2 position,
|
||||
this.color = Colors.black,
|
||||
}) : super(
|
||||
text: text,
|
||||
position: position,
|
||||
anchor: Anchor.center,
|
||||
priority: 100,
|
||||
);
|
||||
|
||||
late final Effect _effect;
|
||||
|
||||
/// The [text]'s [Color].
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
textRenderer = TextPaint(
|
||||
style: TextStyle(
|
||||
fontFamily: PinballFonts.pixeloidMono,
|
||||
color: color,
|
||||
fontSize: 4,
|
||||
),
|
||||
);
|
||||
|
||||
await add(
|
||||
_effect = MoveEffect.by(
|
||||
Vector2(0, -5),
|
||||
EffectController(duration: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
if (_effect.controller.completed) {
|
||||
removeFromParent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/extensions.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/stories/ball/basic_ball_game.dart';
|
||||
|
||||
class AlienBumperAGame extends BasicBallGame {
|
||||
AlienBumperAGame() : super(color: const Color(0xFF0000FF));
|
||||
|
||||
static const info = '''
|
||||
Shows how a AlienBumperA is rendered.
|
||||
|
||||
- Activate the "trace" parameter to overlay the body.
|
||||
''';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final center = screenToWorld(camera.viewport.canvasSize! / 2);
|
||||
final alienBumperA = AlienBumper.a()
|
||||
..initialPosition = Vector2(center.x - 20, center.y - 20)
|
||||
..priority = 1;
|
||||
await add(alienBumperA);
|
||||
|
||||
await traceAllBodies();
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/extensions.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/stories/ball/basic_ball_game.dart';
|
||||
|
||||
class AlienBumperBGame extends BasicBallGame {
|
||||
AlienBumperBGame() : super(color: const Color(0xFF0000FF));
|
||||
|
||||
static const info = '''
|
||||
Shows how a AlienBumperB is rendered.
|
||||
|
||||
- Activate the "trace" parameter to overlay the body.
|
||||
''';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final center = screenToWorld(camera.viewport.canvasSize! / 2);
|
||||
final alienBumperB = AlienBumper.b()
|
||||
..initialPosition = Vector2(center.x - 10, center.y + 10)
|
||||
..priority = 1;
|
||||
await add(alienBumperB);
|
||||
|
||||
await traceAllBodies();
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/alien_zone/alien_bumper_a_game.dart';
|
||||
import 'package:sandbox/stories/alien_zone/alien_bumper_b_game.dart';
|
||||
|
||||
void addAlienZoneStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('Alien Zone')
|
||||
..add(
|
||||
'Alien Bumper A',
|
||||
(context) => GameWidget(
|
||||
game: AlienBumperAGame()..trace = context.boolProperty('Trace', true),
|
||||
),
|
||||
codeLink: buildSourceLink('alien_zone/alien_bumper_a.dart'),
|
||||
info: AlienBumperAGame.info,
|
||||
)
|
||||
..add(
|
||||
'Alien Bumper B',
|
||||
(context) => GameWidget(
|
||||
game: AlienBumperBGame()..trace = context.boolProperty('Trace', true),
|
||||
),
|
||||
codeLink: buildSourceLink('alien_zone/alien_bumper_b.dart'),
|
||||
info: AlienBumperAGame.info,
|
||||
);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/stories/ball/basic_ball_game.dart';
|
||||
|
||||
class GoogleLetterGame extends BasicBallGame {
|
||||
GoogleLetterGame() : super(color: const Color(0xFF009900));
|
||||
|
||||
static const info = '''
|
||||
Shows how a GoogleLetter is rendered.
|
||||
|
||||
- Tap anywhere on the screen to spawn a ball into the game.
|
||||
''';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
addContactCallback(_BallGoogleLetterContactCallback());
|
||||
|
||||
camera.followVector2(Vector2.zero());
|
||||
await add(GoogleLetter(0));
|
||||
|
||||
await traceAllBodies();
|
||||
}
|
||||
}
|
||||
|
||||
class _BallGoogleLetterContactCallback
|
||||
extends ContactCallback<Ball, GoogleLetter> {
|
||||
@override
|
||||
void begin(Ball<Forge2DGame> a, GoogleLetter b, Contact contact) {
|
||||
super.begin(a, b, contact);
|
||||
b.activate();
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/google_word/google_letter_game.dart';
|
||||
|
||||
void addGoogleWordStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('Google Word').add(
|
||||
'Letter',
|
||||
(context) => GameWidget(
|
||||
game: GoogleLetterGame()..trace = context.boolProperty('Trace', true),
|
||||
),
|
||||
codeLink: buildSourceLink('google_word/letter.dart'),
|
||||
info: GoogleLetterGame.info,
|
||||
);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/ball/basic_ball_game.dart';
|
||||
|
||||
class PlungerGame extends BasicBallGame with KeyboardEvents, Traceable {
|
||||
PlungerGame() : super(color: const Color(0xFFFF0000));
|
||||
|
||||
static const info = '''
|
||||
Shows how Plunger is rendered.
|
||||
|
||||
- Activate the "trace" parameter to overlay the body.
|
||||
- Tap anywhere on the screen to spawn a ball into the game.
|
||||
''';
|
||||
|
||||
static const _downKeys = [
|
||||
LogicalKeyboardKey.arrowDown,
|
||||
LogicalKeyboardKey.space,
|
||||
];
|
||||
|
||||
late Plunger plunger;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
final center = screenToWorld(camera.viewport.canvasSize! / 2);
|
||||
|
||||
plunger = Plunger(compressionDistance: 29)
|
||||
..initialPosition = Vector2(center.x - (Kicker.size.x * 2), center.y);
|
||||
await add(plunger);
|
||||
|
||||
await traceAllBodies();
|
||||
}
|
||||
|
||||
@override
|
||||
KeyEventResult onKeyEvent(
|
||||
RawKeyEvent event,
|
||||
Set<LogicalKeyboardKey> keysPressed,
|
||||
) {
|
||||
final movedPlungerDown = _downKeys.contains(event.logicalKey);
|
||||
if (movedPlungerDown) {
|
||||
if (event is RawKeyDownEvent) {
|
||||
plunger.pull();
|
||||
} else if (event is RawKeyUpEvent) {
|
||||
plunger.release();
|
||||
}
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/plunger/plunger_game.dart';
|
||||
|
||||
void addPlungerStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('Plunger').add(
|
||||
'Basic',
|
||||
(context) => GameWidget(
|
||||
game: PlungerGame()..trace = context.boolProperty('Trace', true),
|
||||
),
|
||||
codeLink: buildSourceLink('plunger_game/basic.dart'),
|
||||
info: PlungerGame.info,
|
||||
);
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
|
||||
class ScoreTextBasicGame extends BasicGame with TapDetector {
|
||||
static const info = '''
|
||||
Simple game to show how score text works,
|
||||
|
||||
- Tap anywhere on the screen to spawn an text on the given location.
|
||||
''';
|
||||
|
||||
final random = Random();
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
camera.followVector2(Vector2.zero());
|
||||
}
|
||||
|
||||
@override
|
||||
void onTapUp(TapUpInfo info) {
|
||||
add(
|
||||
ScoreText(
|
||||
text: random.nextInt(100000).toString(),
|
||||
color: Colors.white,
|
||||
position: info.eventPosition.game..multiply(Vector2(1, -1)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import 'package:dashbook/dashbook.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:sandbox/common/common.dart';
|
||||
import 'package:sandbox/stories/score_text/basic.dart';
|
||||
|
||||
void addScoreTextStories(Dashbook dashbook) {
|
||||
dashbook.storiesOf('ScoreText').add(
|
||||
'Basic',
|
||||
(context) => GameWidget(
|
||||
game: ScoreTextBasicGame(),
|
||||
),
|
||||
codeLink: buildSourceLink('score_text/basic.dart'),
|
||||
info: ScoreTextBasicGame.info,
|
||||
);
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(TestGame.new);
|
||||
|
||||
group('AlienBumper', () {
|
||||
flameTester.test('"a" loads correctly', (game) async {
|
||||
final bumper = AlienBumper.a();
|
||||
await game.ensureAdd(bumper);
|
||||
|
||||
expect(game.contains(bumper), isTrue);
|
||||
});
|
||||
|
||||
flameTester.test('"b" loads correctly', (game) async {
|
||||
final bumper = AlienBumper.b();
|
||||
await game.ensureAdd(bumper);
|
||||
expect(game.contains(bumper), isTrue);
|
||||
});
|
||||
|
||||
flameTester.test('activate returns normally', (game) async {
|
||||
final bumper = AlienBumper.a();
|
||||
await game.ensureAdd(bumper);
|
||||
|
||||
expect(bumper.activate, returnsNormally);
|
||||
});
|
||||
|
||||
flameTester.test('deactivate returns normally', (game) async {
|
||||
final bumper = AlienBumper.a();
|
||||
await game.ensureAdd(bumper);
|
||||
|
||||
expect(bumper.deactivate, returnsNormally);
|
||||
});
|
||||
|
||||
flameTester.test('changes sprite', (game) async {
|
||||
final bumper = AlienBumper.a();
|
||||
await game.ensureAdd(bumper);
|
||||
|
||||
final spriteComponent = bumper.firstChild<SpriteComponent>()!;
|
||||
|
||||
final deactivatedSprite = spriteComponent.sprite;
|
||||
bumper.activate();
|
||||
expect(
|
||||
spriteComponent.sprite,
|
||||
isNot(equals(deactivatedSprite)),
|
||||
);
|
||||
|
||||
final activatedSprite = spriteComponent.sprite;
|
||||
bumper.deactivate();
|
||||
expect(
|
||||
spriteComponent.sprite,
|
||||
isNot(equals(activatedSprite)),
|
||||
);
|
||||
|
||||
expect(
|
||||
activatedSprite,
|
||||
isNot(equals(deactivatedSprite)),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(TestGame.new);
|
||||
|
||||
group('DashAnimatronic', () {
|
||||
flameTester.test(
|
||||
'loads correctly',
|
||||
(game) async {
|
||||
final dashAnimatronic = DashAnimatronic();
|
||||
await game.ensureAdd(dashAnimatronic);
|
||||
|
||||
expect(game.contains(dashAnimatronic), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'stops animating after animation completes',
|
||||
(game) async {
|
||||
final dashAnimatronic = DashAnimatronic();
|
||||
await game.ensureAdd(dashAnimatronic);
|
||||
|
||||
dashAnimatronic.playing = true;
|
||||
dashAnimatronic.animation?.setToLast();
|
||||
game.update(1);
|
||||
|
||||
expect(dashAnimatronic.playing, isFalse);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(TestGame.new);
|
||||
|
||||
group('Google Letter', () {
|
||||
flameTester.test(
|
||||
'0th loads correctly',
|
||||
(game) async {
|
||||
final googleLetter = GoogleLetter(0);
|
||||
await game.ready();
|
||||
await game.ensureAdd(googleLetter);
|
||||
|
||||
expect(game.contains(googleLetter), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'1st loads correctly',
|
||||
(game) async {
|
||||
final googleLetter = GoogleLetter(1);
|
||||
await game.ready();
|
||||
await game.ensureAdd(googleLetter);
|
||||
|
||||
expect(game.contains(googleLetter), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'2nd loads correctly',
|
||||
(game) async {
|
||||
final googleLetter = GoogleLetter(2);
|
||||
await game.ready();
|
||||
await game.ensureAdd(googleLetter);
|
||||
|
||||
expect(game.contains(googleLetter), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'3d loads correctly',
|
||||
(game) async {
|
||||
final googleLetter = GoogleLetter(3);
|
||||
await game.ready();
|
||||
await game.ensureAdd(googleLetter);
|
||||
|
||||
expect(game.contains(googleLetter), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'4th loads correctly',
|
||||
(game) async {
|
||||
final googleLetter = GoogleLetter(4);
|
||||
await game.ready();
|
||||
await game.ensureAdd(googleLetter);
|
||||
|
||||
expect(game.contains(googleLetter), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.test(
|
||||
'5th loads correctly',
|
||||
(game) async {
|
||||
final googleLetter = GoogleLetter(5);
|
||||
await game.ready();
|
||||
await game.ensureAdd(googleLetter);
|
||||
|
||||
expect(game.contains(googleLetter), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
test('throws error when index out of range', () {
|
||||
expect(() => GoogleLetter(-1), throwsA(isA<RangeError>()));
|
||||
expect(() => GoogleLetter(6), throwsA(isA<RangeError>()));
|
||||
});
|
||||
|
||||
group('activate', () {
|
||||
flameTester.test('returns normally', (game) async {
|
||||
final googleLetter = GoogleLetter(0);
|
||||
await game.ensureAdd(googleLetter);
|
||||
await expectLater(googleLetter.activate, returnsNormally);
|
||||
});
|
||||
|
||||
flameTester.test('adds an Effect', (game) async {
|
||||
final googleLetter = GoogleLetter(0);
|
||||
await game.ensureAdd(googleLetter);
|
||||
await googleLetter.activate();
|
||||
await game.ready();
|
||||
|
||||
expect(
|
||||
googleLetter.descendants().whereType<Effect>().length,
|
||||
equals(1),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('deactivate', () {
|
||||
flameTester.test('returns normally', (game) async {
|
||||
final googleLetter = GoogleLetter(0);
|
||||
await game.ensureAdd(googleLetter);
|
||||
await expectLater(googleLetter.deactivate, returnsNormally);
|
||||
});
|
||||
|
||||
flameTester.test('adds an Effect', (game) async {
|
||||
final googleLetter = GoogleLetter(0);
|
||||
await game.ensureAdd(googleLetter);
|
||||
await googleLetter.deactivate();
|
||||
await game.ready();
|
||||
|
||||
expect(
|
||||
googleLetter.descendants().whereType<Effect>().length,
|
||||
equals(1),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('ScoreText', () {
|
||||
final flameTester = FlameTester(TestGame.new);
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'renders correctly',
|
||||
setUp: (game, tester) async {
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
await game.ensureAdd(
|
||||
ScoreText(
|
||||
text: '123',
|
||||
position: Vector2.zero(),
|
||||
color: Colors.white,
|
||||
),
|
||||
);
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
final texts = game.descendants().whereType<TextComponent>().length;
|
||||
expect(texts, equals(1));
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'has a movement effect',
|
||||
setUp: (game, tester) async {
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
await game.ensureAdd(
|
||||
ScoreText(
|
||||
text: '123',
|
||||
position: Vector2.zero(),
|
||||
color: Colors.white,
|
||||
),
|
||||
);
|
||||
|
||||
game.update(0.5);
|
||||
await tester.pump();
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
final text = game.descendants().whereType<TextComponent>().first;
|
||||
expect(text.firstChild<MoveEffect>(), isNotNull);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'is removed once finished',
|
||||
setUp: (game, tester) async {
|
||||
game.camera.followVector2(Vector2.zero());
|
||||
await game.ensureAdd(
|
||||
ScoreText(
|
||||
text: '123',
|
||||
position: Vector2.zero(),
|
||||
color: Colors.white,
|
||||
),
|
||||
);
|
||||
|
||||
game.update(1);
|
||||
game.update(0); // Ensure all component removals
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
expect(game.children.length, equals(0));
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(EmptyPinballGameTest.new);
|
||||
|
||||
group('AlienZone', () {
|
||||
flameTester.test(
|
||||
'loads correctly',
|
||||
(game) async {
|
||||
await game.ready();
|
||||
final alienZone = AlienZone();
|
||||
await game.ensureAdd(alienZone);
|
||||
|
||||
expect(game.contains(alienZone), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
group('loads', () {
|
||||
flameTester.test(
|
||||
'two AlienBumper',
|
||||
(game) async {
|
||||
await game.ready();
|
||||
final alienZone = AlienZone();
|
||||
await game.ensureAdd(alienZone);
|
||||
|
||||
expect(
|
||||
alienZone.descendants().whereType<AlienBumper>().length,
|
||||
equals(2),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('bumpers', () {
|
||||
late ControlledAlienBumper controlledAlienBumper;
|
||||
late GameBloc gameBloc;
|
||||
|
||||
setUp(() {
|
||||
gameBloc = MockGameBloc();
|
||||
});
|
||||
|
||||
final flameBlocTester = FlameBlocTester<PinballGame, GameBloc>(
|
||||
gameBuilder: EmptyPinballGameTest.new,
|
||||
blocBuilder: () => gameBloc,
|
||||
);
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'activate when deactivated bumper is hit',
|
||||
setUp: (game, tester) async {
|
||||
controlledAlienBumper = ControlledAlienBumper.a();
|
||||
await game.ensureAdd(controlledAlienBumper);
|
||||
|
||||
controlledAlienBumper.controller.hit();
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
expect(controlledAlienBumper.controller.isActivated, isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
flameTester.testGameWidget(
|
||||
'deactivate when activated bumper is hit',
|
||||
setUp: (game, tester) async {
|
||||
controlledAlienBumper = ControlledAlienBumper.a();
|
||||
await game.ensureAdd(controlledAlienBumper);
|
||||
|
||||
controlledAlienBumper.controller.hit();
|
||||
controlledAlienBumper.controller.hit();
|
||||
},
|
||||
verify: (game, tester) async {
|
||||
expect(controlledAlienBumper.controller.isActivated, isFalse);
|
||||
},
|
||||
);
|
||||
|
||||
flameBlocTester.testGameWidget(
|
||||
'add Scored event',
|
||||
setUp: (game, tester) async {
|
||||
final ball = Ball(baseColor: const Color(0xFF00FFFF));
|
||||
final alienZone = AlienZone();
|
||||
whenListen(
|
||||
gameBloc,
|
||||
const Stream<GameState>.empty(),
|
||||
initialState: const GameState.initial(),
|
||||
);
|
||||
|
||||
await game.ensureAdd(alienZone);
|
||||
await game.ensureAdd(ball);
|
||||
game.addContactCallback(BallScorePointsCallback(game));
|
||||
|
||||
final bumpers = alienZone.descendants().whereType<ScorePoints>();
|
||||
|
||||
for (final bumper in bumpers) {
|
||||
beginContact(game, bumper, ball);
|
||||
verify(
|
||||
() => gameBloc.add(
|
||||
Scored(points: bumper.points),
|
||||
),
|
||||
).called(1);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final flameTester = FlameTester(EmptyPinballGameTest.new);
|
||||
|
||||
group('PlungerController', () {
|
||||
group('onKeyEvent', () {
|
||||
final downKeys = UnmodifiableListView([
|
||||
LogicalKeyboardKey.arrowDown,
|
||||
LogicalKeyboardKey.space,
|
||||
LogicalKeyboardKey.keyS,
|
||||
]);
|
||||
|
||||
late Plunger plunger;
|
||||
late PlungerController controller;
|
||||
|
||||
setUp(() {
|
||||
plunger = Plunger(compressionDistance: 10);
|
||||
controller = PlungerController(plunger);
|
||||
plunger.add(controller);
|
||||
});
|
||||
|
||||
testRawKeyDownEvents(downKeys, (event) {
|
||||
flameTester.test(
|
||||
'moves down '
|
||||
'when ${event.logicalKey.keyLabel} is pressed',
|
||||
(game) async {
|
||||
await game.ensureAdd(plunger);
|
||||
controller.onKeyEvent(event, {});
|
||||
|
||||
expect(plunger.body.linearVelocity.y, isNegative);
|
||||
expect(plunger.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testRawKeyUpEvents(downKeys, (event) {
|
||||
flameTester.test(
|
||||
'moves up '
|
||||
'when ${event.logicalKey.keyLabel} is released '
|
||||
'and plunger is below its starting position',
|
||||
(game) async {
|
||||
await game.ensureAdd(plunger);
|
||||
plunger.body.setTransform(Vector2(0, -1), 0);
|
||||
controller.onKeyEvent(event, {});
|
||||
|
||||
expect(plunger.body.linearVelocity.y, isPositive);
|
||||
expect(plunger.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testRawKeyUpEvents(downKeys, (event) {
|
||||
flameTester.test(
|
||||
'does not move when ${event.logicalKey.keyLabel} is released '
|
||||
'and plunger is in its starting position',
|
||||
(game) async {
|
||||
await game.ensureAdd(plunger);
|
||||
controller.onKeyEvent(event, {});
|
||||
|
||||
expect(plunger.body.linearVelocity.y, isZero);
|
||||
expect(plunger.body.linearVelocity.x, isZero);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('ScoreEffectController', () {
|
||||
late ScoreEffectController controller;
|
||||
late PinballGame game;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(Component());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
game = MockPinballGame();
|
||||
when(() => game.add(any())).thenAnswer((_) async {});
|
||||
|
||||
controller = ScoreEffectController(game);
|
||||
});
|
||||
|
||||
group('listenWhen', () {
|
||||
test('returns true when the user has earned points', () {
|
||||
const previous = GameState.initial();
|
||||
const current = GameState(
|
||||
score: 10,
|
||||
balls: 3,
|
||||
activatedBonusLetters: [],
|
||||
bonusHistory: [],
|
||||
activatedDashNests: {},
|
||||
);
|
||||
expect(controller.listenWhen(previous, current), isTrue);
|
||||
});
|
||||
|
||||
test(
|
||||
'returns true when the user has earned points and there was no '
|
||||
'previous state',
|
||||
() {
|
||||
const current = GameState(
|
||||
score: 10,
|
||||
balls: 3,
|
||||
activatedBonusLetters: [],
|
||||
bonusHistory: [],
|
||||
activatedDashNests: {},
|
||||
);
|
||||
expect(controller.listenWhen(null, current), isTrue);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'returns false when no points were earned',
|
||||
() {
|
||||
const current = GameState.initial();
|
||||
const previous = GameState.initial();
|
||||
expect(controller.listenWhen(previous, current), isFalse);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('onNewState', () {
|
||||
test(
|
||||
'adds a ScoreText with the correct score for the '
|
||||
'first time',
|
||||
() {
|
||||
const state = GameState(
|
||||
score: 10,
|
||||
balls: 3,
|
||||
activatedBonusLetters: [],
|
||||
bonusHistory: [],
|
||||
activatedDashNests: {},
|
||||
);
|
||||
|
||||
controller.onNewState(state);
|
||||
|
||||
final effect =
|
||||
verify(() => game.add(captureAny())).captured.first as ScoreText;
|
||||
|
||||
expect(effect.text, equals('10'));
|
||||
},
|
||||
);
|
||||
|
||||
test('adds a ScoreTextEffect with the correct score', () {
|
||||
controller.onNewState(
|
||||
const GameState(
|
||||
score: 10,
|
||||
balls: 3,
|
||||
activatedBonusLetters: [],
|
||||
bonusHistory: [],
|
||||
activatedDashNests: {},
|
||||
),
|
||||
);
|
||||
|
||||
controller.onNewState(
|
||||
const GameState(
|
||||
score: 14,
|
||||
balls: 3,
|
||||
activatedBonusLetters: [],
|
||||
bonusHistory: [],
|
||||
activatedDashNests: {},
|
||||
),
|
||||
);
|
||||
|
||||
final effect =
|
||||
verify(() => game.add(captureAny())).captured.last as ScoreText;
|
||||
|
||||
expect(effect.text, equals('4'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|