Merge branch 'main' into feat/add-debug-information

pull/222/head
Jochum van der Ploeg 3 years ago committed by GitHub
commit a608f4c4d4
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_ball.dart';
export 'controlled_flipper.dart'; export 'controlled_flipper.dart';
export 'controlled_plunger.dart'; export 'controlled_plunger.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 'google_word.dart'; export 'google_word.dart';

@ -62,10 +62,13 @@ class BallController extends ComponentController<Ball>
Future<void> turboCharge() async { Future<void> turboCharge() async {
gameRef.read<GameBloc>().add(const SparkyTurboChargeActivated()); gameRef.read<GameBloc>().add(const SparkyTurboChargeActivated());
// TODO(allisonryan0002): adjust delay to match animation duration once
// given animations.
component.stop(); 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(); component.resume();
await component.boost(Vector2(40, 110)); 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 // ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template sparky_fire_zone} /// {@template sparky_fire_zone}
/// Area positioned at the top left of the [Board] where the [Ball] /// 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. /// When a [Ball] hits [SparkyBumper]s, the bumper animates.
/// {@endtemplate} /// {@endtemplate}
class SparkyFireZone extends Component with HasGameRef<PinballGame> { class SparkyFireZone extends Blueprint {
/// {@macro sparky_fire_zone} /// {@macro sparky_fire_zone}
SparkyFireZone(); SparkyFireZone()
: super(
@override components: [
Future<void> onLoad() async { _SparkyBumper.a()..initialPosition = Vector2(-22.9, -41.65),
await super.onLoad(); _SparkyBumper.b()..initialPosition = Vector2(-21.25, -57.9),
_SparkyBumper.c()..initialPosition = Vector2(-3.3, -52.55),
gameRef.addContactCallback(SparkyBumperBallContactCallback()); SparkyComputerSensor()..initialPosition = Vector2(-13, -49.8),
SparkyAnimatronic()..position = Vector2(-13.8, -58.2),
final lowerLeftBumper = _SparkyBumper.a() ],
..initialPosition = Vector2(-22.9, -41.65); blueprints: [
final upperLeftBumper = _SparkyBumper.b() SparkyComputer(),
..initialPosition = Vector2(-21.25, -57.9); ],
final rightBumper = _SparkyBumper.c() );
..initialPosition = Vector2(-3.3, -52.55);
await addAll([
lowerLeftBumper,
upperLeftBumper,
rightBumper,
]);
}
} }
// TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D // TODO(alestiago): Revisit ScorePoints logic once the FlameForge2D
@ -48,6 +40,14 @@ class _SparkyBumper extends SparkyBumper with ScorePoints {
@override @override
int get points => 20; 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]. /// Listens when a [Ball] bounces bounces against a [SparkyBumper].
@ -63,3 +63,48 @@ class SparkyBumperBallContactCallback
sparkyBumper.animate(); 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,6 +43,8 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.dash.bumper.b.inactive.keyName), 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.active.keyName),
images.load(components.Assets.images.dash.bumper.main.inactive.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.bottom.keyName),
images.load(components.Assets.images.boundary.outer.keyName), images.load(components.Assets.images.boundary.outer.keyName),
images.load(components.Assets.images.spaceship.saucer.keyName), images.load(components.Assets.images.spaceship.saucer.keyName),
@ -80,12 +82,11 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.alienBumper.b.inactive.keyName), images.load(components.Assets.images.alienBumper.b.inactive.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.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.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.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.active.keyName),
images.load(components.Assets.images.sparky.bumper.b.inactive.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.active.keyName),

