From de3569c479dc43469b048cb3bde7193928c99a60 Mon Sep 17 00:00:00 2001 From: Rui Miguel Alonso Date: Wed, 4 May 2022 23:04:16 +0200 Subject: [PATCH] feat: include `Ball` final assets (#279) * refactor: load ball assets from cache * feat: new ball assets * refactor: ball loads new sprites * refactor: modified ball assets for sandbox * refactor: removing baseColor from ball * refactor: moved ball assets to pinball_theme * refactor: removed all baseColor for ball * refactor: removed unused imports * test: golden tests for ball * test: added missed ball assets to tests * chore: reorder imports * refactor: removed all default ball, use dash * test: fixed test for ball changes * test: adde ball assets to forest tests * fix: fixed tests for ball * refactor: removed baseColor from ball tests * chore: unused imports * test: removed golden tests for balls * refactor: removed unused assets at test and renamed spriteAsset for ball * refactor: spriteAsset changed for ball sprite Co-authored-by: Tom Arra --- lib/game/components/controlled_ball.dart | 7 ++- lib/game/game_assets.dart | 5 +- .../assets/images/ball/ball.png | Bin 3190 -> 0 bytes .../lib/src/components/ball/ball.dart | 28 +++++++----- .../sandbox/lib/common/games.dart | 8 ++++ .../android_acres/android_bumper_a_game.dart | 1 - .../android_acres/android_bumper_b_game.dart | 1 - .../android_acres/spaceship_rail_game.dart | 2 - .../android_acres/spaceship_ramp_game.dart | 1 - .../lib/stories/ball/ball_booster_game.dart | 15 +++++- .../lib/stories/ball/basic_ball_game.dart | 22 +++++++-- .../sandbox/lib/stories/ball/stories.dart | 9 ++-- .../google_word/google_letter_game.dart | 4 -- .../stories/launch_ramp/launch_ramp_game.dart | 2 - .../lib/stories/plunger/plunger_game.dart | 2 - .../test/src/components/ball/ball_test.dart | 43 ++++++++++-------- .../ball_gravitating_behavior_test.dart | 14 ++---- .../behaviors/ball_scaling_behavior_test.dart | 20 +++----- .../ball_turbo_charging_behavior_test.dart | 17 +++---- .../chrome_dino_chomping_behavior_test.dart | 9 ++-- .../chrome_dino_spitting_behavior_test.dart | 11 +++-- .../cubit/chrome_dino_cubit_test.dart | 3 +- .../cubit/chrome_dino_state_test.dart | 3 +- .../test/src/components/flipper_test.dart | 5 +- .../src/components/golden/ball/android.png | Bin 0 -> 28038 bytes .../test/src/components/golden/ball/dash.png | Bin 0 -> 28052 bytes .../test/src/components/golden/ball/dino.png | Bin 0 -> 28658 bytes .../src/components/golden/ball/sparky.png | Bin 0 -> 27904 bytes .../assets/images/android/ball.png | Bin 0 -> 6544 bytes .../pinball_theme/assets/images/dash/ball.png | Bin 0 -> 6561 bytes .../pinball_theme/assets/images/dino/ball.png | Bin 0 -> 6973 bytes .../assets/images/sparky/ball.png | Bin 0 -> 6449 bytes .../lib/src/generated/assets.gen.dart | 22 ++++----- .../lib/src/themes/android_theme.dart | 3 +- .../lib/src/themes/character_theme.dart | 7 ++- .../lib/src/themes/dash_theme.dart | 3 +- .../lib/src/themes/dino_theme.dart | 3 +- .../lib/src/themes/sparky_theme.dart | 3 +- .../game/components/controlled_ball_test.dart | 12 +++-- .../flutter_forest_bonus_behavior_test.dart | 9 +++- test/game/pinball_game_test.dart | 6 ++- 41 files changed, 167 insertions(+), 133 deletions(-) delete mode 100644 packages/pinball_components/assets/images/ball/ball.png create mode 100644 packages/pinball_components/test/src/components/golden/ball/android.png create mode 100644 packages/pinball_components/test/src/components/golden/ball/dash.png create mode 100644 packages/pinball_components/test/src/components/golden/ball/dino.png create mode 100644 packages/pinball_components/test/src/components/golden/ball/sparky.png create mode 100644 packages/pinball_theme/assets/images/android/ball.png create mode 100644 packages/pinball_theme/assets/images/dash/ball.png create mode 100644 packages/pinball_theme/assets/images/dino/ball.png create mode 100644 packages/pinball_theme/assets/images/sparky/ball.png diff --git a/lib/game/components/controlled_ball.dart b/lib/game/components/controlled_ball.dart index 9dc81135..132639d4 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -1,7 +1,6 @@ // ignore_for_file: avoid_renaming_method_parameters import 'package:flame/components.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'; @@ -17,7 +16,7 @@ class ControlledBall extends Ball with Controls { /// A [Ball] that launches from the [Plunger]. ControlledBall.launch({ required CharacterTheme characterTheme, - }) : super(baseColor: characterTheme.ballColor) { + }) : super(assetPath: characterTheme.ball.keyName) { controller = BallController(this); layer = Layer.launcher; zIndex = ZIndexes.ballOnLaunchRamp; @@ -28,13 +27,13 @@ class ControlledBall extends Ball with Controls { /// {@endtemplate} ControlledBall.bonus({ required CharacterTheme characterTheme, - }) : super(baseColor: characterTheme.ballColor) { + }) : super(assetPath: characterTheme.ball.keyName) { controller = BallController(this); zIndex = ZIndexes.ballOnBoard; } /// [Ball] used in [DebugPinballGame]. - ControlledBall.debug() : super(baseColor: const Color(0xFFFF0000)) { + ControlledBall.debug() : super() { controller = BallController(this); zIndex = ZIndexes.ballOnBoard; } diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 7ab86553..d066ce0d 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -13,7 +13,6 @@ extension PinballGameAssetsX on PinballGame { return [ images.load(components.Assets.images.boardBackground.keyName), - images.load(components.Assets.images.ball.ball.keyName), images.load(components.Assets.images.ball.flameEffect.keyName), images.load(components.Assets.images.signpost.inactive.keyName), images.load(components.Assets.images.signpost.active1.keyName), @@ -136,6 +135,10 @@ extension PinballGameAssetsX on PinballGame { images.load(sparkyTheme.leaderboardIcon.keyName), images.load(androidTheme.leaderboardIcon.keyName), images.load(dinoTheme.leaderboardIcon.keyName), + images.load(androidTheme.ball.keyName), + images.load(dashTheme.ball.keyName), + images.load(dinoTheme.ball.keyName), + images.load(sparkyTheme.ball.keyName), ]; } } diff --git a/packages/pinball_components/assets/images/ball/ball.png b/packages/pinball_components/assets/images/ball/ball.png deleted file mode 100644 index 43332c9aba015855ea7d77252cd9b3c6ca339e38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3190 zcmW+(30P9u`^KfjG^KQkCO5Ja3b)b`70pmeaY^})8RR;Iism+GiF-Ip=)meZTiR-@WGNdur#7A9sL2 zpq-u`?s#Cd`rg3Xfu3r9>oG8BCU_92Adr^c_of13l|exuZMdiViJzlB%@Dov&l8AS z{SGUe`KPeh{~jK0s5xB!n2D_Eh#_+K4mmoSy>^7FO! zem*bfAU3VqEt4@UI>bdASVyT7^r8p-uPxR zdudCl*c$X1Q9k{xblHs#0nzcV;hgQHH{peM%H$ZZzXzfXk2}oK2Q6i2oCd@4!duII zW%w>>sP6LwJ8+KH2}sZ)I=^TNTf7o&i6#m2>Ly%ZQ{ZOkBrRifRR3&>9)MB@6#bMc)^ zMIm)7V_1Kbe9a)yz_gfDNhv@I!=T%tBtP5<#5NoPDa;u|B^YQh$uO{LYpvFE>o7?( zo6+m!^Bjcymux3^>Om z8^42AG4`9KN-q=)v(|p-0wHNP$C`pwlN=2jC9x?Lf@<+()<=GZq6IMJ+F9Lu(O(;M zWVBBsOM{`x*G2p7`MzL+VbRGX5=rghJ8(e21NaVLGSbS5WDRx2X^L%k<-yXWSI1D+ z+G?Qac`|{G^zyF$KX-rieg~wG&7TVm3J%`1fw`mac>i<-@`aik-$XnT-?h0s7u@#i zmr^0`Ldi+fU*|qK7YaycnOCf}V=T`xw~6EF;=Wfoebi zOholI)AK;=QddbdsP1TQUw>F3b9g>FAKXv-_GZLFxFc@3ed%1?vxV1IWWxB@xj7P* zsz+dU%h7(g2aFsF3vjh|x+ghU{RBuPk1lp9R+BV`v&`U}`-~o2AHWG7GA2=ws0~vG zAPk4je*XM92KPr)eEeK6hmjIJPlq&r2YqR2sqPESs8~Sy*p+4MdGk*m5}5nye9pTl zI#TEgR)MJ())N^Fyv3s40bjAcc=G!LQ0}=KRjh_X@Q`YtRlaWTZD*)Z^~H zM2_0_r#1}7g%UzTm4{^;LHgK~)p^*soHo;R&=ZKfN(Rc&5n~{@?OW|3)(1Gr3kSAB z9MXQCO<{pR{SG$|b|;{KD1^P{sl(J6cz|d>Tw+2(wh8#{+)B9Q|D$DVJq5i5_q_Qo zGRNLW91869-UJ-tb(5}r)y=@bAe*WqNOT3C(8nh0Xk>G_Pg`29qKW~GUi0V>%&A>W za`#GxAI`?8}0*q0Mytg|YV8KNHKmevHz4#^88OruqFJ54;J3R5p0 zE8BB4rioYw!ThT^8ev=tpbhRS@zz_-V~x2O$=+F{Y6Tnd2I`v>X!BB0xQtkd0hb^GgMoihl`_m2^W zg*?;Y1LCo#KR0~%+Qq=*ul6;^0@@YwrgrIo9LseK1b?L6E)B1p9?*HW?Qc2y-{#;J zvA}mv!-os!M6HV`gI|BTJFJIctiPzDu-``m>iM^Mr;xYUn5(td@}JGM>nsXuBFWK& z9_Dh7WF#7;8MP0`8IpHaP%8`#4TqgnjIvoG*}a5*S)boJza*qC@KRcHtQ%NG$a_1z z6mlCtIH@BTU=ilt`4b^12t8_@@rCA6#Gbe%7|KNomlx2+_SKn^=6BIeQ~Lr=x#O<+ zR_C6nS^C%qg{eO|qm|nYqC?(99Q%(}oY|H2In$?KkofEt`9n8pq=sAYx9zY)`6WZa zi53aAMdIv#yMXOlH<&ATDsAej5`9r{@ROS!4TGFr$?_L-V1nO&jA9xW#8 zMy!gVeHbvmqeEfuub;8;LZyuA(v_v$GOlpCH2cH^Tx(nXFruN7;rQ;km4yXNByYcS zq(GIzVsp7elJ=3d+UwTZ(_GrehHRTWq|o=mkHts$tiA~2?}bA>fmL-c$+2A2R-1MW&C_#b%}h;B*tTs|QYLTjNbbtq6Hshb4;^TslHKP~w(acc(CU`$eO*g)QO^Z)2OA%TwlC9h$ z2mY~umcr;aE_Q61k>13m28%hG?8y&iZ^uU$BON#Gx+DfKI1IROVSaYFqwAP~j&j*k zk-CgHcBq6s;l|O|%^7hdy%BFv{`OG2u+XWAS98AZ(w9FmhynjzHJLynjom$W?p&4` zJ!RSA+qazsA1*AHBZU{!rOI%zty8tJhplb`@sZDbUpH}3wY+>$Rz|fc_nk{$9lhFB z-`Tk-t8NfBxCCY6xqyU>vHpvE87I9-=l+pG1tMRiYM%sJ|^XCI|fr9jtZ zb>xVKhK90|kveK_nhfC~B`%JR>wy@LDAqU@R9q3o8u66}=UzAywsWf$bqy&z`Q5|R zmD%CCmx<}Re+tvglB0WcB{RzoDXV$KpUX;tDz-by zc3m-=uv&WS)qheE?yB_q)xYTY=#JVn&fxUsVoy&`X}Ywl7b=I6wsROZy-~S`@rXl} zjNc&#u8pnsC!jV4fSbGnDKzpxT%Qw;rD4Fwv>(b){XYnKOann%m}I$N47Jxs{PpL6 ztgSrTMpElRfAO Q;70=FiS>1_#f0Vj56Hwch5!Hn diff --git a/packages/pinball_components/lib/src/components/ball/ball.dart b/packages/pinball_components/lib/src/components/ball/ball.dart index 9234e69c..e8cea997 100644 --- a/packages/pinball_components/lib/src/components/ball/ball.dart +++ b/packages/pinball_components/lib/src/components/ball/ball.dart @@ -2,9 +2,10 @@ import 'dart:async'; import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; export 'behaviors/behaviors.dart'; @@ -14,11 +15,11 @@ export 'behaviors/behaviors.dart'; class Ball extends BodyComponent with Layered, InitialPosition, ZIndex { /// {@macro ball} Ball({ - required this.baseColor, + String? assetPath, }) : super( renderBody: false, children: [ - _BallSpriteComponent()..tint(baseColor.withOpacity(0.5)), + _BallSpriteComponent(assetPath: assetPath), BallScalingBehavior(), BallGravitatingBehavior(), ], @@ -35,7 +36,7 @@ class Ball extends BodyComponent with Layered, InitialPosition, ZIndex { /// /// This can be used for testing [Ball]'s behaviors in isolation. @visibleForTesting - Ball.test({required this.baseColor}) + Ball.test() : super( children: [_BallSpriteComponent()], ); @@ -43,9 +44,6 @@ class Ball extends BodyComponent with Layered, InitialPosition, ZIndex { /// The size of the [Ball]. static final Vector2 size = Vector2.all(4.13); - /// The base [Color] used to tint this [Ball]. - final Color baseColor; - @override Body createBody() { final shape = CircleShape()..radius = size.x / 2; @@ -79,14 +77,22 @@ class Ball extends BodyComponent with Layered, InitialPosition, ZIndex { } class _BallSpriteComponent extends SpriteComponent with HasGameRef { + _BallSpriteComponent({ + this.assetPath, + }) : super( + anchor: Anchor.center, + ); + + final String? assetPath; + @override Future onLoad() async { await super.onLoad(); - final sprite = await gameRef.loadSprite( - Assets.images.ball.ball.keyName, + final sprite = Sprite( + gameRef.images + .fromCache(assetPath ?? theme.Assets.images.dash.ball.keyName), ); this.sprite = sprite; - size = sprite.originalSize / 10; - anchor = Anchor.center; + size = sprite.originalSize / 12.5; } } diff --git a/packages/pinball_components/sandbox/lib/common/games.dart b/packages/pinball_components/sandbox/lib/common/games.dart index 89d16450..bee6a280 100644 --- a/packages/pinball_components/sandbox/lib/common/games.dart +++ b/packages/pinball_components/sandbox/lib/common/games.dart @@ -24,6 +24,14 @@ abstract class AssetsGame extends Forge2DGame { } abstract class LineGame extends AssetsGame with PanDetector { + LineGame({ + List? imagesFileNames, + }) : super( + imagesFileNames: [ + if (imagesFileNames != null) ...imagesFileNames, + ], + ); + Vector2? _lineEnd; @override diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_a_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_a_game.dart index 32638c2d..78cebd95 100644 --- a/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_a_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_a_game.dart @@ -7,7 +7,6 @@ import 'package:sandbox/stories/ball/basic_ball_game.dart'; class AndroidBumperAGame extends BallGame { AndroidBumperAGame() : super( - color: const Color(0xFF0000FF), imagesFileNames: [ Assets.images.android.bumper.a.lit.keyName, Assets.images.android.bumper.a.dimmed.keyName, diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_b_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_b_game.dart index bfd4206c..9bd2caff 100644 --- a/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_b_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/android_bumper_b_game.dart @@ -7,7 +7,6 @@ import 'package:sandbox/stories/ball/basic_ball_game.dart'; class AndroidBumperBGame extends BallGame { AndroidBumperBGame() : super( - color: const Color(0xFF0000FF), imagesFileNames: [ Assets.images.android.bumper.b.lit.keyName, Assets.images.android.bumper.b.dimmed.keyName, diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_rail_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_rail_game.dart index dee83e26..4093ad33 100644 --- a/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_rail_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_rail_game.dart @@ -1,14 +1,12 @@ import 'dart:async'; import 'package:flame/input.dart'; -import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class SpaceshipRailGame extends BallGame { SpaceshipRailGame() : super( - color: Colors.blue, ballPriority: ZIndexes.ballOnSpaceshipRail, ballLayer: Layer.spaceshipExitRail, imagesFileNames: [ diff --git a/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart index 3446670a..fe4e6dae 100644 --- a/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/android_acres/spaceship_ramp_game.dart @@ -9,7 +9,6 @@ import 'package:sandbox/stories/ball/basic_ball_game.dart'; class SpaceshipRampGame extends BallGame with KeyboardEvents { SpaceshipRampGame() : super( - color: Colors.blue, ballPriority: ZIndexes.ballOnSpaceshipRamp, ballLayer: Layer.spaceshipEntranceRamp, imagesFileNames: [ diff --git a/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart b/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart index a66459a6..ac0989e2 100644 --- a/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/ball/ball_booster_game.dart @@ -1,9 +1,20 @@ import 'package:flame/components.dart'; -import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import 'package:sandbox/common/common.dart'; class BallBoosterGame extends LineGame { + BallBoosterGame() + : super( + imagesFileNames: [ + theme.Assets.images.android.ball.keyName, + theme.Assets.images.dash.ball.keyName, + theme.Assets.images.dino.ball.keyName, + theme.Assets.images.sparky.ball.keyName, + Assets.images.ball.flameEffect.keyName, + ], + ); + static const description = ''' Shows how a Ball with a boost works. @@ -12,7 +23,7 @@ class BallBoosterGame extends LineGame { @override void onLine(Vector2 line) { - final ball = Ball(baseColor: Colors.transparent); + final ball = Ball(); final impulse = line * -1 * 20; ball.add(BallTurboChargingBehavior(impulse: impulse)); diff --git a/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart b/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart index e57a0322..f3ba50f3 100644 --- a/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/ball/basic_ball_game.dart @@ -1,17 +1,20 @@ import 'package:flame/input.dart'; -import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import 'package:sandbox/common/common.dart'; class BallGame extends AssetsGame with TapDetector, Traceable { BallGame({ - this.color = Colors.blue, this.ballPriority = 0, this.ballLayer = Layer.all, + this.character, List? imagesFileNames, }) : super( imagesFileNames: [ - Assets.images.ball.ball.keyName, + theme.Assets.images.android.ball.keyName, + theme.Assets.images.dash.ball.keyName, + theme.Assets.images.dino.ball.keyName, + theme.Assets.images.sparky.ball.keyName, if (imagesFileNames != null) ...imagesFileNames, ], ); @@ -22,14 +25,23 @@ class BallGame extends AssetsGame with TapDetector, Traceable { - Tap anywhere on the screen to spawn a ball into the game. '''; - final Color color; + static final characterBallPaths = { + 'Dash': theme.Assets.images.dash.ball.keyName, + 'Sparky': theme.Assets.images.sparky.ball.keyName, + 'Android': theme.Assets.images.android.ball.keyName, + 'Dino': theme.Assets.images.dino.ball.keyName, + }; + final int ballPriority; final Layer ballLayer; + final String? character; @override void onTapUp(TapUpInfo info) { add( - Ball(baseColor: color) + Ball( + assetPath: characterBallPaths[character], + ) ..initialPosition = info.eventPosition.game ..layer = ballLayer ..priority = ballPriority, diff --git a/packages/pinball_components/sandbox/lib/stories/ball/stories.dart b/packages/pinball_components/sandbox/lib/stories/ball/stories.dart index eb472282..146ebcda 100644 --- a/packages/pinball_components/sandbox/lib/stories/ball/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/ball/stories.dart @@ -1,5 +1,4 @@ import 'package:dashbook/dashbook.dart'; -import 'package:flutter/material.dart'; import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/ball_booster_game.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; @@ -7,10 +6,14 @@ import 'package:sandbox/stories/ball/basic_ball_game.dart'; void addBallStories(Dashbook dashbook) { dashbook.storiesOf('Ball') ..addGame( - title: 'Colored', + title: 'Themed', description: BallGame.description, gameBuilder: (context) => BallGame( - color: context.colorProperty('color', Colors.blue), + character: context.listProperty( + 'Character', + BallGame.characterBallPaths.keys.first, + BallGame.characterBallPaths.keys.toList(), + ), ), ) ..addGame( diff --git a/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart b/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart index bc537de2..94389f60 100644 --- a/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/google_word/google_letter_game.dart @@ -1,14 +1,10 @@ -import 'dart:ui'; - import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class GoogleLetterGame extends BallGame { GoogleLetterGame() : super( - color: const Color(0xFF009900), imagesFileNames: [ Assets.images.googleWord.letter1.lit.keyName, Assets.images.googleWord.letter1.dimmed.keyName, diff --git a/packages/pinball_components/sandbox/lib/stories/launch_ramp/launch_ramp_game.dart b/packages/pinball_components/sandbox/lib/stories/launch_ramp/launch_ramp_game.dart index ea3bd4db..b6955a26 100644 --- a/packages/pinball_components/sandbox/lib/stories/launch_ramp/launch_ramp_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/launch_ramp/launch_ramp_game.dart @@ -1,14 +1,12 @@ import 'dart:async'; import 'package:flame/input.dart'; -import 'package:flutter/material.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class LaunchRampGame extends BallGame { LaunchRampGame() : super( - color: Colors.blue, ballPriority: ZIndexes.ballOnLaunchRamp, ballLayer: Layer.launcher, ); diff --git a/packages/pinball_components/sandbox/lib/stories/plunger/plunger_game.dart b/packages/pinball_components/sandbox/lib/stories/plunger/plunger_game.dart index 0f1ec2e4..0ee58cc9 100644 --- a/packages/pinball_components/sandbox/lib/stories/plunger/plunger_game.dart +++ b/packages/pinball_components/sandbox/lib/stories/plunger/plunger_game.dart @@ -6,8 +6,6 @@ import 'package:sandbox/common/common.dart'; import 'package:sandbox/stories/ball/basic_ball_game.dart'; class PlungerGame extends BallGame with KeyboardEvents, Traceable { - PlungerGame() : super(color: const Color(0xFFFF0000)); - static const description = ''' Shows how Plunger is rendered. diff --git a/packages/pinball_components/test/src/components/ball/ball_test.dart b/packages/pinball_components/test/src/components/ball/ball_test.dart index 655836a0..9195e0b2 100644 --- a/packages/pinball_components/test/src/components/ball/ball_test.dart +++ b/packages/pinball_components/test/src/components/ball/ball_test.dart @@ -2,31 +2,36 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final assets = [ + theme.Assets.images.android.ball.keyName, + theme.Assets.images.dash.ball.keyName, + theme.Assets.images.dino.ball.keyName, + theme.Assets.images.sparky.ball.keyName, + ]; - group('Ball', () { - const baseColor = Color(0xFFFFFFFF); + final flameTester = FlameTester(() => TestGame(assets)); + group('Ball', () { test( 'can be instantiated', () { - expect(Ball(baseColor: baseColor), isA()); - expect(Ball.test(baseColor: baseColor), isA()); + expect(Ball(), isA()); + expect(Ball.test(), isA()); }, ); flameTester.test( 'loads correctly', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ready(); await game.ensureAdd(ball); @@ -36,7 +41,7 @@ void main() { group('adds', () { flameTester.test('a BallScalingBehavior', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); expect( ball.descendants().whereType().length, @@ -45,7 +50,7 @@ void main() { }); flameTester.test('a BallGravitatingBehavior', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); expect( ball.descendants().whereType().length, @@ -58,7 +63,7 @@ void main() { flameTester.test( 'is dynamic', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); expect(ball.body.bodyType, equals(BodyType.dynamic)); @@ -67,7 +72,7 @@ void main() { group('can be moved', () { flameTester.test('by its weight', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); game.update(1); @@ -75,7 +80,7 @@ void main() { }); flameTester.test('by applying velocity', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); ball.body.gravityScale = Vector2.zero(); @@ -90,7 +95,7 @@ void main() { flameTester.test( 'exists', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); expect(ball.body.fixtures[0], isA()); @@ -100,7 +105,7 @@ void main() { flameTester.test( 'is dense', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); final fixture = ball.body.fixtures[0]; @@ -111,7 +116,7 @@ void main() { flameTester.test( 'shape is circular', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); final fixture = ball.body.fixtures[0]; @@ -123,7 +128,7 @@ void main() { flameTester.test( 'has Layer.all as default filter maskBits', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ready(); await game.ensureAdd(ball); await game.ready(); @@ -137,7 +142,7 @@ void main() { group('stop', () { group("can't be moved", () { flameTester.test('by its weight', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); ball.stop(); @@ -152,7 +157,7 @@ void main() { flameTester.test( 'by its weight when previously stopped', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); ball.stop(); ball.resume(); @@ -165,7 +170,7 @@ void main() { flameTester.test( 'by applying velocity when previously stopped', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); ball.stop(); ball.resume(); diff --git a/packages/pinball_components/test/src/components/ball/behaviors/ball_gravitating_behavior_test.dart b/packages/pinball_components/test/src/components/ball/behaviors/ball_gravitating_behavior_test.dart index d78df37a..ce193dc8 100644 --- a/packages/pinball_components/test/src/components/ball/behaviors/ball_gravitating_behavior_test.dart +++ b/packages/pinball_components/test/src/components/ball/behaviors/ball_gravitating_behavior_test.dart @@ -1,21 +1,19 @@ // ignore_for_file: cascade_invocations -import 'dart:ui'; - import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final asset = Assets.images.ball.ball.keyName; + final asset = theme.Assets.images.dash.ball.keyName; final flameTester = FlameTester(() => TestGame([asset])); group('BallGravitatingBehavior', () { - const baseColor = Color(0xFFFFFFFF); test('can be instantiated', () { expect( BallGravitatingBehavior(), @@ -24,7 +22,7 @@ void main() { }); flameTester.test('can be loaded', (game) async { - final ball = Ball.test(baseColor: baseColor); + final ball = Ball.test(); final behavior = BallGravitatingBehavior(); await ball.add(behavior); await game.ensureAdd(ball); @@ -37,12 +35,10 @@ void main() { flameTester.test( "overrides the body's horizontal gravity symmetrically", (game) async { - final ball1 = Ball.test(baseColor: baseColor) - ..initialPosition = Vector2(10, 0); + final ball1 = Ball.test()..initialPosition = Vector2(10, 0); await ball1.add(BallGravitatingBehavior()); - final ball2 = Ball.test(baseColor: baseColor) - ..initialPosition = Vector2(-10, 0); + final ball2 = Ball.test()..initialPosition = Vector2(-10, 0); await ball2.add(BallGravitatingBehavior()); await game.ensureAddAll([ball1, ball2]); diff --git a/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart b/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart index 0aeeda98..bd0cca49 100644 --- a/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart +++ b/packages/pinball_components/test/src/components/ball/behaviors/ball_scaling_behavior_test.dart @@ -1,21 +1,19 @@ // ignore_for_file: cascade_invocations -import 'dart:ui'; - import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../../../helpers/helpers.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final asset = Assets.images.ball.ball.keyName; + final asset = theme.Assets.images.dash.ball.keyName; final flameTester = FlameTester(() => TestGame([asset])); group('BallScalingBehavior', () { - const baseColor = Color(0xFFFFFFFF); test('can be instantiated', () { expect( BallScalingBehavior(), @@ -24,7 +22,7 @@ void main() { }); flameTester.test('can be loaded', (game) async { - final ball = Ball.test(baseColor: baseColor); + final ball = Ball.test(); final behavior = BallScalingBehavior(); await ball.add(behavior); await game.ensureAdd(ball); @@ -35,12 +33,10 @@ void main() { }); flameTester.test('scales the shape radius', (game) async { - final ball1 = Ball.test(baseColor: baseColor) - ..initialPosition = Vector2(0, 10); + final ball1 = Ball.test()..initialPosition = Vector2(0, 10); await ball1.add(BallScalingBehavior()); - final ball2 = Ball.test(baseColor: baseColor) - ..initialPosition = Vector2(0, -10); + final ball2 = Ball.test()..initialPosition = Vector2(0, -10); await ball2.add(BallScalingBehavior()); await game.ensureAddAll([ball1, ball2]); @@ -57,12 +53,10 @@ void main() { flameTester.test( 'scales the sprite', (game) async { - final ball1 = Ball.test(baseColor: baseColor) - ..initialPosition = Vector2(0, 10); + final ball1 = Ball.test()..initialPosition = Vector2(0, 10); await ball1.add(BallScalingBehavior()); - final ball2 = Ball.test(baseColor: baseColor) - ..initialPosition = Vector2(0, -10); + final ball2 = Ball.test()..initialPosition = Vector2(0, -10); await ball2.add(BallScalingBehavior()); await game.ensureAddAll([ball1, ball2]); diff --git a/packages/pinball_components/test/src/components/ball/behaviors/ball_turbo_charging_behavior_test.dart b/packages/pinball_components/test/src/components/ball/behaviors/ball_turbo_charging_behavior_test.dart index 00f34832..79eb030e 100644 --- a/packages/pinball_components/test/src/components/ball/behaviors/ball_turbo_charging_behavior_test.dart +++ b/packages/pinball_components/test/src/components/ball/behaviors/ball_turbo_charging_behavior_test.dart @@ -1,12 +1,10 @@ // ignore_for_file: cascade_invocations -import 'dart:ui'; - import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../../../helpers/helpers.dart'; @@ -16,9 +14,8 @@ void main() { group( 'BallTurboChargingBehavior', () { - final assets = [Assets.images.ball.ball.keyName]; - final flameTester = FlameTester(() => TestGame(assets)); - const baseColor = Color(0xFFFFFFFF); + final asset = theme.Assets.images.dash.ball.keyName; + final flameTester = FlameTester(() => TestGame([asset])); test('can be instantiated', () { expect( @@ -28,7 +25,7 @@ void main() { }); flameTester.test('can be loaded', (game) async { - final ball = Ball.test(baseColor: baseColor); + final ball = Ball.test(); final behavior = BallTurboChargingBehavior(impulse: Vector2.zero()); await ball.add(behavior); await game.ensureAdd(ball); @@ -41,7 +38,7 @@ void main() { flameTester.test( 'impulses the ball velocity when loaded', (game) async { - final ball = Ball.test(baseColor: baseColor); + final ball = Ball.test(); await game.ensureAdd(ball); final impulse = Vector2.all(1); final behavior = BallTurboChargingBehavior(impulse: impulse); @@ -59,7 +56,7 @@ void main() { ); flameTester.test('adds sprite', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); await ball.ensureAdd( @@ -73,7 +70,7 @@ void main() { }); flameTester.test('removes sprite after it finishes', (game) async { - final ball = Ball(baseColor: baseColor); + final ball = Ball(); await game.ensureAdd(ball); final behavior = BallTurboChargingBehavior(impulse: Vector2.zero()); diff --git a/packages/pinball_components/test/src/components/chrome_dino/behaviors/chrome_dino_chomping_behavior_test.dart b/packages/pinball_components/test/src/components/chrome_dino/behaviors/chrome_dino_chomping_behavior_test.dart index 8d052fab..dfc33967 100644 --- a/packages/pinball_components/test/src/components/chrome_dino/behaviors/chrome_dino_chomping_behavior_test.dart +++ b/packages/pinball_components/test/src/components/chrome_dino/behaviors/chrome_dino_chomping_behavior_test.dart @@ -4,11 +4,11 @@ import 'package:bloc_test/bloc_test.dart'; import 'package:flame/components.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/src/components/chrome_dino/behaviors/behaviors.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../../../helpers/helpers.dart'; @@ -20,7 +20,10 @@ class _MockFixture extends Mock implements Fixture {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final assets = [ + theme.Assets.images.dash.ball.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); group( 'ChromeDinoChompingBehavior', @@ -35,7 +38,7 @@ void main() { flameTester.test( 'beginContact sets ball sprite to be invisible and calls onChomp', (game) async { - final ball = Ball(baseColor: Colors.red); + final ball = Ball(); final behavior = ChromeDinoChompingBehavior(); final bloc = _MockChromeDinoCubit(); whenListen( diff --git a/packages/pinball_components/test/src/components/chrome_dino/behaviors/chrome_dino_spitting_behavior_test.dart b/packages/pinball_components/test/src/components/chrome_dino/behaviors/chrome_dino_spitting_behavior_test.dart index 1d0a55b4..8c2cbe57 100644 --- a/packages/pinball_components/test/src/components/chrome_dino/behaviors/chrome_dino_spitting_behavior_test.dart +++ b/packages/pinball_components/test/src/components/chrome_dino/behaviors/chrome_dino_spitting_behavior_test.dart @@ -5,11 +5,11 @@ import 'dart:async'; import 'package:bloc_test/bloc_test.dart'; import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_components/src/components/chrome_dino/behaviors/behaviors.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../../../helpers/helpers.dart'; @@ -17,7 +17,10 @@ class _MockChromeDinoCubit extends Mock implements ChromeDinoCubit {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final flameTester = FlameTester(TestGame.new); + final assets = [ + theme.Assets.images.dash.ball.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); group( 'ChromeDinoSpittingBehavior', @@ -33,7 +36,7 @@ void main() { flameTester.test( 'sets ball sprite to visible and sets a linear velocity', (game) async { - final ball = Ball(baseColor: Colors.red); + final ball = Ball(); final behavior = ChromeDinoSpittingBehavior(); final bloc = _MockChromeDinoCubit(); final streamController = StreamController(); @@ -71,7 +74,7 @@ void main() { flameTester.test( 'calls onSpit', (game) async { - final ball = Ball(baseColor: Colors.red); + final ball = Ball(); final behavior = ChromeDinoSpittingBehavior(); final bloc = _MockChromeDinoCubit(); final streamController = StreamController(); diff --git a/packages/pinball_components/test/src/components/chrome_dino/cubit/chrome_dino_cubit_test.dart b/packages/pinball_components/test/src/components/chrome_dino/cubit/chrome_dino_cubit_test.dart index 5b31be74..79375a6e 100644 --- a/packages/pinball_components/test/src/components/chrome_dino/cubit/chrome_dino_cubit_test.dart +++ b/packages/pinball_components/test/src/components/chrome_dino/cubit/chrome_dino_cubit_test.dart @@ -1,5 +1,4 @@ import 'package:bloc_test/bloc_test.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -7,7 +6,7 @@ void main() { group( 'ChromeDinoCubit', () { - final ball = Ball(baseColor: Colors.red); + final ball = Ball(); blocTest( 'onOpenMouth emits true', diff --git a/packages/pinball_components/test/src/components/chrome_dino/cubit/chrome_dino_state_test.dart b/packages/pinball_components/test/src/components/chrome_dino/cubit/chrome_dino_state_test.dart index d067674b..442d544b 100644 --- a/packages/pinball_components/test/src/components/chrome_dino/cubit/chrome_dino_state_test.dart +++ b/packages/pinball_components/test/src/components/chrome_dino/cubit/chrome_dino_state_test.dart @@ -1,6 +1,5 @@ // ignore_for_file: prefer_const_constructors -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -61,7 +60,7 @@ void main() { 'copies correctly ' 'when all arguments specified', () { - final ball = Ball(baseColor: Colors.red); + final ball = Ball(); const chromeDinoState = ChromeDinoState( status: ChromeDinoStatus.chomping, isMouthOpen: true, diff --git a/packages/pinball_components/test/src/components/flipper_test.dart b/packages/pinball_components/test/src/components/flipper_test.dart index c34d0d1c..314b1f77 100644 --- a/packages/pinball_components/test/src/components/flipper_test.dart +++ b/packages/pinball_components/test/src/components/flipper_test.dart @@ -2,9 +2,9 @@ import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../helpers/helpers.dart'; @@ -13,6 +13,7 @@ void main() { final assets = [ Assets.images.flipper.left.keyName, Assets.images.flipper.right.keyName, + theme.Assets.images.dash.ball.keyName, ]; final flameTester = FlameTester(() => TestGame(assets)); @@ -89,7 +90,7 @@ void main() { 'has greater mass than Ball', (game) async { final flipper = Flipper(side: BoardSide.left); - final ball = Ball(baseColor: Colors.white); + final ball = Ball(); await game.ready(); await game.ensureAddAll([flipper, ball]); diff --git a/packages/pinball_components/test/src/components/golden/ball/android.png b/packages/pinball_components/test/src/components/golden/ball/android.png new file mode 100644 index 0000000000000000000000000000000000000000..2a659092cba7b6b6798a7b526261ac4fac8bbfce GIT binary patch literal 28038 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 U03({atrP)X*Sl79_4d#I1@L7-YybcN literal 0 HcmV?d00001 diff --git a/packages/pinball_components/test/src/components/golden/ball/dash.png b/packages/pinball_components/test/src/components/golden/ball/dash.png new file mode 100644 index 0000000000000000000000000000000000000000..c95afc8801dee57340120032ea2c376448d4792f GIT binary patch literal 28052 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_components/test/src/components/golden/ball/dino.png b/packages/pinball_components/test/src/components/golden/ball/dino.png new file mode 100644 index 0000000000000000000000000000000000000000..8ea1075884ea25ca26264512a1083de45ce1cde6 GIT binary patch literal 28658 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_components/test/src/components/golden/ball/sparky.png b/packages/pinball_components/test/src/components/golden/ball/sparky.png new file mode 100644 index 0000000000000000000000000000000000000000..afdeb2636132d754c04370e5389fb090fed9298b GIT binary patch literal 27904 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 DFn_Y_ literal 0 HcmV?d00001 diff --git a/packages/pinball_theme/assets/images/android/ball.png b/packages/pinball_theme/assets/images/android/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..b5cfbc3ff3df86e10a37b2126d7a67a3f0c8fb96 GIT binary patch literal 6544 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_theme/assets/images/dash/ball.png b/packages/pinball_theme/assets/images/dash/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..fa754cbc158e12e489a783def24967f9777564d9 GIT binary patch literal 6561 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_theme/assets/images/dino/ball.png b/packages/pinball_theme/assets/images/dino/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..02b99c4314e78c4ff3cd2053ebfe698e65134f0d GIT binary patch literal 6973 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 literal 0 HcmV?d00001 diff --git a/packages/pinball_theme/assets/images/sparky/ball.png b/packages/pinball_theme/assets/images/sparky/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..95e5a10b6a81f879289a985640f155ee97a4ecc0 GIT binary patch literal 6449 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 LXlYQT=Mnc`O=SU) literal 0 HcmV?d00001 diff --git a/packages/pinball_theme/lib/src/generated/assets.gen.dart b/packages/pinball_theme/lib/src/generated/assets.gen.dart index 3feeecce..545f514b 100644 --- a/packages/pinball_theme/lib/src/generated/assets.gen.dart +++ b/packages/pinball_theme/lib/src/generated/assets.gen.dart @@ -36,9 +36,9 @@ class $AssetsImagesAndroidGen { AssetGenImage get background => const AssetGenImage('assets/images/android/background.png'); - /// File path: assets/images/android/character.png - AssetGenImage get character => - const AssetGenImage('assets/images/android/character.png'); + /// File path: assets/images/android/ball.png + AssetGenImage get ball => + const AssetGenImage('assets/images/android/ball.png'); /// File path: assets/images/android/icon.png AssetGenImage get icon => @@ -60,9 +60,8 @@ class $AssetsImagesDashGen { AssetGenImage get background => const AssetGenImage('assets/images/dash/background.png'); - /// File path: assets/images/dash/character.png - AssetGenImage get character => - const AssetGenImage('assets/images/dash/character.png'); + /// File path: assets/images/dash/ball.png + AssetGenImage get ball => const AssetGenImage('assets/images/dash/ball.png'); /// File path: assets/images/dash/icon.png AssetGenImage get icon => const AssetGenImage('assets/images/dash/icon.png'); @@ -83,9 +82,8 @@ class $AssetsImagesDinoGen { AssetGenImage get background => const AssetGenImage('assets/images/dino/background.png'); - /// File path: assets/images/dino/character.png - AssetGenImage get character => - const AssetGenImage('assets/images/dino/character.png'); + /// File path: assets/images/dino/ball.png + AssetGenImage get ball => const AssetGenImage('assets/images/dino/ball.png'); /// File path: assets/images/dino/icon.png AssetGenImage get icon => const AssetGenImage('assets/images/dino/icon.png'); @@ -106,9 +104,9 @@ class $AssetsImagesSparkyGen { AssetGenImage get background => const AssetGenImage('assets/images/sparky/background.png'); - /// File path: assets/images/sparky/character.png - AssetGenImage get character => - const AssetGenImage('assets/images/sparky/character.png'); + /// File path: assets/images/sparky/ball.png + AssetGenImage get ball => + const AssetGenImage('assets/images/sparky/ball.png'); /// File path: assets/images/sparky/icon.png AssetGenImage get icon => diff --git a/packages/pinball_theme/lib/src/themes/android_theme.dart b/packages/pinball_theme/lib/src/themes/android_theme.dart index 8989c717..6e7d76b2 100644 --- a/packages/pinball_theme/lib/src/themes/android_theme.dart +++ b/packages/pinball_theme/lib/src/themes/android_theme.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:pinball_theme/pinball_theme.dart'; /// {@template android_theme} @@ -12,7 +11,7 @@ class AndroidTheme extends CharacterTheme { String get name => 'Android'; @override - Color get ballColor => Colors.green; + AssetGenImage get ball => Assets.images.android.ball; @override AssetGenImage get background => Assets.images.android.background; diff --git a/packages/pinball_theme/lib/src/themes/character_theme.dart b/packages/pinball_theme/lib/src/themes/character_theme.dart index 072c917f..596f41a0 100644 --- a/packages/pinball_theme/lib/src/themes/character_theme.dart +++ b/packages/pinball_theme/lib/src/themes/character_theme.dart @@ -1,5 +1,4 @@ import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; import 'package:pinball_theme/pinball_theme.dart'; /// {@template character_theme} @@ -15,8 +14,8 @@ abstract class CharacterTheme extends Equatable { /// Name of character. String get name; - /// Ball color for this theme. - Color get ballColor; + /// Asset for the ball. + AssetGenImage get ball; /// Asset for the background. AssetGenImage get background; @@ -33,7 +32,7 @@ abstract class CharacterTheme extends Equatable { @override List get props => [ name, - ballColor, + ball, background, icon, leaderboardIcon, diff --git a/packages/pinball_theme/lib/src/themes/dash_theme.dart b/packages/pinball_theme/lib/src/themes/dash_theme.dart index 7584c8ed..be3a8873 100644 --- a/packages/pinball_theme/lib/src/themes/dash_theme.dart +++ b/packages/pinball_theme/lib/src/themes/dash_theme.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:pinball_theme/pinball_theme.dart'; /// {@template dash_theme} @@ -12,7 +11,7 @@ class DashTheme extends CharacterTheme { String get name => 'Dash'; @override - Color get ballColor => Colors.blue; + AssetGenImage get ball => Assets.images.dash.ball; @override AssetGenImage get background => Assets.images.dash.background; diff --git a/packages/pinball_theme/lib/src/themes/dino_theme.dart b/packages/pinball_theme/lib/src/themes/dino_theme.dart index 3baf466c..1de42d41 100644 --- a/packages/pinball_theme/lib/src/themes/dino_theme.dart +++ b/packages/pinball_theme/lib/src/themes/dino_theme.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:pinball_theme/pinball_theme.dart'; /// {@template dino_theme} @@ -12,7 +11,7 @@ class DinoTheme extends CharacterTheme { String get name => 'Dino'; @override - Color get ballColor => Colors.grey; + AssetGenImage get ball => Assets.images.dino.ball; @override AssetGenImage get background => Assets.images.dino.background; diff --git a/packages/pinball_theme/lib/src/themes/sparky_theme.dart b/packages/pinball_theme/lib/src/themes/sparky_theme.dart index 7884a22f..1699f3ae 100644 --- a/packages/pinball_theme/lib/src/themes/sparky_theme.dart +++ b/packages/pinball_theme/lib/src/themes/sparky_theme.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:pinball_theme/pinball_theme.dart'; /// {@template sparky_theme} @@ -9,7 +8,7 @@ class SparkyTheme extends CharacterTheme { const SparkyTheme(); @override - Color get ballColor => Colors.orange; + AssetGenImage get ball => Assets.images.sparky.ball; @override String get name => 'Sparky'; diff --git a/test/game/components/controlled_ball_test.dart b/test/game/components/controlled_ball_test.dart index d8d31b4e..dc142ffd 100644 --- a/test/game/components/controlled_ball_test.dart +++ b/test/game/components/controlled_ball_test.dart @@ -2,13 +2,12 @@ import 'package:bloc_test/bloc_test.dart'; import 'package:flame/components.dart'; -import 'package:flame/extensions.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 'package:pinball_theme/pinball_theme.dart' as theme; import '../../helpers/helpers.dart'; @@ -33,13 +32,16 @@ class _MockBall extends Mock implements Ball {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); + final assets = [ + theme.Assets.images.dash.ball.keyName, + ]; group('BallController', () { late Ball ball; late GameBloc gameBloc; setUp(() { - ball = Ball(baseColor: const Color(0xFF00FFFF)); + ball = Ball(); gameBloc = _MockGameBloc(); whenListen( gameBloc, @@ -51,6 +53,7 @@ void main() { final flameBlocTester = FlameBlocTester( gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, + assets: assets, ); test('can be instantiated', () { @@ -68,7 +71,7 @@ void main() { await ball.add(controller); await game.ensureAdd(ball); - final otherBall = Ball(baseColor: const Color(0xFF00FFFF)); + final otherBall = Ball(); final otherController = BallController(otherBall); await otherBall.add(otherController); await game.ensureAdd(otherBall); @@ -106,6 +109,7 @@ void main() { flameBlocTester.testGameWidget( 'adds TurboChargeActivated', setUp: (game, tester) async { + await game.images.loadAll(assets); final controller = BallController(ball); await ball.add(controller); await game.ensureAdd(ball); diff --git a/test/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior_test.dart b/test/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior_test.dart index f9e2988d..71b41029 100644 --- a/test/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior_test.dart +++ b/test/game/components/flutter_forest/behaviors/flutter_forest_bonus_behavior_test.dart @@ -10,15 +10,21 @@ import 'package:pinball/game/components/flutter_forest/behaviors/behaviors.dart' import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; +import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../../../helpers/helpers.dart'; class _MockGameBloc extends Mock implements GameBloc {} void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + group('FlutterForestBonusBehavior', () { late GameBloc gameBloc; - final assets = [Assets.images.dash.animatronic.keyName]; + final assets = [ + Assets.images.dash.animatronic.keyName, + theme.Assets.images.dash.ball.keyName, + ]; setUp(() { gameBloc = _MockGameBloc(); @@ -32,6 +38,7 @@ void main() { final flameBlocTester = FlameBlocTester( gameBuilder: EmptyPinballTestGame.new, blocBuilder: () => gameBloc, + assets: assets, ); void _contactedBumper(DashNestBumper bumper) => diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index f942c47c..b75b3147 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -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_theme/pinball_theme.dart' as theme; import '../helpers/helpers.dart'; @@ -43,7 +44,10 @@ void main() { Assets.images.backbox.marquee.keyName, Assets.images.backbox.displayDivider.keyName, Assets.images.boardBackground.keyName, - Assets.images.ball.ball.keyName, + theme.Assets.images.android.ball.keyName, + theme.Assets.images.dash.ball.keyName, + theme.Assets.images.dino.ball.keyName, + theme.Assets.images.sparky.ball.keyName, Assets.images.ball.flameEffect.keyName, Assets.images.baseboard.left.keyName, Assets.images.baseboard.right.keyName,