Merge branch 'main' into fix/launch-ramp-render

pull/174/head
Allison Ryan 4 years ago
commit 74f82d3372

@ -13,6 +13,7 @@ class GameBloc extends Bloc<GameEvent, GameState> {
on<Scored>(_onScored); on<Scored>(_onScored);
on<BonusLetterActivated>(_onBonusLetterActivated); on<BonusLetterActivated>(_onBonusLetterActivated);
on<DashNestActivated>(_onDashNestActivated); on<DashNestActivated>(_onDashNestActivated);
on<SparkyTurboChargeActivated>(_onSparkyTurboChargeActivated);
} }
static const bonusWord = 'GOOGLE'; static const bonusWord = 'GOOGLE';
@ -77,4 +78,18 @@ class GameBloc extends Bloc<GameEvent, GameState> {
); );
} }
} }
Future<void> _onSparkyTurboChargeActivated(
SparkyTurboChargeActivated event,
Emitter emit,
) async {
emit(
state.copyWith(
bonusHistory: [
...state.bonusHistory,
GameBonus.sparkyTurboCharge,
],
),
);
}
} }

@ -54,3 +54,10 @@ class DashNestActivated extends GameEvent {
@override @override
List<Object?> get props => [nestId]; List<Object?> get props => [nestId];
} }
class SparkyTurboChargeActivated extends GameEvent {
const SparkyTurboChargeActivated();
@override
List<Object?> get props => [];
}

@ -10,6 +10,9 @@ enum GameBonus {
/// Bonus achieved when the user activates all dash nest bumpers. /// Bonus achieved when the user activates all dash nest bumpers.
dashNest, dashNest,
/// Bonus achieved when a ball enters Sparky's computer.
sparkyTurboCharge,
} }
/// {@template game_state} /// {@template game_state}

@ -3,6 +3,7 @@ export 'bonus_word.dart';
export 'camera_controller.dart'; export 'camera_controller.dart';
export 'controlled_ball.dart'; export 'controlled_ball.dart';
export 'controlled_flipper.dart'; export 'controlled_flipper.dart';
export 'controlled_sparky_computer.dart';
export 'flutter_forest.dart'; export 'flutter_forest.dart';
export 'game_flow_controller.dart'; export 'game_flow_controller.dart';
export 'plunger.dart'; export 'plunger.dart';

@ -54,6 +54,20 @@ class BallController extends ComponentController<Ball>
component.shouldRemove = true; component.shouldRemove = true;
} }
/// Stops the [Ball] inside of the [SparkyComputer] while the turbo charge
/// sequence runs, then boosts the ball out of the computer.
Future<void> turboCharge() async {
gameRef.read<GameBloc>().add(const SparkyTurboChargeActivated());
// TODO(allisonryan0002): adjust delay to match animation duration once
// given animations.
component.stop();
await Future<void>.delayed(const Duration(seconds: 1));
component
..resume()
..boost(Vector2(200, -500));
}
@override @override
void onRemove() { void onRemove() {
super.onRemove(); super.onRemove();

@ -0,0 +1,84 @@
// 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 controlled_sparky_computer}
/// [SparkyComputer] with a [SparkyComputerController] attached.
/// {@endtemplate}
class ControlledSparkyComputer extends SparkyComputer
with Controls<SparkyComputerController>, HasGameRef<PinballGame> {
/// {@macro controlled_sparky_computer}
ControlledSparkyComputer() {
controller = SparkyComputerController(this);
}
@override
void build(Forge2DGame _) {
addContactCallback(SparkyTurboChargeSensorBallContactCallback());
final sparkyTurboChargeSensor = SparkyTurboChargeSensor()
..initialPosition = Vector2(-13, 49.8);
add(sparkyTurboChargeSensor);
super.build(_);
}
}
/// {@template sparky_computer_controller}
/// Controller attached to a [SparkyComputer] that handles its game related
/// logic.
/// {@endtemplate}
// TODO(allisonryan0002): listen for turbo charge game bonus and animate Sparky.
class SparkyComputerController
extends ComponentController<ControlledSparkyComputer> {
/// {@macro sparky_computer_controller}
SparkyComputerController(ControlledSparkyComputer controlledComputer)
: super(controlledComputer);
}
/// {@template sparky_turbo_charge_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer] with the [SparkyTurboChargeSensorBallContactCallback].
/// {@endtemplate}
@visibleForTesting
class SparkyTurboChargeSensor extends BodyComponent with InitialPosition {
/// {@macro sparky_turbo_charge_sensor}
SparkyTurboChargeSensor() {
renderBody = false;
}
@override
Body createBody() {
final shape = CircleShape()..radius = 0.1;
final fixtureDef = FixtureDef(shape)..isSensor = true;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
/// {@template sparky_turbo_charge_sensor_ball_contact_callback}
/// Turbo charges the [Ball] on contact with [SparkyTurboChargeSensor].
/// {@endtemplate}
@visibleForTesting
class SparkyTurboChargeSensorBallContactCallback
extends ContactCallback<SparkyTurboChargeSensor, ControlledBall> {
/// {@macro sparky_turbo_charge_sensor_ball_contact_callback}
SparkyTurboChargeSensorBallContactCallback();
@override
void begin(
SparkyTurboChargeSensor sparkyTurboChargeSensor,
ControlledBall ball,
_,
) {
ball.controller.turboCharge();
}
}

@ -1,21 +1,67 @@
// ignore_for_file: avoid_renaming_method_parameters // ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart'; 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/flame/flame.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/pinball_components.dart';
// TODO(ruimiguel): create and add SparkyFireZone component here in other PR. /// {@template sparky_fire_zone}
/// Area positioned at the top left of the [Board] where the [Ball]
/// can bounce off [SparkyBumper]s.
///
/// When a [Ball] hits [SparkyBumper]s, they toggle between activated and
/// deactivated states.
/// {@endtemplate}
class SparkyFireZone extends Component with HasGameRef<PinballGame> {
/// {@macro sparky_fire_zone}
SparkyFireZone();
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(_ControlledSparkyBumperBallContactCallback());
// TODO(ruimiguel): make private and remove ignore once SparkyFireZone is done final lowerLeftBumper = ControlledSparkyBumper.a()
// ignore: public_member_api_docs ..initialPosition = Vector2(-23.15, 41.65);
final upperLeftBumper = ControlledSparkyBumper.b()
..initialPosition = Vector2(-21.25, 58.15);
final rightBumper = ControlledSparkyBumper.c()
..initialPosition = Vector2(-3.56, 53.051);
await addAll([
lowerLeftBumper,
upperLeftBumper,
rightBumper,
]);
}
}
/// {@template controlled_sparky_bumper}
/// [SparkyBumper] with [_SparkyBumperController] attached.
/// {@endtemplate}
@visibleForTesting
class ControlledSparkyBumper extends SparkyBumper class ControlledSparkyBumper extends SparkyBumper
with Controls<_SparkyBumperController> { with Controls<_SparkyBumperController>, ScorePoints {
// TODO(ruimiguel): make private and remove ignore once SparkyFireZone is done ///{@macro controlled_sparky_bumper}
// ignore: public_member_api_docs ControlledSparkyBumper.a() : super.a() {
ControlledSparkyBumper() : super.a() { controller = _SparkyBumperController(this);
}
///{@macro controlled_sparky_bumper}
ControlledSparkyBumper.b() : super.b() {
controller = _SparkyBumperController(this);
}
///{@macro controlled_sparky_bumper}
ControlledSparkyBumper.c() : super.c() {
controller = _SparkyBumperController(this); controller = _SparkyBumperController(this);
} }
@override
int get points => 20;
} }
/// {@template sparky_bumper_controller} /// {@template sparky_bumper_controller}
@ -42,3 +88,16 @@ class _SparkyBumperController extends ComponentController<SparkyBumper>
isActivated = !isActivated; isActivated = !isActivated;
} }
} }
/// Listens when a [Ball] bounces bounces against a [SparkyBumper].
class _ControlledSparkyBumperBallContactCallback
extends ContactCallback<Controls<_SparkyBumperController>, Ball> {
@override
void begin(
Controls<_SparkyBumperController> controlledSparkyBumper,
Ball _,
Contact __,
) {
controlledSparkyBumper.controller.hit();
}
}

