diff --git a/lib/game/components/android_acres.dart b/lib/game/components/android_acres.dart index 4a235619..8b5fe718 100644 --- a/lib/game/components/android_acres.dart +++ b/lib/game/components/android_acres.dart @@ -18,12 +18,17 @@ class AndroidAcres extends Blueprint { children: [ ScoringBehavior(points: 20000), ], - )..initialPosition = Vector2(-32.52, -9.1), + )..initialPosition = Vector2(-25, 1.3), AndroidBumper.b( children: [ ScoringBehavior(points: 20000), ], - )..initialPosition = Vector2(-22.89, -17.35), + )..initialPosition = Vector2(-32.6, -9.2), + AndroidBumper.cow( + children: [ + ScoringBehavior(points: 20), + ], + )..initialPosition = Vector2(-20.5, -13.8), ], blueprints: [ SpaceshipRamp(), diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 14e3efc8..a8c3c82e 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -83,6 +83,8 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.androidBumper.a.dimmed.keyName), images.load(components.Assets.images.androidBumper.b.lit.keyName), images.load(components.Assets.images.androidBumper.b.dimmed.keyName), + images.load(components.Assets.images.androidBumper.cow.lit.keyName), + images.load(components.Assets.images.androidBumper.cow.dimmed.keyName), images.load(components.Assets.images.sparky.computer.top.keyName), images.load(components.Assets.images.sparky.computer.base.keyName), images.load(components.Assets.images.sparky.animatronic.keyName), diff --git a/packages/pinball_components/assets/images/android_bumper/cow/dimmed.png b/packages/pinball_components/assets/images/android_bumper/cow/dimmed.png new file mode 100644 index 00000000..6a8bb146 Binary files /dev/null and b/packages/pinball_components/assets/images/android_bumper/cow/dimmed.png differ diff --git a/packages/pinball_components/assets/images/android_bumper/cow/lit.png b/packages/pinball_components/assets/images/android_bumper/cow/lit.png new file mode 100644 index 00000000..4909708b Binary files /dev/null and b/packages/pinball_components/assets/images/android_bumper/cow/lit.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index 2778e4a7..be244edb 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -40,6 +40,8 @@ class $AssetsImagesAndroidBumperGen { const $AssetsImagesAndroidBumperAGen(); $AssetsImagesAndroidBumperBGen get b => const $AssetsImagesAndroidBumperBGen(); + $AssetsImagesAndroidBumperCowGen get cow => + const $AssetsImagesAndroidBumperCowGen(); } class $AssetsImagesBackboardGen { @@ -298,6 +300,18 @@ class $AssetsImagesAndroidBumperBGen { const AssetGenImage('assets/images/android_bumper/b/lit.png'); } +class $AssetsImagesAndroidBumperCowGen { + const $AssetsImagesAndroidBumperCowGen(); + + /// File path: assets/images/android_bumper/cow/dimmed.png + AssetGenImage get dimmed => + const AssetGenImage('assets/images/android_bumper/cow/dimmed.png'); + + /// File path: assets/images/android_bumper/cow/lit.png + AssetGenImage get lit => + const AssetGenImage('assets/images/android_bumper/cow/lit.png'); +} + class $AssetsImagesDashBumperGen { const $AssetsImagesDashBumperGen(); diff --git a/packages/pinball_components/lib/src/components/android_bumper/android_bumper.dart b/packages/pinball_components/lib/src/components/android_bumper/android_bumper.dart index ad954975..4cefc28d 100644 --- a/packages/pinball_components/lib/src/components/android_bumper/android_bumper.dart +++ b/packages/pinball_components/lib/src/components/android_bumper/android_bumper.dart @@ -19,6 +19,7 @@ class AndroidBumper extends BodyComponent with InitialPosition { required double minorRadius, required String litAssetPath, required String dimmedAssetPath, + required Vector2 spritePosition, Iterable? children, required this.bloc, }) : _majorRadius = majorRadius, @@ -32,6 +33,7 @@ class AndroidBumper extends BodyComponent with InitialPosition { _AndroidBumperSpriteGroupComponent( dimmedAssetPath: dimmedAssetPath, litAssetPath: litAssetPath, + position: spritePosition, state: bloc.state, ), ...?children, @@ -46,6 +48,7 @@ class AndroidBumper extends BodyComponent with InitialPosition { minorRadius: 2.97, litAssetPath: Assets.images.androidBumper.a.lit.keyName, dimmedAssetPath: Assets.images.androidBumper.a.dimmed.keyName, + spritePosition: Vector2(0, -0.1), bloc: AndroidBumperCubit(), children: children, ); @@ -58,6 +61,20 @@ class AndroidBumper extends BodyComponent with InitialPosition { minorRadius: 2.79, litAssetPath: Assets.images.androidBumper.b.lit.keyName, dimmedAssetPath: Assets.images.androidBumper.b.dimmed.keyName, + spritePosition: Vector2(0, -0.1), + bloc: AndroidBumperCubit(), + children: children, + ); + + /// {@macro android_bumper} + AndroidBumper.cow({ + Iterable? children, + }) : this._( + majorRadius: 3.4, + minorRadius: 2.9, + litAssetPath: Assets.images.androidBumper.cow.lit.keyName, + dimmedAssetPath: Assets.images.androidBumper.cow.dimmed.keyName, + spritePosition: Vector2(0, -0.68), bloc: AndroidBumperCubit(), children: children, ); @@ -113,12 +130,13 @@ class _AndroidBumperSpriteGroupComponent _AndroidBumperSpriteGroupComponent({ required String litAssetPath, required String dimmedAssetPath, + required Vector2 position, required AndroidBumperState state, }) : _litAssetPath = litAssetPath, _dimmedAssetPath = dimmedAssetPath, super( anchor: Anchor.center, - position: Vector2(0, -0.1), + position: position, current: state, ); diff --git a/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_cubit.dart b/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_cubit.dart index 3d3fd4b1..3e75f890 100644 --- a/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_cubit.dart +++ b/packages/pinball_components/lib/src/components/android_bumper/cubit/android_bumper_cubit.dart @@ -5,7 +5,7 @@ import 'package:bloc/bloc.dart'; part 'android_bumper_state.dart'; class AndroidBumperCubit extends Cubit { - AndroidBumperCubit() : super(AndroidBumperState.dimmed); + AndroidBumperCubit() : super(AndroidBumperState.lit); void onBallContacted() { emit(AndroidBumperState.dimmed); diff --git a/packages/pinball_components/lib/src/components/bumping_behavior.dart b/packages/pinball_components/lib/src/components/bumping_behavior.dart index 654f96b4..af0d07c3 100644 --- a/packages/pinball_components/lib/src/components/bumping_behavior.dart +++ b/packages/pinball_components/lib/src/components/bumping_behavior.dart @@ -1,4 +1,5 @@ import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:flutter/material.dart'; import 'package:pinball_flame/pinball_flame.dart'; /// {@template bumping_behavior} @@ -11,15 +12,22 @@ class BumpingBehavior extends ContactBehavior { /// Determines how strong the bump is. final double _strength; + /// This is used to recoginze the current state of a contact manifold in world + /// coordinates. + @visibleForTesting + final WorldManifold worldManifold = WorldManifold(); + @override void postSolve(Object other, Contact contact, ContactImpulse impulse) { super.postSolve(other, contact, impulse); if (other is! BodyComponent) return; + contact.getWorldManifold(worldManifold); other.body.applyLinearImpulse( - contact.manifold.localPoint - ..normalize() - ..multiply(Vector2.all(other.body.mass * _strength)), + worldManifold.normal + ..multiply( + Vector2.all(other.body.mass * _strength), + ), ); } } diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 9a112dfa..37f9bd63 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -66,6 +66,7 @@ flutter: - assets/images/slingshot/ - assets/images/android_bumper/a/ - assets/images/android_bumper/b/ + - assets/images/android_bumper/cow/ - assets/images/sparky/ - assets/images/sparky/computer/ - assets/images/sparky/bumper/a/ diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_cow_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_cow_game.dart new file mode 100644 index 00000000..3b0aa828 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_cow_game.dart @@ -0,0 +1,33 @@ +import 'dart:async'; + +import 'package:flame/extensions.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/stories/ball/basic_ball_game.dart'; + +class AndroidBumperCowGame extends BallGame { + AndroidBumperCowGame() + : super( + imagesFileNames: [ + Assets.images.androidBumper.cow.lit.keyName, + Assets.images.androidBumper.cow.dimmed.keyName, + ], + ); + + static const description = ''' + Shows how a AndroidBumper.cow is rendered. + + - Activate the "trace" parameter to overlay the body. +'''; + + @override + Future onLoad() async { + await super.onLoad(); + + camera.followVector2(Vector2.zero()); + await add( + AndroidBumper.cow()..priority = 1, + ); + + await traceAllBodies(); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/stories.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/stories.dart index 92ddd5d5..d8f84671 100644 --- a/packages/pinball_components/sandbox/lib/stories/android_acres/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/stories.dart @@ -2,6 +2,7 @@ import 'package:dashbook/dashbook.dart'; import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/android_acres/android_bumper_a_game.dart'; import 'package:sandbox/stories/android_acres/android_bumper_b_game.dart'; +import 'package:sandbox/stories/android_acres/android_bumper_cow_game.dart'; import 'package:sandbox/stories/android_acres/spaceship_game.dart'; import 'package:sandbox/stories/android_acres/spaceship_rail_game.dart'; import 'package:sandbox/stories/android_acres/spaceship_ramp_game.dart'; @@ -18,6 +19,11 @@ void addAndroidAcresStories(Dashbook dashbook) { description: AndroidBumperBGame.description, gameBuilder: (_) => AndroidBumperBGame(), ) + ..addGame( + title: 'Android Bumper Cow', + description: AndroidBumperCowGame.description, + gameBuilder: (_) => AndroidBumperCowGame(), + ) ..addGame( title: 'Spaceship', description: SpaceshipGame.description, diff --git a/packages/pinball_components/test/src/components/android_bumper/android_bumper_test.dart b/packages/pinball_components/test/src/components/android_bumper/android_bumper_test.dart index abc51a28..831330c0 100644 --- a/packages/pinball_components/test/src/components/android_bumper/android_bumper_test.dart +++ b/packages/pinball_components/test/src/components/android_bumper/android_bumper_test.dart @@ -17,6 +17,8 @@ void main() { Assets.images.androidBumper.a.dimmed.keyName, Assets.images.androidBumper.b.lit.keyName, Assets.images.androidBumper.b.dimmed.keyName, + Assets.images.androidBumper.cow.lit.keyName, + Assets.images.androidBumper.cow.dimmed.keyName, ]; final flameTester = FlameTester(() => TestGame(assets)); @@ -33,6 +35,12 @@ void main() { expect(game.contains(androidBumper), isTrue); }); + flameTester.test('"cow" loads correctly', (game) async { + final androidBumper = AndroidBumper.cow(); + await game.ensureAdd(androidBumper); + expect(game.contains(androidBumper), isTrue); + }); + // TODO(alestiago): Consider refactoring once the following is merged: // https://github.com/flame-engine/flame/pull/1538 // ignore: public_member_api_docs diff --git a/packages/pinball_components/test/src/components/bumping_behavior_test.dart b/packages/pinball_components/test/src/components/bumping_behavior_test.dart index d346a0ae..dd0493f7 100644 --- a/packages/pinball_components/test/src/components/bumping_behavior_test.dart +++ b/packages/pinball_components/test/src/components/bumping_behavior_test.dart @@ -7,22 +7,16 @@ import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/src/components/bumping_behavior.dart'; import '../../helpers/helpers.dart'; -import 'layer_test.dart'; -class MockContactImpulse extends Mock implements ContactImpulse {} +class _MockContact extends Mock implements Contact {} -class MockManifold extends Mock implements Manifold {} +class _MockContactImpulse extends Mock implements ContactImpulse {} -class TestHeavyBodyComponent extends BodyComponent { +class _TestBodyComponent extends BodyComponent { @override - Body createBody() { - final shape = CircleShape(); - return world.createBody( - BodyDef( - type: BodyType.dynamic, - ), - )..createFixtureFromShape(shape, 20); - } + Body createBody() => world.createBody( + BodyDef(type: BodyType.dynamic), + )..createFixtureFromShape(CircleShape(), 1); } void main() { @@ -32,7 +26,7 @@ void main() { group('BumpingBehavior', () { flameTester.test('can be added', (game) async { final behavior = BumpingBehavior(strength: 0); - final component = TestBodyComponent(); + final component = _TestBodyComponent(); await component.add(behavior); await game.ensureAdd(component); }); @@ -40,16 +34,18 @@ void main() { flameTester.testGameWidget( 'the bump is greater when the strengh is greater', setUp: (game, tester) async { - final component1 = TestBodyComponent(); - final behavior1 = BumpingBehavior(strength: 1); + final component1 = _TestBodyComponent(); + final behavior1 = BumpingBehavior(strength: 1) + ..worldManifold.normal.setFrom(Vector2.all(1)); await component1.add(behavior1); - final component2 = TestBodyComponent(); - final behavior2 = BumpingBehavior(strength: 2); + final component2 = _TestBodyComponent(); + final behavior2 = BumpingBehavior(strength: 2) + ..worldManifold.normal.setFrom(Vector2.all(1)); await component2.add(behavior2); - final dummy1 = TestHeavyBodyComponent(); - final dummy2 = TestHeavyBodyComponent(); + final dummy1 = _TestBodyComponent(); + final dummy2 = _TestBodyComponent(); await game.ensureAddAll([ component1, @@ -58,14 +54,8 @@ void main() { dummy2, ]); - expect(dummy1.body.inverseMass, greaterThan(0)); - expect(dummy2.body.inverseMass, greaterThan(0)); - - final contact = MockContact(); - final manifold = MockManifold(); - final contactImpulse = MockContactImpulse(); - when(() => manifold.localPoint).thenReturn(Vector2.all(1)); - when(() => contact.manifold).thenReturn(manifold); + final contact = _MockContact(); + final contactImpulse = _MockContactImpulse(); behavior1.postSolve(dummy1, contact, contactImpulse); behavior2.postSolve(dummy2, contact, contactImpulse); diff --git a/test/game/components/android_acres_test.dart b/test/game/components/android_acres_test.dart index 419524c6..01ab5d7b 100644 --- a/test/game/components/android_acres_test.dart +++ b/test/game/components/android_acres_test.dart @@ -27,6 +27,8 @@ void main() { Assets.images.androidBumper.a.dimmed.keyName, Assets.images.androidBumper.b.lit.keyName, Assets.images.androidBumper.b.dimmed.keyName, + Assets.images.androidBumper.cow.lit.keyName, + Assets.images.androidBumper.cow.dimmed.keyName, ]; final flameTester = FlameTester( () => EmptyPinballTestGame(assets: assets), @@ -73,7 +75,7 @@ void main() { ); flameTester.test( - 'two AndroidBumper', + 'three AndroidBumper', (game) async { final androidZone = AndroidAcres(); await game.addFromBlueprint(androidZone); @@ -81,7 +83,7 @@ void main() { expect( game.descendants().whereType().length, - equals(2), + equals(3), ); }, ); diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index 30396981..a7240286 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -19,6 +19,8 @@ void main() { Assets.images.androidBumper.a.dimmed.keyName, Assets.images.androidBumper.b.lit.keyName, Assets.images.androidBumper.b.dimmed.keyName, + Assets.images.androidBumper.cow.lit.keyName, + Assets.images.androidBumper.cow.dimmed.keyName, Assets.images.backboard.backboardScores.keyName, Assets.images.backboard.backboardGameOver.keyName, Assets.images.backboard.display.keyName,