Merge branch 'main' into feat/sandbox-assets-game

pull/227/head
Alejandro Santiago 3 years ago committed by GitHub
commit 0f38e2a583
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,18 @@
name: share_repository
on:
push:
paths:
- "packages/share_repository/**"
- ".github/workflows/share_repository.yaml"
pull_request:
paths:
- "packages/share_repository/**"
- ".github/workflows/share_repository.yaml"
jobs:
build:
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
with:
working_directory: packages/share_repository

@ -4,7 +4,6 @@ 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 'google_word.dart';

@ -62,10 +62,13 @@ class BallController extends ComponentController<Ball>
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));
// TODO(alestiago): Refactor this hard coded duration once the following is
// merged:
// https://github.com/flame-engine/flame/pull/1564
await Future<void>.delayed(
const Duration(milliseconds: 2583),
);
component.resume();
await component.boost(Vector2(40, 110));
}

@ -1,52 +0,0 @@
// 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/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template controlled_sparky_computer}
/// [SparkyComputer] with a [SparkyComputerController] attached.
/// {@endtemplate}
class ControlledSparkyComputer extends SparkyComputer
with Controls<SparkyComputerController>, HasGameRef<Forge2DGame> {
/// {@macro controlled_sparky_computer}
ControlledSparkyComputer() : super() {
controller = SparkyComputerController(this);
}
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(SparkyComputerSensorBallContactCallback());
}
}
/// {@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_computer_sensor_ball_contact_callback}
/// Turbo charges the [Ball] when it enters the [SparkyComputer]
/// {@endtemplate}
@visibleForTesting
class SparkyComputerSensorBallContactCallback
extends ContactCallback<SparkyComputerSensor, ControlledBall> {
/// {@macro sparky_computer_sensor_ball_contact_callback}
SparkyComputerSensorBallContactCallback();
@override
void begin(_, ControlledBall ball, __) {
ball.controller.turboCharge();
}
}

@ -1,10 +1,10 @@
// 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/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_fire_zone}
/// Area positioned at the top left of the [Board] where the [Ball]
@ -12,29 +12,21 @@ import 'package:pinball_components/pinball_components.dart';
///
/// When a [Ball] hits [SparkyBumper]s, the bumper animates.
/// {@endtemplate}
class SparkyFireZone extends Component with HasGameRef<PinballGame> {
class SparkyFireZone extends Blueprint {
/// {@macro sparky_fire_zone}
SparkyFireZone();
@override
Future<void> onLoad() async {
await super.onLoad();
gameRef.addContactCallback(SparkyBumperBallContactCallback());
final lowerLeftBumper = _SparkyBumper.a()
..initialPosition = Vector2(-22.9, -41.65);
final upperLeftBumper = _SparkyBumper.b()
..initialPosition = Vector2(-21.25, -57.9);
final rightBumper = _SparkyBumper.c()
..initialPosition = Vector2(-3.3, -52.55);
await addAll([
lowerLeftBumper,
upperLeftBumper,
rightBumper,
]);
}
SparkyFireZone()
: super(
components: [
_SparkyBumper.a()..initialPosition = Vector2(-22.9, -41.65),
_SparkyBumper.b()..initialPosition = Vector2(-21.25, -57.9),
_SparkyBumper.c()..initialPosition = Vector2(-3.3, -52.55),
SparkyComputerSensor()..initialPosition = Vector2(-13, -49.8),
SparkyAnimatronic()..position = Vector2(-13.8, -58.2),
],
blueprints: [
SparkyComputer(),
],
);
}
// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D
@ -48,6 +40,14 @@ class _SparkyBumper extends SparkyBumper with ScorePoints {
@override
int get points => 20;
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Revisit once this has been merged:
// https://github.com/flame-engine/flame/pull/1547
gameRef.addContactCallback(SparkyBumperBallContactCallback());
}
}
/// Listens when a [Ball] bounces bounces against a [SparkyBumper].
@ -63,3 +63,48 @@ class SparkyBumperBallContactCallback
sparkyBumper.animate();
}
}
/// {@template sparky_computer_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer].
/// {@endtemplate}
// TODO(alestiago): Revisit once this has been merged:
// https://github.com/flame-engine/flame/pull/1547
class SparkyComputerSensor extends BodyComponent with InitialPosition {
/// {@macro sparky_computer_sensor}
SparkyComputerSensor() {
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);
}
@override
Future<void> onLoad() async {
await super.onLoad();
// TODO(alestiago): Revisit once this has been merged:
// https://github.com/flame-engine/flame/pull/1547
gameRef.addContactCallback(SparkyComputerSensorBallContactCallback());
}
}
@visibleForTesting
// TODO(alestiago): Revisit once this has been merged:
// https://github.com/flame-engine/flame/pull/1547
// ignore: public_member_api_docs
class SparkyComputerSensorBallContactCallback
extends ContactCallback<SparkyComputerSensor, ControlledBall> {
@override
void begin(_, ControlledBall controlledBall, __) {
controlledBall.controller.turboCharge();
controlledBall.gameRef.firstChild<SparkyAnimatronic>()?.playing = true;
}
}