@ -46,6 +46,14 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.spaceship.rail.foreground.keyName), images.load(components.Assets.images.spaceship.rail.foreground.keyName),
images.load(components.Assets.images.chromeDino.mouth.keyName), images.load(components.Assets.images.chromeDino.mouth.keyName),
images.load(components.Assets.images.chromeDino.head.keyName), images.load(components.Assets.images.chromeDino.head.keyName),
images.load(components.Assets.images.sparky.computer.base.keyName),
images.load(components.Assets.images.sparky.computer.top.keyName),
images.load(components.Assets.images.sparky.bumper.a.active.keyName),
images.load(components.Assets.images.sparky.bumper.a.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.b.active.keyName),
images.load(components.Assets.images.sparky.bumper.b.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.c.active.keyName),
images.load(components.Assets.images.sparky.bumper.c.inactive.keyName),
images.load(components.Assets.images.backboard.backboardScores.keyName), images.load(components.Assets.images.backboard.backboardScores.keyName),
images.load(components.Assets.images.backboard.backboardGameOver.keyName), images.load(components.Assets.images.backboard.backboardGameOver.keyName),
images.load(Assets.images.components.background.path), images.load(Assets.images.components.background.path),

@ -45,12 +45,14 @@ class PinballGame extends Forge2DGame
await _addGameBoundaries(); await _addGameBoundaries();
unawaited(addFromBlueprint(Boundaries())); unawaited(addFromBlueprint(Boundaries()));
unawaited(addFromBlueprint(LaunchRamp())); unawaited(addFromBlueprint(LaunchRamp()));
unawaited(addFromBlueprint(ControlledSparkyComputer()));
final plunger = Plunger(compressionDistance: 29) final plunger = Plunger(compressionDistance: 29)
..initialPosition = Vector2(38, -19); ..initialPosition = Vector2(38, -19);
await add(plunger); await add(plunger);
unawaited(add(Board())); unawaited(add(Board()));
unawaited(add(SparkyFireZone()));
unawaited(addFromBlueprint(Slingshots())); unawaited(addFromBlueprint(Slingshots()));
unawaited(addFromBlueprint(DinoWalls())); unawaited(addFromBlueprint(DinoWalls()));
unawaited(_addBonusWord()); unawaited(_addBonusWord());

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

