feat: added SparkyFireZone component to the board (#165)

* feat: added SparkyFireZone component to the board

* chore: doc for sparky fire zone

* refactor: moved sparky from board to pinball

* refactor: ControlledSparkyBumper visible for testing

* test: fixed test

* test: fixed sparky test in pinball
pull/177/head
Rui Miguel Alonso 2 years ago committed by GitHub
parent 0bec997178
commit 69a88441a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,21 +1,67 @@
// 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';
// 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
// ignore: public_member_api_docs
final lowerLeftBumper = ControlledSparkyBumper.a()
..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
with Controls<_SparkyBumperController> {
// TODO(ruimiguel): make private and remove ignore once SparkyFireZone is done
// ignore: public_member_api_docs
ControlledSparkyBumper() : super.a() {
with Controls<_SparkyBumperController>, ScorePoints {
///{@macro controlled_sparky_bumper}
ControlledSparkyBumper.a() : 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);
}
@override
int get points => 20;
}
/// {@template sparky_bumper_controller}
@ -42,3 +88,16 @@ class _SparkyBumperController extends ComponentController<SparkyBumper>
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();
}
}

@ -52,6 +52,7 @@ class PinballGame extends Forge2DGame
await add(plunger);
unawaited(add(Board()));
unawaited(add(SparkyFireZone()));
unawaited(addFromBlueprint(Slingshots()));
unawaited(addFromBlueprint(DinoWalls()));
unawaited(_addBonusWord());

@ -1,8 +1,13 @@
// 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';
@ -11,13 +16,57 @@ void main() {
final flameTester = FlameTester(EmptyPinballGameTest.new);
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', () {
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(
'activate when deactivated bumper is hit',
setUp: (game, tester) async {
controlledSparkyBumper = ControlledSparkyBumper();
controlledSparkyBumper = ControlledSparkyBumper.a();
await game.ensureAdd(controlledSparkyBumper);
controlledSparkyBumper.controller.hit();
@ -30,7 +79,7 @@ void main() {
flameTester.testGameWidget(
'deactivate when activated bumper is hit',
setUp: (game, tester) async {
controlledSparkyBumper = ControlledSparkyBumper();
controlledSparkyBumper = ControlledSparkyBumper.a();
await game.ensureAdd(controlledSparkyBumper);
controlledSparkyBumper.controller.hit();
@ -40,6 +89,27 @@ void main() {
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', () {
// TODO(alestiago): Write test to be controller agnostic.
group('listenWhen', () {

Loading…
Cancel
Save