@ -43,8 +43,11 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.dash.bumper.b.inactive.keyName),
images.load(components.Assets.images.dash.bumper.main.active.keyName),
images.load(components.Assets.images.dash.bumper.main.inactive.keyName),
images.load(components.Assets.images.plunger.plunger.keyName),
images.load(components.Assets.images.plunger.rocket.keyName),
images.load(components.Assets.images.boundary.bottom.keyName),
images.load(components.Assets.images.boundary.outer.keyName),
images.load(components.Assets.images.boundary.outerBottom.keyName),
images.load(components.Assets.images.spaceship.saucer.keyName),
images.load(components.Assets.images.spaceship.bridge.keyName),
images.load(components.Assets.images.spaceship.ramp.boardOpening.keyName),
@ -80,12 +83,11 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.alienBumper.b.inactive.keyName),
images.load(components.Assets.images.chromeDino.mouth.keyName),
images.load(components.Assets.images.chromeDino.head.keyName),
images.load(components.Assets.images.plunger.plunger.keyName),
images.load(components.Assets.images.plunger.rocket.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.computer.base.keyName),
images.load(components.Assets.images.sparky.animatronic.keyName),
images.load(components.Assets.images.sparky.bumper.a.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.a.active.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),

@ -2,6 +2,7 @@
import 'dart:async';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/input.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
@ -49,13 +50,13 @@ class PinballGame extends Forge2DGame
// TODO(allisonryan0002): banish Wall and Board classes in later PR.
await add(BottomWall());
unawaited(addFromBlueprint(Boundaries()));
unawaited(addFromBlueprint(ControlledSparkyComputer()));
unawaited(addFromBlueprint(LaunchRamp()));
final launcher = Launcher();
unawaited(addFromBlueprint(launcher));
unawaited(add(Board()));
unawaited(add(AlienZone()));
unawaited(add(SparkyFireZone()));
await addFromBlueprint(SparkyFireZone());
unawaited(addFromBlueprint(Slingshots()));
unawaited(addFromBlueprint(DinoWalls()));
unawaited(_addBonusWord());
@ -134,7 +135,7 @@ class _GameBallsController extends ComponentController<PinballGame>
}
}
class DebugPinballGame extends PinballGame with TapDetector {
class DebugPinballGame extends PinballGame with FPSCounter, TapDetector {
DebugPinballGame({
required PinballTheme theme,
required PinballAudio audio,
@ -149,6 +150,7 @@ class DebugPinballGame extends PinballGame with TapDetector {
Future<void> onLoad() async {
await super.onLoad();
await _loadBackground();
await add(_DebugInformation());
}
// TODO(alestiago): Move to PinballGame once we have the real background
@ -191,3 +193,35 @@ class _DebugGameBallsController extends _GameBallsController {
return noBallsLeft && canBallRespawn;
}
}
class _DebugInformation extends Component with HasGameRef<DebugPinballGame> {
_DebugInformation() : super(priority: RenderPriority.debugInfo);
@override
PositionType get positionType => PositionType.widget;
final _debugTextPaint = TextPaint(
style: const TextStyle(
color: Colors.green,
fontSize: 10,
),
);
final _debugBackgroundPaint = Paint()..color = Colors.white;
@override
void render(Canvas canvas) {
final debugText = [
'FPS: ${gameRef.fps().toStringAsFixed(1)}',
'BALLS: ${gameRef.descendants().whereType<ControlledBall>().length}',
].join(' | ');
final height = _debugTextPaint.measureTextHeight(debugText);
final position = Vector2(0, gameRef.camera.canvasSize.y - height);
canvas.drawRect(
position & Vector2(gameRef.camera.canvasSize.x, height),
_debugBackgroundPaint,
);
_debugTextPaint.render(canvas, debugText, position);
}
}

@ -36,7 +36,7 @@ extension LeaderboardEntryDataX on LeaderboardEntryData {
rank: position.toString(),
playerInitials: playerInitials,
score: score,
character: character.toTheme.character,
character: character.toTheme.leaderboardIcon,
);
}
}

@ -124,7 +124,7 @@ class CharacterImageButton extends StatelessWidget {
),
child: Padding(
padding: const EdgeInsets.all(8),
child: characterTheme.character.image(),
child: characterTheme.icon.image(),
),
),
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

@ -86,6 +86,10 @@ class $AssetsImagesBoundaryGen {
AssetGenImage get bottom =>
const AssetGenImage('assets/images/boundary/bottom.png');
/// File path: assets/images/boundary/outer-bottom.png
AssetGenImage get outerBottom =>
const AssetGenImage('assets/images/boundary/outer-bottom.png');
/// File path: assets/images/boundary/outer.png
AssetGenImage get outer =>
const AssetGenImage('assets/images/boundary/outer.png');
@ -257,6 +261,8 @@ class $AssetsImagesSpaceshipGen {
class $AssetsImagesSparkyGen {
const $AssetsImagesSparkyGen();
AssetGenImage get animatronic =>
const AssetGenImage('assets/images/sparky/animatronic.png');
$AssetsImagesSparkyBumperGen get bumper =>
const $AssetsImagesSparkyBumperGen();
$AssetsImagesSparkyComputerGen get computer =>

@ -13,6 +13,7 @@ class Boundaries extends Blueprint {
components: [
_BottomBoundary(),
_OuterBoundary(),
_OuterBottomBoundarySpriteComponent(),
],
);
}
@ -91,8 +92,10 @@ class _OuterBoundary extends BodyComponent with InitialPosition {
/// {@macro outer_boundary}
_OuterBoundary()
: super(
priority: RenderPriority.outerBoudary,
children: [_OuterBoundarySpriteComponent()],
priority: RenderPriority.outerBoundary,
children: [
_OuterBoundarySpriteComponent(),
],
) {
renderBody = false;
}
@ -157,3 +160,25 @@ class _OuterBoundarySpriteComponent extends SpriteComponent with HasGameRef {
size = sprite.originalSize / 10;
}
}
class _OuterBottomBoundarySpriteComponent extends SpriteComponent
with HasGameRef {
_OuterBottomBoundarySpriteComponent()
: super(
priority: RenderPriority.outerBottomBoundary,
anchor: Anchor.center,
position: Vector2(0, 71),
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = Sprite(
gameRef.images.fromCache(
Assets.images.boundary.outerBottom.keyName,
),
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}

@ -29,5 +29,6 @@ export 'slingshot.dart';
export 'spaceship.dart';
export 'spaceship_rail.dart';
export 'spaceship_ramp.dart';
export 'sparky_animatronic.dart';
export 'sparky_bumper.dart';
export 'sparky_computer.dart';

@ -38,17 +38,9 @@ class DashAnimatronic extends SpriteAnimationComponent with HasGameRef {
textureSize: textureSize,
loop: false,
),
);
}
@override
void update(double dt) {
super.update(dt);
if (animation != null) {
if (animation!.isLastFrame) {
animation!.reset();
)..onComplete = () {
animation?.reset();
playing = false;
}
}
};
}
}

@ -39,7 +39,9 @@ abstract class RenderPriority {
static const int bottomBoundary = _above + dinoBottomWall;
static const int outerBoudary = _above + background;
static const int outerBoundary = _above + background;
static const int outerBottomBoundary = _above + rocket;
// Bottom Group
@ -47,7 +49,7 @@ abstract class RenderPriority {
// Launcher
static const int launchRamp = _above + outerBoudary;
static const int launchRamp = _above + outerBoundary;
static const int launchRampForegroundRailing = _below + ballOnBoard;
@ -113,4 +115,7 @@ abstract class RenderPriority {
// Score Text
static const int scoreText = _above + spaceshipRampForegroundRailing;
// Debug information
static const int debugInfo = _above + scoreText;
}

@ -0,0 +1,46 @@
import 'package:flame/components.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template sparky_animatronic}
/// Animated Sparky that sits on top of the [SparkyComputer].
/// {@endtemplate}
class SparkyAnimatronic extends SpriteAnimationComponent with HasGameRef {
/// {@macro sparky_animatronic}
SparkyAnimatronic()
: super(
anchor: Anchor.center,
playing: false,
priority: RenderPriority.sparkyAnimatronic,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final spriteSheet = gameRef.images.fromCache(
Assets.images.sparky.animatronic.keyName,
);
const amountPerRow = 9;
const amountPerColumn = 7;
final textureSize = Vector2(
spriteSheet.width / amountPerRow,
spriteSheet.height / amountPerColumn,
);
size = textureSize / 10;
animation = SpriteAnimation.fromFrameData(
spriteSheet,
SpriteAnimationData.sequenced(
amount: (amountPerRow * amountPerColumn) - 1,
amountPerRow: amountPerRow,
stepTime: 1 / 24,
textureSize: textureSize,
loop: false,
),
)..onComplete = () {
animation?.reset();
playing = false;
};
}
}

@ -7,9 +7,6 @@ import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_computer}
/// A computer owned by Sparky.
///
/// Register a [ContactCallback] for [SparkyComputerSensor] to listen when
/// something enters the [SparkyComputer].
/// {@endtemplate}
class SparkyComputer extends Blueprint {
/// {@macro sparky_computer}
@ -18,7 +15,6 @@ class SparkyComputer extends Blueprint {
components: [
_ComputerBase(),
_ComputerTopSpriteComponent(),
SparkyComputerSensor(),
],
);
}
@ -104,24 +100,3 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef {
size = sprite.originalSize / 10;
}
}
/// {@template sparky_computer_sensor}
/// Small sensor body used to detect when a ball has entered the
/// [SparkyComputer].
/// {@endtemplate}
class SparkyComputerSensor extends BodyComponent with InitialPosition {
/// {@macro sparky_computer_sensor}
SparkyComputerSensor() {
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);
}
}