@ -25,8 +25,7 @@ class $AssetsImagesGen {
const $AssetsImagesLaunchRampGen(); const $AssetsImagesLaunchRampGen();
$AssetsImagesSlingshotGen get slingshot => const $AssetsImagesSlingshotGen(); $AssetsImagesSlingshotGen get slingshot => const $AssetsImagesSlingshotGen();
$AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen(); $AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen();
$AssetsImagesSparkyBumperGen get sparkyBumper => $AssetsImagesSparkyGen get sparky => const $AssetsImagesSparkyGen();
const $AssetsImagesSparkyBumperGen();
} }
class $AssetsImagesBackboardGen { class $AssetsImagesBackboardGen {
@ -136,12 +135,13 @@ class $AssetsImagesSpaceshipGen {
const AssetGenImage('assets/images/spaceship/saucer.png'); const AssetGenImage('assets/images/spaceship/saucer.png');
} }
class $AssetsImagesSparkyBumperGen { class $AssetsImagesSparkyGen {
const $AssetsImagesSparkyBumperGen(); const $AssetsImagesSparkyGen();
$AssetsImagesSparkyBumperAGen get a => const $AssetsImagesSparkyBumperAGen(); $AssetsImagesSparkyBumperGen get bumper =>
$AssetsImagesSparkyBumperBGen get b => const $AssetsImagesSparkyBumperBGen(); const $AssetsImagesSparkyBumperGen();
$AssetsImagesSparkyBumperCGen get c => const $AssetsImagesSparkyBumperCGen(); $AssetsImagesSparkyComputerGen get computer =>
const $AssetsImagesSparkyComputerGen();
} }
class $AssetsImagesDashBumperAGen { class $AssetsImagesDashBumperAGen {
@ -191,31 +191,60 @@ class $AssetsImagesSpaceshipRampGen {
'assets/images/spaceship/ramp/railing-foreground.png'); 'assets/images/spaceship/ramp/railing-foreground.png');
} }
class $AssetsImagesSparkyBumperGen {
const $AssetsImagesSparkyBumperGen();
$AssetsImagesSparkyBumperAGen get a => const $AssetsImagesSparkyBumperAGen();
$AssetsImagesSparkyBumperBGen get b => const $AssetsImagesSparkyBumperBGen();
$AssetsImagesSparkyBumperCGen get c => const $AssetsImagesSparkyBumperCGen();
}
class $AssetsImagesSparkyComputerGen {
const $AssetsImagesSparkyComputerGen();
/// File path: assets/images/sparky/computer/base.png
AssetGenImage get base =>
const AssetGenImage('assets/images/sparky/computer/base.png');
/// File path: assets/images/sparky/computer/top.png
AssetGenImage get top =>
const AssetGenImage('assets/images/sparky/computer/top.png');
}
class $AssetsImagesSparkyBumperAGen { class $AssetsImagesSparkyBumperAGen {
const $AssetsImagesSparkyBumperAGen(); const $AssetsImagesSparkyBumperAGen();
/// File path: assets/images/sparky/bumper/a/active.png
AssetGenImage get active => AssetGenImage get active =>
const AssetGenImage('assets/images/sparky_bumper/a/active.png'); const AssetGenImage('assets/images/sparky/bumper/a/active.png');
/// File path: assets/images/sparky/bumper/a/inactive.png
AssetGenImage get inactive => AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky_bumper/a/inactive.png'); const AssetGenImage('assets/images/sparky/bumper/a/inactive.png');
} }
class $AssetsImagesSparkyBumperBGen { class $AssetsImagesSparkyBumperBGen {
const $AssetsImagesSparkyBumperBGen(); const $AssetsImagesSparkyBumperBGen();
/// File path: assets/images/sparky/bumper/b/active.png
AssetGenImage get active => AssetGenImage get active =>
const AssetGenImage('assets/images/sparky_bumper/b/active.png'); const AssetGenImage('assets/images/sparky/bumper/b/active.png');
/// File path: assets/images/sparky/bumper/b/inactive.png
AssetGenImage get inactive => AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky_bumper/b/inactive.png'); const AssetGenImage('assets/images/sparky/bumper/b/inactive.png');
} }
class $AssetsImagesSparkyBumperCGen { class $AssetsImagesSparkyBumperCGen {
const $AssetsImagesSparkyBumperCGen(); const $AssetsImagesSparkyBumperCGen();
/// File path: assets/images/sparky/bumper/c/active.png
AssetGenImage get active => AssetGenImage get active =>
const AssetGenImage('assets/images/sparky_bumper/c/active.png'); const AssetGenImage('assets/images/sparky/bumper/c/active.png');
/// File path: assets/images/sparky/bumper/c/inactive.png
AssetGenImage get inactive => AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky_bumper/c/inactive.png'); const AssetGenImage('assets/images/sparky/bumper/c/inactive.png');
} }
class Assets { class Assets {

@ -41,6 +41,8 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
await add( await add(
_spriteComponent..tint(baseColor.withOpacity(0.5)), _spriteComponent..tint(baseColor.withOpacity(0.5)),
); );
renderBody = false;
} }
@override @override
@ -59,15 +61,19 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
/// ///
/// The [Ball] will no longer be affected by any forces, including it's /// The [Ball] will no longer be affected by any forces, including it's
/// weight and those emitted from collisions. /// weight and those emitted from collisions.
// TODO(allisonryan0002): prevent motion from contact with other balls.
void stop() { void stop() {
body.setType(BodyType.static); body
..gravityScale = 0
..linearVelocity = Vector2.zero()
..angularVelocity = 0;
} }
/// Allows the [Ball] to be affected by forces. /// Allows the [Ball] to be affected by forces.
/// ///
/// If previously [stop]ped, the previous ball's velocity is not kept. /// If previously [stop]ped, the previous ball's velocity is not kept.
void resume() { void resume() {
body.setType(BodyType.dynamic); body.gravityScale = 1;
} }
@override @override
@ -91,7 +97,7 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
/// Applies a boost on this [Ball]. /// Applies a boost on this [Ball].
void boost(Vector2 impulse) { void boost(Vector2 impulse) {
body.applyLinearImpulse(impulse); body.linearVelocity = impulse;
_boostTimer = _boostDuration; _boostTimer = _boostDuration;
} }

@ -23,3 +23,4 @@ export 'spaceship.dart';
export 'spaceship_rail.dart'; export 'spaceship_rail.dart';
export 'spaceship_ramp.dart'; export 'spaceship_ramp.dart';
export 'sparky_bumper.dart'; export 'sparky_bumper.dart';
export 'sparky_computer.dart';