@ -50,13 +50,13 @@ class PinballGame extends Forge2DGame
// TODO(allisonryan0002): banish Wall and Board classes in later PR. // TODO(allisonryan0002): banish Wall and Board classes in later PR.
await add(BottomWall()); await add(BottomWall());
unawaited(addFromBlueprint(Boundaries())); unawaited(addFromBlueprint(Boundaries()));
unawaited(addFromBlueprint(ControlledSparkyComputer())); unawaited(addFromBlueprint(LaunchRamp()));
final launcher = Launcher(); final launcher = Launcher();
unawaited(addFromBlueprint(launcher)); unawaited(addFromBlueprint(launcher));
unawaited(add(Board())); unawaited(add(Board()));
unawaited(add(AlienZone())); unawaited(add(AlienZone()));
unawaited(add(SparkyFireZone())); await addFromBlueprint(SparkyFireZone());
unawaited(addFromBlueprint(Slingshots())); unawaited(addFromBlueprint(Slingshots()));
unawaited(addFromBlueprint(DinoWalls())); unawaited(addFromBlueprint(DinoWalls()));
unawaited(_addBonusWord()); unawaited(_addBonusWord());

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

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

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

@ -257,6 +257,8 @@ class $AssetsImagesSpaceshipGen {
class $AssetsImagesSparkyGen { class $AssetsImagesSparkyGen {
const $AssetsImagesSparkyGen(); const $AssetsImagesSparkyGen();
AssetGenImage get animatronic =>
const AssetGenImage('assets/images/sparky/animatronic.png');
$AssetsImagesSparkyBumperGen get bumper => $AssetsImagesSparkyBumperGen get bumper =>
const $AssetsImagesSparkyBumperGen(); const $AssetsImagesSparkyBumperGen();
$AssetsImagesSparkyComputerGen get computer => $AssetsImagesSparkyComputerGen get computer =>

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

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

@ -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} /// {@template sparky_computer}
/// A computer owned by Sparky. /// A computer owned by Sparky.
///
/// Register a [ContactCallback] for [SparkyComputerSensor] to listen when
/// something enters the [SparkyComputer].
/// {@endtemplate} /// {@endtemplate}
class SparkyComputer extends Blueprint { class SparkyComputer extends Blueprint {
/// {@macro sparky_computer} /// {@macro sparky_computer}
@ -18,7 +15,6 @@ class SparkyComputer extends Blueprint {
components: [ components: [
_ComputerBase(), _ComputerBase(),
_ComputerTopSpriteComponent(), _ComputerTopSpriteComponent(),
SparkyComputerSensor(),
], ],
); );
} }
@ -104,24 +100,3 @@ class _ComputerTopSpriteComponent extends SpriteComponent with HasGameRef {
size = sprite.originalSize / 10; 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/slingshot/
- assets/images/alien_bumper/a/ - assets/images/alien_bumper/a/
- assets/images/alien_bumper/b/ - assets/images/alien_bumper/b/
- assets/images/sparky/
- assets/images/sparky/computer/ - assets/images/sparky/computer/
- assets/images/sparky/bumper/a/ - assets/images/sparky/bumper/a/
- assets/images/sparky/bumper/b/ - assets/images/sparky/bumper/b/

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

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

@ -14,9 +14,6 @@ class AndroidTheme extends CharacterTheme {
@override @override
Color get ballColor => Colors.green; Color get ballColor => Colors.green;
@override
AssetGenImage get character => Assets.images.android.character;
@override @override
AssetGenImage get background => Assets.images.android.background; AssetGenImage get background => Assets.images.android.background;
@ -25,4 +22,7 @@ class AndroidTheme extends CharacterTheme {
@override @override
AssetGenImage get leaderboardIcon => Assets.images.android.leaderboardIcon; 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. /// Ball color for this theme.
Color get ballColor; Color get ballColor;
/// Asset for the theme character.
AssetGenImage get character;
/// Asset for the background. /// Asset for the background.
AssetGenImage get background; AssetGenImage get background;
@ -30,13 +27,16 @@ abstract class CharacterTheme extends Equatable {
/// Icon asset for the leaderboard. /// Icon asset for the leaderboard.
AssetGenImage get leaderboardIcon; AssetGenImage get leaderboardIcon;
/// Asset for the the idle character animation.
AssetGenImage get animation;
@override @override
List<Object?> get props => [ List<Object?> get props => [
name, name,
ballColor, ballColor,
character,
background, background,
icon, icon,
leaderboardIcon, leaderboardIcon,
animation,
]; ];
} }

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

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

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

@ -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:mocktail/mocktail.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';
import 'package:pinball_flame/pinball_flame.dart';
import '../../helpers/helpers.dart'; import '../../helpers/helpers.dart';
@ -20,29 +21,50 @@ void main() {
Assets.images.sparky.bumper.b.inactive.keyName, Assets.images.sparky.bumper.b.inactive.keyName,
Assets.images.sparky.bumper.c.active.keyName, Assets.images.sparky.bumper.c.active.keyName,
Assets.images.sparky.bumper.c.inactive.keyName, Assets.images.sparky.bumper.c.inactive.keyName,
Assets.images.sparky.animatronic.keyName,
]; ];
final flameTester = FlameTester(() => EmptyPinballTestGame(assets)); final flameTester = FlameTester(() => EmptyPinballTestGame(assets));
group('SparkyFireZone', () { group('SparkyFireZone', () {
flameTester.test('loads correctly', (game) async {
final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone);
});
group('loads', () {
flameTester.test( flameTester.test(
'loads correctly', 'a SparkyComputer',
(game) async {
expect(
SparkyFireZone().blueprints.whereType<SparkyComputer>().single,
isNotNull,
);
},
);
flameTester.test(
'a SparkyAnimatronic',
(game) async { (game) async {
final sparkyFireZone = SparkyFireZone(); 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( flameTester.test(
'three SparkyBumper', 'three SparkyBumper',
(game) async { (game) async {
final sparkyFireZone = SparkyFireZone(); final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone); await game.addFromBlueprint(sparkyFireZone);
await game.ready();
expect( expect(
sparkyFireZone.descendants().whereType<SparkyBumper>().length, game.descendants().whereType<SparkyBumper>().length,
equals(3), equals(3),
); );
}, },
@ -84,11 +106,11 @@ void main() {
setUp: (game, tester) async { setUp: (game, tester) async {
final ball = Ball(baseColor: const Color(0xFF00FFFF)); final ball = Ball(baseColor: const Color(0xFF00FFFF));
final sparkyFireZone = SparkyFireZone(); final sparkyFireZone = SparkyFireZone();
await game.ensureAdd(sparkyFireZone); await game.addFromBlueprint(sparkyFireZone);
await game.ensureAdd(ball); await game.ensureAdd(ball);
game.addContactCallback(BallScorePointsCallback(game)); game.addContactCallback(BallScorePointsCallback(game));
final bumpers = sparkyFireZone.descendants().whereType<ScorePoints>(); final bumpers = sparkyFireZone.components.whereType<ScorePoints>();
for (final bumper in bumpers) { for (final bumper in bumpers) {
beginContact(game, bumper, ball); 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.b.inactive.keyName,
Assets.images.sparky.bumper.c.active.keyName, Assets.images.sparky.bumper.c.active.keyName,
Assets.images.sparky.bumper.c.inactive.keyName, Assets.images.sparky.bumper.c.inactive.keyName,
Assets.images.sparky.animatronic.keyName,
Assets.images.spaceship.ramp.boardOpening.keyName, Assets.images.spaceship.ramp.boardOpening.keyName,
Assets.images.spaceship.ramp.railingForeground.keyName, Assets.images.spaceship.ramp.railingForeground.keyName,
Assets.images.spaceship.ramp.railingBackground.keyName, Assets.images.spaceship.ramp.railingBackground.keyName,
@ -92,14 +93,6 @@ void main() {
); );
}); });
flameTester.test(
'one SparkyFireZone',
(game) async {
await game.ready();
expect(game.children.whereType<SparkyFireZone>().length, equals(1));
},
);
flameTester.test( flameTester.test(
'one AlienZone', 'one AlienZone',
(game) async { (game) async {

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

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

@ -19,18 +19,18 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <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 --> <!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes"> <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-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"> <link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png" /> <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"> <link rel="manifest" href="manifest.json">
<script src="/__/firebase/8.9.1/firebase-app.js"></script> <script src="/__/firebase/8.9.1/firebase-app.js"></script>
<script src="/__/firebase/8.9.1/firebase-firestore.js"></script> <script src="/__/firebase/8.9.1/firebase-firestore.js"></script>

Loading…
Cancel
Save