@ -61,6 +61,7 @@ flutter:
- assets/images/slingshot/
- assets/images/alien_bumper/a/
- assets/images/alien_bumper/b/
- assets/images/sparky/
- assets/images/sparky/computer/
- assets/images/sparky/bumper/a/
- assets/images/sparky/bumper/b/

@ -8,6 +8,7 @@ class BoundariesGame extends BallGame {
: super(
imagesFileNames: [
Assets.images.boundary.outer.keyName,
Assets.images.boundary.outerBottom.keyName,
Assets.images.boundary.bottom.keyName,
],
);

@ -10,20 +10,24 @@ import '../../helpers/helpers.dart';
void main() {
group('Boundaries', () {
TestWidgetsFlutterBinding.ensureInitialized();
final assets = [
Assets.images.boundary.outer.keyName,
Assets.images.boundary.outerBottom.keyName,
Assets.images.boundary.bottom.keyName,
];
final tester = FlameTester(TestGame.new);
final flameTester = FlameTester(TestGame.new);
tester.testGameWidget(
flameTester.testGameWidget(
'render correctly',
setUp: (game, tester) async {
await game.images.loadAll(assets);
await game.addFromBlueprint(Boundaries());
await game.ready();
game.camera.followVector2(Vector2.zero());
game.camera.zoom = 3.2;
await tester.pump();
},
verify: (game, tester) async {
await expectLater(

@ -22,7 +22,9 @@ void main() {
await tester.pump();
},
verify: (game, tester) async {
const animationDuration = 3.25;
final animationDuration =
game.firstChild<DashAnimatronic>()!.animation!.totalDuration();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/dash_animatronic/start.png'),
@ -60,8 +62,7 @@ void main() {
await game.ensureAdd(dashAnimatronic);
dashAnimatronic.playing = true;
dashAnimatronic.animation?.setToLast();
game.update(1);
game.update(4);
expect(dashAnimatronic.playing, isFalse);
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

@ -0,0 +1,76 @@
// ignore_for_file: cascade_invocations
import 'package:flame/extensions.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();
group('SparkyAnimatronic', () {
final asset = Assets.images.sparky.animatronic.keyName;
final flameTester = FlameTester(() => TestGame([asset]));
flameTester.testGameWidget(
'renders correctly',
setUp: (game, tester) async {
await game.images.load(asset);
await game.ensureAdd(SparkyAnimatronic()..playing = true);
await tester.pump();
game.camera.followVector2(Vector2.zero());
},
verify: (game, tester) async {
final animationDuration =
game.firstChild<SparkyAnimatronic>()!.animation!.totalDuration();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/sparky_animatronic/start.png'),
);
game.update(animationDuration * 0.25);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/sparky_animatronic/middle.png'),
);
game.update(animationDuration * 0.75);
await tester.pump();
await expectLater(
find.byGame<TestGame>(),
matchesGoldenFile('golden/sparky_animatronic/end.png'),
);
},
);
flameTester.test(
'loads correctly',
(game) async {
final sparkyAnimatronic = SparkyAnimatronic();
await game.ensureAdd(sparkyAnimatronic);
expect(game.contains(sparkyAnimatronic), isTrue);
},
);
flameTester.test(
'stops animating after animation completes',
(game) async {
final sparkyAnimatronic = SparkyAnimatronic();
await game.ensureAdd(sparkyAnimatronic);
sparkyAnimatronic.playing = true;
final animationDuration =
game.firstChild<SparkyAnimatronic>()!.animation!.totalDuration();
game.update(animationDuration);
expect(sparkyAnimatronic.playing, isFalse);
},
);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

@ -28,6 +28,10 @@ class $AssetsImagesGen {
class $AssetsImagesAndroidGen {
const $AssetsImagesAndroidGen();
/// File path: assets/images/android/animation.png
AssetGenImage get animation =>
const AssetGenImage('assets/images/android/animation.png');
/// File path: assets/images/android/background.png
AssetGenImage get background =>
const AssetGenImage('assets/images/android/background.png');
@ -48,6 +52,10 @@ class $AssetsImagesAndroidGen {
class $AssetsImagesDashGen {
const $AssetsImagesDashGen();
/// File path: assets/images/dash/animation.png
AssetGenImage get animation =>
const AssetGenImage('assets/images/dash/animation.png');
/// File path: assets/images/dash/background.png
AssetGenImage get background =>
const AssetGenImage('assets/images/dash/background.png');
@ -67,6 +75,10 @@ class $AssetsImagesDashGen {
class $AssetsImagesDinoGen {
const $AssetsImagesDinoGen();
/// File path: assets/images/dino/animation.png
AssetGenImage get animation =>
const AssetGenImage('assets/images/dino/animation.png');
/// File path: assets/images/dino/background.png
AssetGenImage get background =>
const AssetGenImage('assets/images/dino/background.png');
@ -86,6 +98,10 @@ class $AssetsImagesDinoGen {
class $AssetsImagesSparkyGen {
const $AssetsImagesSparkyGen();
/// File path: assets/images/sparky/animation.png
AssetGenImage get animation =>
const AssetGenImage('assets/images/sparky/animation.png');
/// File path: assets/images/sparky/background.png
AssetGenImage get background =>
const AssetGenImage('assets/images/sparky/background.png');

@ -14,9 +14,6 @@ class AndroidTheme extends CharacterTheme {
@override
Color get ballColor => Colors.green;
@override
AssetGenImage get character => Assets.images.android.character;
@override
AssetGenImage get background => Assets.images.android.background;
@ -25,4 +22,7 @@ class AndroidTheme extends CharacterTheme {
@override
AssetGenImage get leaderboardIcon => Assets.images.android.leaderboardIcon;
@override
AssetGenImage get animation => Assets.images.android.animation;
}

@ -18,9 +18,6 @@ abstract class CharacterTheme extends Equatable {
/// Ball color for this theme.
Color get ballColor;
/// Asset for the theme character.
AssetGenImage get character;
/// Asset for the background.
AssetGenImage get background;
@ -30,13 +27,16 @@ abstract class CharacterTheme extends Equatable {
/// Icon asset for the leaderboard.
AssetGenImage get leaderboardIcon;
/// Asset for the the idle character animation.
AssetGenImage get animation;
@override
List<Object?> get props => [
name,
ballColor,
character,
background,
icon,
leaderboardIcon,
animation,
];
}

@ -14,9 +14,6 @@ class DashTheme extends CharacterTheme {
@override
Color get ballColor => Colors.blue;
@override
AssetGenImage get character => Assets.images.dash.character;
@override
AssetGenImage get background => Assets.images.dash.background;
@ -25,4 +22,7 @@ class DashTheme extends CharacterTheme {
@override
AssetGenImage get leaderboardIcon => Assets.images.dash.leaderboardIcon;
@override
AssetGenImage get animation => Assets.images.dash.animation;
}

@ -14,9 +14,6 @@ class DinoTheme extends CharacterTheme {
@override
Color get ballColor => Colors.grey;
@override
AssetGenImage get character => Assets.images.dino.character;
@override
AssetGenImage get background => Assets.images.dino.background;
@ -25,4 +22,7 @@ class DinoTheme extends CharacterTheme {
@override
AssetGenImage get leaderboardIcon => Assets.images.dino.leaderboardIcon;
@override
AssetGenImage get animation => Assets.images.dino.animation;
}

@ -14,9 +14,6 @@ class SparkyTheme extends CharacterTheme {
@override
String get name => 'Sparky';
@override
AssetGenImage get character => Assets.images.sparky.character;
@override
AssetGenImage get background => Assets.images.sparky.background;
@ -25,4 +22,7 @@ class SparkyTheme extends CharacterTheme {
@override
AssetGenImage get leaderboardIcon => Assets.images.sparky.leaderboardIcon;
@override
AssetGenImage get animation => Assets.images.sparky.animation;
}

@ -0,0 +1,7 @@
# See https://www.dartlang.org/guides/libraries/private-files
# Files and directories created by pub
.dart_tool/
.packages
build/
pubspec.lock

@ -0,0 +1,11 @@
# share_repository
[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link]
[![License: MIT][license_badge]][license_link]
Repository to facilitate sharing scores.
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
[license_link]: https://opensource.org/licenses/MIT
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg
[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis

@ -0,0 +1 @@
include: package:very_good_analysis/analysis_options.2.4.0.yaml

@ -0,0 +1,3 @@
library share_repository;
export 'src/share_repository.dart';

@ -0,0 +1,7 @@
/// {@template share_repository}
/// Repository to facilitate sharing scores.
/// {@endtemplate}
class ShareRepository {
/// {@macro share_repository}
const ShareRepository();
}

@ -0,0 +1,13 @@
name: share_repository
description: Repository to facilitate sharing scores.
version: 1.0.0+1
publish_to: none
environment:
sdk: ">=2.16.0 <3.0.0"
dev_dependencies:
coverage: ^1.1.0
mocktail: ^0.2.0
test: ^1.19.2
very_good_analysis: ^2.4.0

@ -0,0 +1,11 @@
// ignore_for_file: prefer_const_constructors
import 'package:share_repository/share_repository.dart';
import 'package:test/test.dart';
void main() {
group('ShareRepository', () {
test('can be instantiated', () {
expect(ShareRepository(), isNotNull);
});
});
}

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "39.0.0"
version: "31.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
version: "2.8.0"
args:
dependency: transitive
description:
@ -71,6 +71,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
clock:
dependency: transitive
description:
@ -314,7 +321,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
version: "0.6.3"
json_annotation:
dependency: transitive
description:
@ -349,7 +356,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
version: "0.1.3"
meta:
dependency: transitive
description:
@ -412,7 +419,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.0"
path_provider:
dependency: transitive
description:
@ -585,7 +592,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
version: "1.8.1"
stack_trace:
dependency: transitive
description:
@ -627,21 +634,21 @@ packages:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.21.1"
version: "1.19.5"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
version: "0.4.8"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.13"
version: "0.4.9"
typed_data:
dependency: transitive
description:
@ -662,7 +669,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
version: "2.1.1"
very_good_analysis:
dependency: "direct dev"
description:

@ -1,38 +0,0 @@
// 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('ControlledSparkyComputer', () {
TestWidgetsFlutterBinding.ensureInitialized();
final flameTester = FlameTester(EmptyPinballTestGame.new);
flameTester.test('loads correctly', (game) async {
final sparkyComputer = ControlledSparkyComputer();
await game.ensureAdd(sparkyComputer);
expect(game.children, contains(sparkyComputer));
});
flameTester.testGameWidget(
'SparkyTurboChargeSensorBallContactCallback turbo charges the ball',
setUp: (game, tester) async {
final contackCallback = SparkyComputerSensorBallContactCallback();
final sparkyTurboChargeSensor = MockSparkyComputerSensor();
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);
},
);
});
}

@ -8,6 +8,7 @@ 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 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart';
@ -20,29 +21,50 @@ void main() {
Assets.images.sparky.bumper.b.inactive.keyName,
Assets.images.sparky.bumper.c.active.keyName,
Assets.images.sparky.bumper.c.inactive.keyName,
Assets.images.sparky.animatronic.keyName,
];
final flameTester = FlameTester(() => EmptyPinballTestGame(assets));
group('SparkyFireZone', () {
flameTester.test('loads correctly', (game) async {
final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone);
});
group('loads', () {
flameTester.test(
'loads correctly',
'a SparkyComputer',
(game) async {
expect(
SparkyFireZone().blueprints.whereType<SparkyComputer>().single,
isNotNull,
);
},
);
flameTester.test(
'a SparkyAnimatronic',
(game) async {
final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone);
await game.addFromBlueprint(sparkyFireZone);
await game.ready();
expect(game.contains(sparkyFireZone), isTrue);
expect(
game.descendants().whereType<SparkyAnimatronic>().single,
isNotNull,
);
},
);
group('loads', () {
flameTester.test(
'three SparkyBumper',
(game) async {
final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone);
await game.addFromBlueprint(sparkyFireZone);
await game.ready();
expect(
sparkyFireZone.descendants().whereType<SparkyBumper>().length,
game.descendants().whereType<SparkyBumper>().length,
equals(3),
);
},
@ -84,11 +106,11 @@ void main() {
setUp: (game, tester) async {
final ball = Ball(baseColor: const Color(0xFF00FFFF));
final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone);
await game.addFromBlueprint(sparkyFireZone);
await game.ensureAdd(ball);
game.addContactCallback(BallScorePointsCallback(game));
final bumpers = sparkyFireZone.descendants().whereType<ScorePoints>();
final bumpers = sparkyFireZone.components.whereType<ScorePoints>();
for (final bumper in bumpers) {
beginContact(game, bumper, ball);
@ -102,4 +124,40 @@ void main() {
);
});
});
group('SparkyTurboChargeSensorBallContactCallback', () {
flameTester.test('calls turboCharge', (game) async {
final callback = SparkyComputerSensorBallContactCallback();
final ball = MockControlledBall();
final controller = MockBallController();
when(() => ball.controller).thenReturn(controller);
when(() => ball.gameRef).thenReturn(game);
when(controller.turboCharge).thenAnswer((_) async {});
callback.begin(MockSparkyComputerSensor(), ball, MockContact());
verify(() => ball.controller.turboCharge()).called(1);
});
flameTester.test('plays SparkyAnimatronic', (game) async {
final callback = SparkyComputerSensorBallContactCallback();
final ball = MockControlledBall();
final controller = MockBallController();
when(() => ball.controller).thenReturn(controller);
when(() => ball.gameRef).thenReturn(game);
when(controller.turboCharge).thenAnswer((_) async {});
final sparkyFireZone = SparkyFireZone();
await game.addFromBlueprint(sparkyFireZone);
await game.ready();
final sparkyAnimatronic =
sparkyFireZone.components.whereType<SparkyAnimatronic>().single;
expect(sparkyAnimatronic.playing, isFalse);
callback.begin(MockSparkyComputerSensor(), ball, MockContact());
expect(sparkyAnimatronic.playing, isTrue);
});
});
}

@ -34,6 +34,7 @@ void main() {
Assets.images.sparky.bumper.b.inactive.keyName,
Assets.images.sparky.bumper.c.active.keyName,
Assets.images.sparky.bumper.c.inactive.keyName,
Assets.images.sparky.animatronic.keyName,
Assets.images.spaceship.ramp.boardOpening.keyName,
Assets.images.spaceship.ramp.railingForeground.keyName,
Assets.images.spaceship.ramp.railingBackground.keyName,
@ -49,6 +50,7 @@ void main() {
Assets.images.flipper.left.keyName,
Assets.images.flipper.right.keyName,
Assets.images.boundary.outer.keyName,
Assets.images.boundary.outerBottom.keyName,
Assets.images.boundary.bottom.keyName,
Assets.images.slingshot.upper.keyName,
Assets.images.slingshot.lower.keyName,
@ -92,14 +94,6 @@ void main() {
);
});
flameTester.test(
'one SparkyFireZone',
(game) async {
await game.ready();
expect(game.children.whereType<SparkyFireZone>().length, equals(1));
},
);
flameTester.test(
'one AlienZone',
(game) async {

@ -30,7 +30,7 @@ void main() {
rank: '1',
playerInitials: 'ABC',
score: 1500,
character: DashTheme().character,
character: DashTheme().leaderboardIcon,
);
test(

@ -121,7 +121,7 @@ void main() {
rank: '1',
playerInitials: 'ABC',
score: 10000,
character: DashTheme().character,
character: DashTheme().leaderboardIcon,
),
],
),

@ -19,18 +19,18 @@
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A Very Good new Flutter project.">
<meta name="description" content="Come play Pinball with your favorite Google Developer Mascots! Built with Flutter & Firebase for Google I/O 2022.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Pinball">
<meta name="apple-mobile-web-app-title" content="I/O Pinball Machine - Flutter">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png" />
<title>Pinball</title>
<title>I/O Pinball Machine - Flutter</title>
<link rel="manifest" href="manifest.json">
<script src="/__/firebase/8.9.1/firebase-app.js"></script>
<script src="/__/firebase/8.9.1/firebase-firestore.js"></script>

Loading…
Cancel
Save