@ -37,7 +37,7 @@ class Spaceship extends Forge2DBlueprint {
AndroidHead()..initialPosition = position, AndroidHead()..initialPosition = position,
SpaceshipHole( SpaceshipHole(
outsideLayer: Layer.spaceshipExitRail, outsideLayer: Layer.spaceshipExitRail,
outsidePriority: SpaceshipRail.ballPriorityWhenOnSpaceshipRail, outsidePriority: SpaceshipRail.ballPriorityInsideRail,
)..initialPosition = position - Vector2(5.2, 4.8), )..initialPosition = position - Vector2(5.2, 4.8),
SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8), SpaceshipHole()..initialPosition = position - Vector2(-7.2, 0.8),
SpaceshipWall()..initialPosition = position, SpaceshipWall()..initialPosition = position,

@ -14,8 +14,8 @@ class SpaceshipRail extends Forge2DBlueprint {
/// {@macro spaceship_rail} /// {@macro spaceship_rail}
SpaceshipRail(); SpaceshipRail();
/// Base priority for ball while be in [_SpaceshipRailRamp]. /// Base priority for [Ball] while inside [SpaceshipRail].
static const ballPriorityWhenOnSpaceshipRail = 2; static const ballPriorityInsideRail = 2;
@override @override
void build(_) { void build(_) {
@ -45,9 +45,8 @@ class SpaceshipRail extends Forge2DBlueprint {
class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered { class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
_SpaceshipRailRamp() _SpaceshipRailRamp()
: super( : super(
priority: SpaceshipRail.ballPriorityWhenOnSpaceshipRail - 1, priority: SpaceshipRail.ballPriorityInsideRail - 1,
) { ) {
renderBody = false;
layer = Layer.spaceshipExitRail; layer = Layer.spaceshipExitRail;
} }
@ -139,6 +138,8 @@ class _SpaceshipRailRamp extends BodyComponent with InitialPosition, Layered {
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
renderBody = false;
await add(_SpaceshipRailRampSpriteComponent()); await add(_SpaceshipRailRampSpriteComponent());
} }
} }
@ -161,11 +162,7 @@ class _SpaceshipRailRampSpriteComponent extends SpriteComponent
class _SpaceshipRailForeground extends SpriteComponent with HasGameRef { class _SpaceshipRailForeground extends SpriteComponent with HasGameRef {
_SpaceshipRailForeground() _SpaceshipRailForeground()
: super( : super(priority: SpaceshipRail.ballPriorityInsideRail + 1);
anchor: Anchor.center,
position: Vector2(-28.5, 19.7),
priority: SpaceshipRail.ballPriorityWhenOnSpaceshipRail + 1,
);
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -176,6 +173,8 @@ class _SpaceshipRailForeground extends SpriteComponent with HasGameRef {
); );
this.sprite = sprite; this.sprite = sprite;
size = sprite.originalSize / 10; size = sprite.originalSize / 10;
anchor = Anchor.center;
position = Vector2(-28.5, 19.7);
} }
} }
@ -183,7 +182,7 @@ class _SpaceshipRailForeground extends SpriteComponent with HasGameRef {
class _SpaceshipRailBase extends BodyComponent with InitialPosition, Layered { class _SpaceshipRailBase extends BodyComponent with InitialPosition, Layered {
_SpaceshipRailBase({required this.radius}) _SpaceshipRailBase({required this.radius})
: super( : super(
priority: SpaceshipRail.ballPriorityWhenOnSpaceshipRail + 1, priority: SpaceshipRail.ballPriorityInsideRail + 1,
) { ) {
renderBody = false; renderBody = false;
layer = Layer.board; layer = Layer.board;
@ -213,9 +212,9 @@ class SpaceshipRailExit extends RampOpening {
/// {@macro spaceship_rail_exit} /// {@macro spaceship_rail_exit}
SpaceshipRailExit() SpaceshipRailExit()
: super( : super(
insideLayer: Layer.spaceshipExitRail,
orientation: RampOrientation.down, orientation: RampOrientation.down,
insidePriority: 3, insideLayer: Layer.spaceshipExitRail,
insidePriority: SpaceshipRail.ballPriorityInsideRail,
) { ) {
renderBody = false; renderBody = false;
layer = Layer.spaceshipExitRail; layer = Layer.spaceshipExitRail;
@ -224,10 +223,10 @@ class SpaceshipRailExit extends RampOpening {
@override @override
Shape get shape { Shape get shape {
return ArcShape( return ArcShape(
center: Vector2(-28, -19), center: Vector2(-29, -19),
arcRadius: 2.5, arcRadius: 2.5,
angle: math.pi * 0.4, angle: math.pi * 0.4,
rotation: -0.16, rotation: 0.26,
); );
} }
} }

@ -184,8 +184,6 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
@override @override
Body createBody() { Body createBody() {
renderBody = false;
final bodyDef = BodyDef() final bodyDef = BodyDef()
..userData = this ..userData = this
..position = initialPosition; ..position = initialPosition;
@ -199,11 +197,13 @@ class _SpaceshipRampForegroundRailing extends BodyComponent
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
await super.onLoad(); await super.onLoad();
await add(_SpaceshipRampForegroundRalingSpriteComponent()); renderBody = false;
await add(_SpaceshipRampForegroundRailingSpriteComponent());
} }
} }
class _SpaceshipRampForegroundRalingSpriteComponent extends SpriteComponent class _SpaceshipRampForegroundRailingSpriteComponent extends SpriteComponent
with HasGameRef { with HasGameRef {
@override @override
Future<void> onLoad() async { Future<void> onLoad() async {
@ -221,13 +221,12 @@ class _SpaceshipRampForegroundRalingSpriteComponent extends SpriteComponent
/// Represents the ground right base of the [SpaceshipRamp]. /// Represents the ground right base of the [SpaceshipRamp].
class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered { class _SpaceshipRampBase extends BodyComponent with InitialPosition, Layered {
_SpaceshipRampBase() { _SpaceshipRampBase() {
renderBody = false;
layer = Layer.board; layer = Layer.board;
} }
@override @override
Body createBody() { Body createBody() {
renderBody = false;
const baseWidth = 6; const baseWidth = 6;
final baseShape = BezierCurveShape( final baseShape = BezierCurveShape(
controlPoints: [ controlPoints: [
@ -266,7 +265,9 @@ class _SpaceshipRampOpening extends RampOpening {
orientation: RampOrientation.down, orientation: RampOrientation.down,
insidePriority: SpaceshipRamp.ballPriorityInsideRamp, insidePriority: SpaceshipRamp.ballPriorityInsideRamp,
outsidePriority: outsidePriority, outsidePriority: outsidePriority,
); ) {
renderBody = false;
}
final double _rotation; final double _rotation;
@ -274,7 +275,6 @@ class _SpaceshipRampOpening extends RampOpening {
@override @override
Shape get shape { Shape get shape {
renderBody = false;
return PolygonShape() return PolygonShape()
..setAsBox( ..setAsBox(
_size.x, _size.x,

@ -27,8 +27,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
: this._( : this._(
majorRadius: 2.9, majorRadius: 2.9,
minorRadius: 2.1, minorRadius: 2.1,
activeAssetPath: Assets.images.sparkyBumper.a.active.keyName, activeAssetPath: Assets.images.sparky.bumper.a.active.keyName,
inactiveAssetPath: Assets.images.sparkyBumper.a.inactive.keyName, inactiveAssetPath: Assets.images.sparky.bumper.a.inactive.keyName,
spriteComponent: SpriteComponent( spriteComponent: SpriteComponent(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(0, -0.25), position: Vector2(0, -0.25),
@ -40,8 +40,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
: this._( : this._(
majorRadius: 2.85, majorRadius: 2.85,
minorRadius: 2, minorRadius: 2,
activeAssetPath: Assets.images.sparkyBumper.b.active.keyName, activeAssetPath: Assets.images.sparky.bumper.b.active.keyName,
inactiveAssetPath: Assets.images.sparkyBumper.b.inactive.keyName, inactiveAssetPath: Assets.images.sparky.bumper.b.inactive.keyName,
spriteComponent: SpriteComponent( spriteComponent: SpriteComponent(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(0, -0.35), position: Vector2(0, -0.35),
@ -53,8 +53,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
: this._( : this._(
majorRadius: 3, majorRadius: 3,
minorRadius: 2.2, minorRadius: 2.2,
activeAssetPath: Assets.images.sparkyBumper.c.active.keyName, activeAssetPath: Assets.images.sparky.bumper.c.active.keyName,
inactiveAssetPath: Assets.images.sparkyBumper.c.inactive.keyName, inactiveAssetPath: Assets.images.sparky.bumper.c.inactive.keyName,
spriteComponent: SpriteComponent( spriteComponent: SpriteComponent(
anchor: Anchor.center, anchor: Anchor.center,
position: Vector2(0, -0.4), position: Vector2(0, -0.4),

@ -0,0 +1,115 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template sparky_computer}
/// A [Blueprint] which creates the [_ComputerBase] and
/// [_ComputerTopSpriteComponent].
/// {@endtemplate}
class SparkyComputer extends Forge2DBlueprint {
@override
void build(_) {
final computerBase = _ComputerBase();
final computerTop = _ComputerTopSpriteComponent();
addAll([
computerBase,
computerTop,
]);
}
}
class _ComputerBase extends BodyComponent with InitialPosition {
_ComputerBase();
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final leftEdge = EdgeShape()
..set(
Vector2(-14.9, 46),
Vector2(-15.3, 49.6),
);
final leftEdgeFixtureDef = FixtureDef(leftEdge);
fixturesDef.add(leftEdgeFixtureDef);
final topEdge = EdgeShape()
..set(
Vector2(-15.3, 49.6),
Vector2(-10.7, 50.6),
);
final topEdgeFixtureDef = FixtureDef(topEdge);
fixturesDef.add(topEdgeFixtureDef);
final rightEdge = EdgeShape()
..set(
Vector2(-10.7, 50.6),
Vector2(-9, 47.2),
);
final rightEdgeFixtureDef = FixtureDef(rightEdge);
fixturesDef.add(rightEdgeFixtureDef);
return fixturesDef;
}
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
return body;
}
@override
Future<void> onLoad() async {
await super.onLoad();
renderBody = false;
await add(_ComputerBaseSpriteComponent());
}
}
class _ComputerBaseSpriteComponent extends SpriteComponent with HasGameRef {
_ComputerBaseSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-11.95, -48.35),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.sparky.computer.base.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}
class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef {
_ComputerTopSpriteComponent()
: super(
anchor: Anchor.center,
position: Vector2(-12.45, -49.75),
priority: 1,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.sparky.computer.top.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}

@ -12,7 +12,8 @@ const _attachedErrorMessage = "Can't add to attached Blueprints";
/// A [Blueprint] is a virtual way of grouping [Component]s /// A [Blueprint] is a virtual way of grouping [Component]s
/// that are related, but they need to be added directly on /// that are related, but they need to be added directly on
/// the [FlameGame] level. /// the [FlameGame] level.
abstract class Blueprint<T extends FlameGame> { // TODO(alestiago): refactor with feat/make-blueprint-extend-component.
abstract class Blueprint<T extends FlameGame> extends Component {
final List<Component> _components = []; final List<Component> _components = [];
final List<Blueprint> _blueprints = []; final List<Blueprint> _blueprints = [];
@ -34,14 +35,9 @@ abstract class Blueprint<T extends FlameGame> {
_isAttached = true; _isAttached = true;
} }
/// Adds a list of [Component]s to this blueprint.
void addAll(List<Component> components) {
assert(!_isAttached, _attachedErrorMessage);
_components.addAll(components);
}
/// Adds a single [Component] to this blueprint. /// Adds a single [Component] to this blueprint.
void add(Component component) { @override
Future<void> add(Component component) async {
assert(!_isAttached, _attachedErrorMessage); assert(!_isAttached, _attachedErrorMessage);
_components.add(component); _components.add(component);
} }

@ -40,9 +40,10 @@ flutter:
- assets/images/chrome_dino/ - assets/images/chrome_dino/
- assets/images/kicker/ - assets/images/kicker/
- assets/images/slingshot/ - assets/images/slingshot/
- assets/images/sparky_bumper/a/ - assets/images/sparky/computer/
- assets/images/sparky_bumper/b/ - assets/images/sparky/bumper/a/
- assets/images/sparky_bumper/c/ - assets/images/sparky/bumper/b/
- assets/images/sparky/bumper/c/
- assets/images/backboard/ - assets/images/backboard/
flutter_gen: flutter_gen:

@ -25,5 +25,8 @@ void main() {
addSparkyBumperStories(dashbook); addSparkyBumperStories(dashbook);
addZoomStories(dashbook); addZoomStories(dashbook);
addBoundariesStories(dashbook); addBoundariesStories(dashbook);
addSpaceshipRampStories(dashbook);
addSpaceshipRailStories(dashbook);
addLaunchRampStories(dashbook);
runApp(dashbook); runApp(dashbook);
} }

@ -4,7 +4,11 @@ import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/common/common.dart'; import 'package:sandbox/common/common.dart';
class BasicBallGame extends BasicGame with TapDetector, Traceable { class BasicBallGame extends BasicGame with TapDetector, Traceable {
BasicBallGame({required this.color}); BasicBallGame({
required this.color,
this.ballPriority = 0,
this.ballLayer = Layer.all,
});
static const info = ''' static const info = '''
Shows how a Ball works. Shows how a Ball works.
@ -13,11 +17,16 @@ class BasicBallGame extends BasicGame with TapDetector, Traceable {
'''; ''';
final Color color; final Color color;
final int ballPriority;
final Layer ballLayer;
@override @override
void onTapUp(TapUpInfo info) { void onTapUp(TapUpInfo info) {
add( add(
Ball(baseColor: color)..initialPosition = info.eventPosition.game, Ball(baseColor: color)
..initialPosition = info.eventPosition.game
..layer = ballLayer
..priority = ballPriority,
); );
traceAllBodies(); traceAllBodies();
} }

@ -0,0 +1,36 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class LaunchRampGame extends BasicBallGame {
LaunchRampGame()
: super(
color: Colors.blue,
ballPriority: LaunchRamp.ballPriorityInsideRamp,
ballLayer: Layer.launcher,
);
static const info = '''
Shows how LaunchRamp are rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
''';
@override
Future<void> onLoad() async {
await super.onLoad();
camera
..followVector2(Vector2(0, 0))
..zoom = 7.5;
final launchRamp = LaunchRamp();
unawaited(addFromBlueprint(launchRamp));
await traceAllBodies();
}
}

@ -0,0 +1,15 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/launch_ramp/launch_ramp_game.dart';
void addLaunchRampStories(Dashbook dashbook) {
dashbook.storiesOf('LaunchRamp').add(
'Basic',
(context) => GameWidget(
game: LaunchRampGame()..trace = context.boolProperty('Trace', true),
),
codeLink: buildSourceLink('launch_ramp/basic.dart'),
info: LaunchRampGame.info,
);
}

@ -0,0 +1,34 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SpaceshipRailGame extends BasicBallGame {
SpaceshipRailGame()
: super(
color: Colors.blue,
ballPriority: SpaceshipRail.ballPriorityInsideRail,
ballLayer: Layer.spaceshipExitRail,
);
static const info = '''
Shows how SpaceshipRail are rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
''';
@override
Future<void> onLoad() async {
await super.onLoad();
camera.followVector2(Vector2(-30, -10));
final spaceshipRail = SpaceshipRail();
unawaited(addFromBlueprint(spaceshipRail));
await traceAllBodies();
}
}

@ -0,0 +1,16 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/spaceship_rail/spaceship_rail_game.dart';
void addSpaceshipRailStories(Dashbook dashbook) {
dashbook.storiesOf('SpaceshipRail').add(
'Basic',
(context) => GameWidget(
game: SpaceshipRailGame()
..trace = context.boolProperty('Trace', true),
),
codeLink: buildSourceLink('spaceship_rail/basic.dart'),
info: SpaceshipRailGame.info,
);
}

@ -0,0 +1,34 @@
import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:sandbox/stories/ball/basic_ball_game.dart';
class SpaceshipRampGame extends BasicBallGame {
SpaceshipRampGame()
: super(
color: Colors.blue,
ballPriority: SpaceshipRamp.ballPriorityInsideRamp,
ballLayer: Layer.spaceshipEntranceRamp,
);
static const info = '''
Shows how SpaceshipRamp are rendered.
- Activate the "trace" parameter to overlay the body.
- Tap anywhere on the screen to spawn a ball into the game.
''';
@override
Future<void> onLoad() async {
await super.onLoad();
camera.followVector2(Vector2(-10, -20));
final spaceshipRamp = SpaceshipRamp();
unawaited(addFromBlueprint(spaceshipRamp));
await traceAllBodies();
}
}

@ -0,0 +1,16 @@
import 'package:dashbook/dashbook.dart';
import 'package:flame/game.dart';
import 'package:sandbox/common/common.dart';
import 'package:sandbox/stories/spaceship_ramp/spaceship_ramp_game.dart';
void addSpaceshipRampStories(Dashbook dashbook) {
dashbook.storiesOf('SpaceshipRamp').add(
'Basic',
(context) => GameWidget(
game: SpaceshipRampGame()
..trace = context.boolProperty('Trace', true),
),
codeLink: buildSourceLink('spaceship_ramp/basic.dart'),
info: SpaceshipRampGame.info,
);
}

@ -5,8 +5,11 @@ export 'chrome_dino/stories.dart';
export 'effects/stories.dart'; export 'effects/stories.dart';
export 'flipper/stories.dart'; export 'flipper/stories.dart';
export 'flutter_forest/stories.dart'; export 'flutter_forest/stories.dart';
export 'launch_ramp/stories.dart';
export 'layer/stories.dart'; export 'layer/stories.dart';
export 'slingshot/stories.dart'; export 'slingshot/stories.dart';
export 'spaceship/stories.dart'; export 'spaceship/stories.dart';
export 'spaceship_rail/stories.dart';
export 'spaceship_ramp/stories.dart';
export 'sparky_bumper/stories.dart'; export 'sparky_bumper/stories.dart';
export 'zoom/stories.dart'; export 'zoom/stories.dart';

@ -116,15 +116,18 @@ void main() {
}); });
}); });
flameTester.test('by applying velocity', (game) async { // TODO(allisonryan0002): delete or retest this if/when solution is added
final ball = Ball(baseColor: Colors.blue); // to prevent forces on a ball while stopped.
await game.ensureAdd(ball);
ball.stop(); // flameTester.test('by applying velocity', (game) async {
// final ball = Ball(baseColor: Colors.blue);
ball.body.linearVelocity.setValues(10, 10); // await game.ensureAdd(ball);
game.update(1); // ball.stop();
expect(ball.body.position, equals(ball.initialPosition));
}); // ball.body.linearVelocity.setValues(10, 10);
// game.update(1);
// expect(ball.body.position, equals(ball.initialPosition));
// });
}); });
group('resume', () { group('resume', () {

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

@ -0,0 +1,30 @@
// ignore_for_file: cascade_invocations
import 'package:flame_forge2d/flame_forge2d.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() {
group('SparkyComputer', () {
final tester = FlameTester(TestGame.new);
tester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.addFromBlueprint(SparkyComputer());
await game.ready();
game.camera.followVector2(Vector2(-15, -50));
},
// TODO(allisonryan0002): enable test when workflows are fixed.
// verify: (game, tester) async {
// await expectLater(
// find.byGame<Forge2DGame>(),
// matchesGoldenFile('golden/sparky-computer.png'),
// );
// },
);
});
}

@ -221,5 +221,22 @@ void main() {
], ],
); );
}); });
group('SparkyTurboChargeActivated', () {
blocTest<GameBloc, GameState>(
'adds game bonus',
build: GameBloc.new,
act: (bloc) => bloc..add(const SparkyTurboChargeActivated()),
expect: () => const [
GameState(
score: 0,
balls: 3,
activatedBonusLetters: [],
activatedDashNests: {},
bonusHistory: [GameBonus.sparkyTurboCharge],
),
],
);
});
}); });
} }

@ -84,5 +84,18 @@ void main() {
); );
}); });
}); });
group('SparkyTurboChargeActivated', () {
test('can be instantiated', () {
expect(const SparkyTurboChargeActivated(), isNotNull);
});
test('supports value equality', () {
expect(
SparkyTurboChargeActivated(),
equals(SparkyTurboChargeActivated()),
);
});
});
}); });
} }

