diff --git a/analysis_options.yaml b/analysis_options.yaml index 07aa1dab..44aef9ac 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1 @@ include: package:very_good_analysis/analysis_options.2.4.0.yaml -linter: - rules: - public_member_api_docs: false diff --git a/assets/images/components/flipper.png b/assets/images/components/flipper.png new file mode 100644 index 00000000..f63974c4 Binary files /dev/null and b/assets/images/components/flipper.png differ diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 7e3fdf17..cf6213e9 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -5,6 +5,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +// ignore_for_file: public_member_api_docs + import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:pinball/l10n/l10n.dart'; diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index c612b584..34fcc47a 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -5,6 +5,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'dart:developer'; diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 3b5c16b0..31aa0498 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; diff --git a/lib/game/bloc/game_event.dart b/lib/game/bloc/game_event.dart index 417f6322..fa57cbff 100644 --- a/lib/game/bloc/game_event.dart +++ b/lib/game/bloc/game_event.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + part of 'game_bloc.dart'; @immutable @@ -5,16 +7,22 @@ abstract class GameEvent extends Equatable { const GameEvent(); } +/// {@template ball_lost_game_event} /// Event added when a user drops a ball off the screen. +/// {@endtemplate} class BallLost extends GameEvent { + /// {@macro ball_lost_game_event} const BallLost(); @override List get props => []; } +/// {@template scored_game_event} /// Event added when a user increases their score. +/// {@endtemplate} class Scored extends GameEvent { + /// {@macro scored_game_event} const Scored({ required this.points, }) : assert(points > 0, 'Points must be greater than 0'); diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 8a5ab298..f8456518 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + part of 'game_bloc.dart'; /// {@template game_state} diff --git a/lib/game/components/ball.dart b/lib/game/components/ball.dart index 5f1bbfd6..6a06af47 100644 --- a/lib/game/components/ball.dart +++ b/lib/game/components/ball.dart @@ -3,29 +3,39 @@ import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:pinball/game/game.dart'; +/// {@template ball} +/// A solid, [BodyType.dynamic] sphere that rolls and bounces along the +/// [PinballGame]. +/// {@endtemplate} class Ball extends PositionBodyComponent with BlocComponent { + /// {@macro ball} Ball({ required Vector2 position, }) : _position = Vector2(position.x, position.y + ballSize.y), super(size: ballSize); + /// Dimensions of the [Ball]. static final ballSize = Vector2.all(2); + /// The initial position of the [Ball] body. final Vector2 _position; + /// Asset location of the sprite that renders with the [Ball]. + /// + /// Sprite is preloaded by [PinballGameAssetsX]. static const spritePath = 'components/ball.png'; @override Future onLoad() async { await super.onLoad(); final sprite = await gameRef.loadSprite(spritePath); - positionComponent = SpriteComponent(sprite: sprite, size: ballSize); + positionComponent = SpriteComponent(sprite: sprite, size: size); } @override Body createBody() { - final shape = CircleShape()..radius = ballSize.x / 2; + final shape = CircleShape()..radius = size.x / 2; final fixtureDef = FixtureDef(shape)..density = 1; @@ -37,6 +47,11 @@ class Ball extends PositionBodyComponent return world.createBody(bodyDef)..createFixture(fixtureDef); } + /// Removes the [Ball] from a [PinballGame]; spawning a new [Ball] if + /// any are left. + /// + /// Triggered by [BottomWallBallContactCallback] when the [Ball] falls into + /// a [BottomWall]. void lost() { shouldRemove = true; diff --git a/lib/game/components/flipper.dart b/lib/game/components/flipper.dart index bd071b93..16754ed3 100644 --- a/lib/game/components/flipper.dart +++ b/lib/game/components/flipper.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:math' as math; +import 'package:flame/components.dart' show SpriteComponent; import 'package:flame/input.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinball/game/game.dart'; @@ -12,19 +12,15 @@ import 'package:pinball/game/game.dart'; /// /// [Flipper] can be controlled by the player in an arc motion. /// {@endtemplate flipper} -class Flipper extends BodyComponent with KeyboardHandler { +class Flipper extends PositionBodyComponent with KeyboardHandler { /// {@macro flipper} Flipper._({ required Vector2 position, required this.side, required List keys, }) : _position = position, - _keys = keys { - // TODO(alestiago): Use sprite instead of color when provided. - paint = Paint() - ..color = const Color(0xFF00FF00) - ..style = PaintingStyle.fill; - } + _keys = keys, + super(size: Vector2(width, height)); /// A left positioned [Flipper]. Flipper.left({ @@ -50,6 +46,11 @@ class Flipper extends BodyComponent with KeyboardHandler { ], ); + /// Asset location of the sprite that renders with the [Flipper]. + /// + /// Sprite is preloaded by [PinballGameAssetsX]. + static const spritePath = 'components/flipper.png'; + /// The width of the [Flipper]. static const width = 12.0; @@ -75,6 +76,20 @@ class Flipper extends BodyComponent with KeyboardHandler { /// [onKeyEvent] method listens to when one of these keys is pressed. final List _keys; + @override + Future onLoad() async { + await super.onLoad(); + final sprite = await gameRef.loadSprite(spritePath); + positionComponent = SpriteComponent( + sprite: sprite, + size: size, + ); + + if (side == BoardSide.right) { + positionComponent?.flipHorizontally(); + } + } + /// Applies downward linear velocity to the [Flipper], moving it to its /// resting position. void _moveDown() { @@ -148,6 +163,7 @@ class Flipper extends BodyComponent with KeyboardHandler { // TODO(erickzanardo): Remove this once the issue is solved: // https://github.com/flame-engine/flame/issues/1417 + // ignore: public_member_api_docs final Completer hasMounted = Completer(); @override diff --git a/lib/game/components/wall.dart b/lib/game/components/wall.dart index a05b50b8..3e9b1f8b 100644 --- a/lib/game/components/wall.dart +++ b/lib/game/components/wall.dart @@ -6,6 +6,7 @@ import 'package:pinball/game/components/components.dart'; /// {@template wall} /// A continuous generic and [BodyType.static] barrier that divides a game area. /// {@endtemplate} +// TODO(alestiago): Remove [Wall] for [Pathway.straight]. class Wall extends BodyComponent { /// {@macro wall} Wall({ @@ -13,7 +14,10 @@ class Wall extends BodyComponent { required this.end, }); + /// The [start] of the [Wall]. final Vector2 start; + + /// The [end] of the [Wall]. final Vector2 end; @override diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 964aeda1..778e2bc2 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -6,6 +6,7 @@ extension PinballGameAssetsX on PinballGame { Future preLoadAssets() async { await Future.wait([ images.load(Ball.spritePath), + images.load(Flipper.spritePath), ]); } } diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 0c27ee94..c287a378 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'package:flame/input.dart'; import 'package:flame_bloc/flame_bloc.dart'; diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index 8a9a981c..a49ff0c1 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + import 'package:flame/game.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/game/view/widgets/game_over_dialog.dart b/lib/game/view/widgets/game_over_dialog.dart index 586d6c56..9d1c61b0 100644 --- a/lib/game/view/widgets/game_over_dialog.dart +++ b/lib/game/view/widgets/game_over_dialog.dart @@ -1,6 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:pinball/game/game.dart'; +/// {@template game_over_dialog} +/// [Dialog] displayed when the [PinballGame] is over. +/// {@endtemplate} class GameOverDialog extends StatelessWidget { + /// {@macro game_over_dialog} const GameOverDialog({Key? key}) : super(key: key); @override diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index 766b5e31..548a81a6 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -5,6 +5,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +// ignore_for_file: public_member_api_docs + import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; diff --git a/lib/landing/view/landing_page.dart b/lib/landing/view/landing_page.dart index a688dee1..c705f084 100644 --- a/lib/landing/view/landing_page.dart +++ b/lib/landing/view/landing_page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: public_member_api_docs + import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/l10n/l10n.dart'; diff --git a/lib/theme/cubit/theme_cubit.dart b/lib/theme/cubit/theme_cubit.dart index 7ba79e59..94eba4a6 100644 --- a/lib/theme/cubit/theme_cubit.dart +++ b/lib/theme/cubit/theme_cubit.dart @@ -1,3 +1,6 @@ +// ignore_for_file: public_member_api_docs +// TODO(allisonryan0002): Document this section when the API is stable. + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:pinball_theme/pinball_theme.dart'; diff --git a/lib/theme/cubit/theme_state.dart b/lib/theme/cubit/theme_state.dart index 13b3ea5f..078f5c84 100644 --- a/lib/theme/cubit/theme_state.dart +++ b/lib/theme/cubit/theme_state.dart @@ -1,3 +1,6 @@ +// ignore_for_file: public_member_api_docs +// TODO(allisonryan0002): Document this section when the API is stable. + part of 'theme_cubit.dart'; class ThemeState extends Equatable { diff --git a/test/game/components/anchor_test.dart b/test/game/components/anchor_test.dart index 5cc37eca..a5aa7d2d 100644 --- a/test/game/components/anchor_test.dart +++ b/test/game/components/anchor_test.dart @@ -15,6 +15,7 @@ void main() { 'loads correctly', (game) async { final anchor = Anchor(position: Vector2.zero()); + await game.ready(); await game.ensureAdd(anchor); expect(game.contains(anchor), isTrue); @@ -25,6 +26,7 @@ void main() { flameTester.test( 'positions correctly', (game) async { + await game.ready(); final position = Vector2.all(10); final anchor = Anchor(position: position); await game.ensureAdd(anchor); @@ -37,6 +39,7 @@ void main() { flameTester.test( 'is static', (game) async { + await game.ready(); final anchor = Anchor(position: Vector2.zero()); await game.ensureAdd(anchor); diff --git a/test/game/components/pathway_test.dart b/test/game/components/pathway_test.dart index d3b82e96..036309b1 100644 --- a/test/game/components/pathway_test.dart +++ b/test/game/components/pathway_test.dart @@ -18,6 +18,7 @@ void main() { flameTester.test( 'has transparent color by default when no color is specified', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -38,6 +39,7 @@ void main() { flameTester.test( 'has a color when is specified', (game) async { + await game.ready(); const defaultColor = Colors.blue; final pathway = Pathway.straight( @@ -59,6 +61,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -75,6 +78,7 @@ void main() { flameTester.test( 'positions correctly', (game) async { + await game.ready(); final position = Vector2.all(10); final pathway = Pathway.straight( position: position, @@ -92,6 +96,7 @@ void main() { flameTester.test( 'is static', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -109,6 +114,7 @@ void main() { flameTester.test( 'has only one ChainShape when singleWall is true', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -128,6 +134,7 @@ void main() { flameTester.test( 'has two ChainShape when singleWall is false (default)', (game) async { + await game.ready(); final pathway = Pathway.straight( position: Vector2.zero(), start: Vector2(10, 10), @@ -150,6 +157,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final pathway = Pathway.arc( position: Vector2.zero(), width: width, @@ -166,6 +174,7 @@ void main() { flameTester.test( 'positions correctly', (game) async { + await game.ready(); final position = Vector2.all(10); final pathway = Pathway.arc( position: position, @@ -183,6 +192,7 @@ void main() { flameTester.test( 'is static', (game) async { + await game.ready(); final pathway = Pathway.arc( position: Vector2.zero(), width: width, @@ -208,6 +218,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final pathway = Pathway.bezierCurve( position: Vector2.zero(), controlPoints: controlPoints, @@ -223,6 +234,7 @@ void main() { flameTester.test( 'positions correctly', (game) async { + await game.ready(); final position = Vector2.all(10); final pathway = Pathway.bezierCurve( position: position, @@ -239,6 +251,7 @@ void main() { flameTester.test( 'is static', (game) async { + await game.ready(); final pathway = Pathway.bezierCurve( position: Vector2.zero(), controlPoints: controlPoints, diff --git a/test/game/components/plunger_test.dart b/test/game/components/plunger_test.dart index ace0c9f5..bb654d56 100644 --- a/test/game/components/plunger_test.dart +++ b/test/game/components/plunger_test.dart @@ -19,6 +19,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final plunger = Plunger(position: Vector2.zero()); await game.ensureAdd(plunger); diff --git a/test/game/components/wall_test.dart b/test/game/components/wall_test.dart index 54263a85..07e66a38 100644 --- a/test/game/components/wall_test.dart +++ b/test/game/components/wall_test.dart @@ -37,6 +37,7 @@ void main() { flameTester.test( 'loads correctly', (game) async { + await game.ready(); final wall = Wall( start: Vector2.zero(), end: Vector2(100, 0),