@ -1,8 +1,9 @@
// ignore_for_file: cascade_invocations // ignore_for_file: cascade_invocations
import 'package:bloc_test/bloc_test.dart'; import 'package:bloc_test/bloc_test.dart';
import 'package:flame/extensions.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
@ -11,18 +12,21 @@ import 'package:pinball_components/pinball_components.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
// TODO(allisonryan0002): remove once
// https://github.com/flame-engine/flame/pull/1520 is merged
class WrappedBallController extends BallController {
WrappedBallController(Ball<Forge2DGame> ball, this._gameRef) : super(ball);
final PinballGame _gameRef;
@override
PinballGame get gameRef => _gameRef;
}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
group('BallController', () { group('BallController', () {
test('can be instantiated', () {
expect(
BallController(MockBall()),
isA<BallController>(),
);
});
group('description', () {
late Ball ball; late Ball ball;
late GameBloc gameBloc; late GameBloc gameBloc;
@ -41,6 +45,13 @@ void main() {
blocBuilder: () => gameBloc, blocBuilder: () => gameBloc,
); );
test('can be instantiated', () {
expect(
BallController(MockBall()),
isA<BallController>(),
);
});
flameBlocTester.testGameWidget( flameBlocTester.testGameWidget(
'lost adds BallLost to GameBloc', 'lost adds BallLost to GameBloc',
setUp: (game, tester) async { setUp: (game, tester) async {
@ -54,6 +65,71 @@ void main() {
verify(() => gameBloc.add(const BallLost())).called(1); verify(() => gameBloc.add(const BallLost())).called(1);
}, },
); );
group('turboCharge', () {
setUpAll(() {
registerFallbackValue(Vector2.zero());
});
flameBlocTester.testGameWidget(
'adds TurboChargeActivated',
setUp: (game, tester) async {
final controller = BallController(ball);
await ball.add(controller);
await game.ensureAdd(ball);
await controller.turboCharge();
},
verify: (game, tester) async {
verify(() => gameBloc.add(const SparkyTurboChargeActivated()))
.called(1);
},
);
flameBlocTester.test(
'initially stops the ball',
(game) async {
final gameRef = MockPinballGame();
final ball = MockControlledBall();
final controller = WrappedBallController(ball, gameRef);
when(() => gameRef.read<GameBloc>()).thenReturn(gameBloc);
when(() => ball.controller).thenReturn(controller);
await controller.turboCharge();
verify(ball.stop).called(1);
},
);
flameBlocTester.test(
'resumes the ball',
(game) async {
final gameRef = MockPinballGame();
final ball = MockControlledBall();
final controller = WrappedBallController(ball, gameRef);
when(() => gameRef.read<GameBloc>()).thenReturn(gameBloc);
when(() => ball.controller).thenReturn(controller);
await controller.turboCharge();
verify(ball.resume).called(1);
},
);
flameBlocTester.test(
'boosts the ball',
(game) async {
final gameRef = MockPinballGame();
final ball = MockControlledBall();
final controller = WrappedBallController(ball, gameRef);
when(() => gameRef.read<GameBloc>()).thenReturn(gameBloc);
when(() => ball.controller).thenReturn(controller);
await controller.turboCharge();
verify(() => ball.boost(any())).called(1);
},
);
}); });
}); });
} }

@ -0,0 +1,45 @@
// ignore_for_file: cascade_invocations
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 '../../helpers/helpers.dart';
void main() {
group('SparkyComputerController', () {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(EmptyPinballGameTest.new);
late ControlledSparkyComputer controlledSparkyComputer;
setUp(() {
controlledSparkyComputer = ControlledSparkyComputer();
});
test('can be instantiated', () {
expect(
SparkyComputerController(controlledSparkyComputer),
isA<SparkyComputerController>(),
);
});
flameTester.testGameWidget(
'SparkyTurboChargeSensorBallContactCallback turbo charges the ball',
setUp: (game, tester) async {
final contackCallback = SparkyTurboChargeSensorBallContactCallback();
final sparkyTurboChargeSensor = MockSparkyTurboChargeSensor();
final ball = MockControlledBall();
final controller = MockBallController();
when(() => ball.controller).thenReturn(controller);
when(controller.turboCharge).thenAnswer((_) async {});
contackCallback.begin(sparkyTurboChargeSensor, ball, MockContact());
verify(() => ball.controller.turboCharge()).called(1);
},
);
});
}

@ -1,8 +1,13 @@
// ignore_for_file: cascade_invocations // ignore_for_file: cascade_invocations
import 'dart:ui';
import 'package:bloc_test/bloc_test.dart';
import 'package:flame_test/flame_test.dart'; import 'package:flame_test/flame_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:pinball/game/game.dart'; import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -11,13 +16,57 @@ void main() {
final flameTester = FlameTester(EmptyPinballGameTest.new); final flameTester = FlameTester(EmptyPinballGameTest.new);
group('SparkyFireZone', () { group('SparkyFireZone', () {
flameTester.test(
'loads correctly',
(game) async {
await game.ready();
final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone);
expect(game.contains(sparkyFireZone), isTrue);
},
);
group('loads', () {
flameTester.test(
'three SparkyBumper',
(game) async {
await game.ready();
final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone);
expect(
sparkyFireZone.descendants().whereType<SparkyBumper>().length,
equals(3),
);
},
);
});
group('bumpers', () { group('bumpers', () {
late ControlledSparkyBumper controlledSparkyBumper; late ControlledSparkyBumper controlledSparkyBumper;
late Ball ball;
late GameBloc gameBloc;
setUp(() {
ball = Ball(baseColor: const Color(0xFF00FFFF));
gameBloc = MockGameBloc();
whenListen(
gameBloc,
const Stream<GameState>.empty(),
initialState: const GameState.initial(),
);
});
final flameBlocTester = FlameBlocTester<PinballGame, GameBloc>(
gameBuilder: EmptyPinballGameTest.new,
blocBuilder: () => gameBloc,
);
flameTester.testGameWidget( flameTester.testGameWidget(
'activate when deactivated bumper is hit', 'activate when deactivated bumper is hit',
setUp: (game, tester) async { setUp: (game, tester) async {
controlledSparkyBumper = ControlledSparkyBumper(); controlledSparkyBumper = ControlledSparkyBumper.a();
await game.ensureAdd(controlledSparkyBumper); await game.ensureAdd(controlledSparkyBumper);
controlledSparkyBumper.controller.hit(); controlledSparkyBumper.controller.hit();
@ -30,7 +79,7 @@ void main() {
flameTester.testGameWidget( flameTester.testGameWidget(
'deactivate when activated bumper is hit', 'deactivate when activated bumper is hit',
setUp: (game, tester) async { setUp: (game, tester) async {
controlledSparkyBumper = ControlledSparkyBumper(); controlledSparkyBumper = ControlledSparkyBumper.a();
await game.ensureAdd(controlledSparkyBumper); await game.ensureAdd(controlledSparkyBumper);
controlledSparkyBumper.controller.hit(); controlledSparkyBumper.controller.hit();
@ -40,6 +89,27 @@ void main() {
expect(controlledSparkyBumper.controller.isActivated, isFalse); expect(controlledSparkyBumper.controller.isActivated, isFalse);
}, },
); );
flameBlocTester.testGameWidget(
'add Scored event',
setUp: (game, tester) async {
final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone);
await game.ensureAdd(ball);
game.addContactCallback(BallScorePointsCallback(game));
final bumpers = sparkyFireZone.descendants().whereType<ScorePoints>();
for (final bumper in bumpers) {
beginContact(game, bumper, ball);
verify(
() => gameBloc.add(
Scored(points: bumper.points),
),
).called(1);
}
},
);
}); });
}); });
} }

@ -62,6 +62,14 @@ void main() {
); );
}); });
flameTester.test(
'one SparkyFireZone',
(game) async {
await game.ready();
expect(game.children.whereType<SparkyFireZone>().length, equals(1));
},
);
group('controller', () { group('controller', () {
// TODO(alestiago): Write test to be controller agnostic. // TODO(alestiago): Write test to be controller agnostic.
group('listenWhen', () { group('listenWhen', () {

@ -76,6 +76,9 @@ class MockDashNestBumper extends Mock implements DashNestBumper {}
class MockPinballAudio extends Mock implements PinballAudio {} class MockPinballAudio extends Mock implements PinballAudio {}
class MockSparkyTurboChargeSensor extends Mock
implements SparkyTurboChargeSensor {}
class MockAssetsManagerCubit extends Mock implements AssetsManagerCubit {} class MockAssetsManagerCubit extends Mock implements AssetsManagerCubit {}
class MockBackboard extends Mock implements Backboard {} class MockBackboard extends Mock implements Backboard {}

Loading…
Cancel
Save