feat: add sparky computer and mechanics

pull/155/head
Allison Ryan 4 years ago
parent 655007b2d2
commit 125e4e14e3

@ -13,6 +13,7 @@ class GameBloc extends Bloc<GameEvent, GameState> {
on<Scored>(_onScored);
on<BonusLetterActivated>(_onBonusLetterActivated);
on<DashNestActivated>(_onDashNestActivated);
on<SparkyTurboChargeActivated>(_onSparkyTurboChargeActivated);
}
static const bonusWord = 'GOOGLE';
@ -77,4 +78,18 @@ class GameBloc extends Bloc<GameEvent, GameState> {
);
}
}
Future<void> _onSparkyTurboChargeActivated(
SparkyTurboChargeActivated event,
Emitter emit,
) async {
emit(
state.copyWith(
bonusHistory: [
...state.bonusHistory,
GameBonus.sparkyTurboCharge,
],
),
);
}
}

@ -54,3 +54,10 @@ class DashNestActivated extends GameEvent {
@override
List<Object?> get props => [nestId];
}
class SparkyTurboChargeActivated extends GameEvent {
const SparkyTurboChargeActivated();
@override
List<Object?> get props => [];
}

@ -10,6 +10,9 @@ enum GameBonus {
/// Bonus achieved when the user activates all dash nest bumpers.
dashNest,
/// Bonus achieved when a ball enters Sparky's computer.
sparkyTurboCharge,
}
/// {@template game_state}

@ -2,6 +2,7 @@ export 'board.dart';
export 'bonus_word.dart';
export 'controlled_ball.dart';
export 'controlled_flipper.dart';
export 'controlled_sparky_computer.dart';
export 'flutter_forest.dart';
export 'plunger.dart';
export 'score_points.dart';

@ -53,6 +53,20 @@ class BallController extends ComponentController<Ball>
component.shouldRemove = true;
}
/// Stops the [Ball] inside of the [SparkyComputer] while the turbo charge
/// sequence runs, then boosts the ball out of the computer.
Future<void> turboCharge() async {
gameRef.read<GameBloc>().add(const SparkyTurboChargeActivated());
// TODO(allisonryan0002): adjust delay to match animation duration once
// given animations.
component.stop();
await Future<void>.delayed(const Duration(seconds: 1));
component
..resume()
..boost(Vector2(200, -500));
}
@override
void onRemove() {
super.onRemove();

@ -0,0 +1,70 @@
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball/flame/flame.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template controlled_sparky_computer}
/// [SparkyComputer] with a [SparkyComputerController] attached.
/// {@endtemplate}
class ControlledSparkyComputer extends SparkyComputer
with Controls<SparkyComputerController>, HasGameRef<PinballGame> {
/// {@macro controlled_sparky_computer}
ControlledSparkyComputer() {
controller = SparkyComputerController(this);
}
@override
void build(Forge2DGame _) {
addContactCallback(_SparkyTurboChargeSensorBallContactCallback());
final sparkyTurboChargeSensor = _SparkyTurboChargeSensor()
..initialPosition = Vector2(-13.4, 49.8);
add(sparkyTurboChargeSensor);
super.build(_);
}
}
class _SparkyTurboChargeSensor extends BodyComponent with InitialPosition {
_SparkyTurboChargeSensor() {
renderBody = false;
}
@override
Body createBody() {
final shape = CircleShape()..radius = .1;
final fixtureDef = FixtureDef(shape)..isSensor = true;
final bodyDef = BodyDef()
..position = initialPosition
..userData = this;
return world.createBody(bodyDef)..createFixture(fixtureDef);
}
}
/// {@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);
}
class _SparkyTurboChargeSensorBallContactCallback
extends ContactCallback<_SparkyTurboChargeSensor, ControlledBall> {
_SparkyTurboChargeSensorBallContactCallback();
@override
void begin(
_SparkyTurboChargeSensor sparkyTurboChargeSensor,
ControlledBall ball,
_,
) {
ball.controller.turboCharge();
}
}

@ -42,6 +42,14 @@ extension PinballGameAssetsX on PinballGame {
images.load(components.Assets.images.spaceship.rail.foreground.keyName),
images.load(components.Assets.images.chromeDino.mouth.keyName),
images.load(components.Assets.images.chromeDino.head.keyName),
images.load(components.Assets.images.sparky.computer.base.keyName),
images.load(components.Assets.images.sparky.computer.top.keyName),
images.load(components.Assets.images.sparky.bumper.a.active.keyName),
images.load(components.Assets.images.sparky.bumper.a.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.b.active.keyName),
images.load(components.Assets.images.sparky.bumper.b.inactive.keyName),
images.load(components.Assets.images.sparky.bumper.c.active.keyName),
images.load(components.Assets.images.sparky.bumper.c.inactive.keyName),
images.load(Assets.images.components.background.path),
]);
}

@ -40,6 +40,7 @@ class PinballGame extends Forge2DGame
await _addGameBoundaries();
unawaited(addFromBlueprint(Boundaries()));
unawaited(addFromBlueprint(LaunchRamp()));
unawaited(addFromBlueprint(ControlledSparkyComputer()));
final plunger = Plunger(compressionDistance: 29)
..initialPosition = Vector2(38, -19);
@ -147,7 +148,7 @@ class DebugPinballGame extends PinballGame with TapDetector {
@override
Future<void> onLoad() async {
await super.onLoad();
await _loadBackground();
// await _loadBackground();
}
// TODO(alestiago): Move to PinballGame once we have the real background

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

@ -30,8 +30,7 @@ class $AssetsImagesGen {
$AssetsImagesLaunchRampGen get launchRamp =>
const $AssetsImagesLaunchRampGen();
$AssetsImagesSpaceshipGen get spaceship => const $AssetsImagesSpaceshipGen();
$AssetsImagesSparkyBumperGen get sparkyBumper =>
const $AssetsImagesSparkyBumperGen();
$AssetsImagesSparkyGen get sparky => const $AssetsImagesSparkyGen();
}
class $AssetsImagesBaseboardGen {
@ -144,12 +143,13 @@ class $AssetsImagesSpaceshipGen {
const AssetGenImage('assets/images/spaceship/saucer.png');
}
class $AssetsImagesSparkyBumperGen {
const $AssetsImagesSparkyBumperGen();
class $AssetsImagesSparkyGen {
const $AssetsImagesSparkyGen();
$AssetsImagesSparkyBumperAGen get a => const $AssetsImagesSparkyBumperAGen();
$AssetsImagesSparkyBumperBGen get b => const $AssetsImagesSparkyBumperBGen();
$AssetsImagesSparkyBumperCGen get c => const $AssetsImagesSparkyBumperCGen();
$AssetsImagesSparkyBumperGen get bumper =>
const $AssetsImagesSparkyBumperGen();
$AssetsImagesSparkyComputerGen get computer =>
const $AssetsImagesSparkyComputerGen();
}
class $AssetsImagesDashBumperAGen {
@ -216,40 +216,60 @@ class $AssetsImagesSpaceshipRampGen {
'assets/images/spaceship/ramp/railing-foreground.png');
}
class $AssetsImagesSparkyBumperGen {
const $AssetsImagesSparkyBumperGen();
$AssetsImagesSparkyBumperAGen get a => const $AssetsImagesSparkyBumperAGen();
$AssetsImagesSparkyBumperBGen get b => const $AssetsImagesSparkyBumperBGen();
$AssetsImagesSparkyBumperCGen get c => const $AssetsImagesSparkyBumperCGen();
}
class $AssetsImagesSparkyComputerGen {
const $AssetsImagesSparkyComputerGen();
/// File path: assets/images/sparky/computer/base.png
AssetGenImage get base =>
const AssetGenImage('assets/images/sparky/computer/base.png');
/// File path: assets/images/sparky/computer/top.png
AssetGenImage get top =>
const AssetGenImage('assets/images/sparky/computer/top.png');
}
class $AssetsImagesSparkyBumperAGen {
const $AssetsImagesSparkyBumperAGen();
/// File path: assets/images/sparky_bumper/a/active.png
/// File path: assets/images/sparky/bumper/a/active.png
AssetGenImage get active =>
const AssetGenImage('assets/images/sparky_bumper/a/active.png');
const AssetGenImage('assets/images/sparky/bumper/a/active.png');
/// File path: assets/images/sparky_bumper/a/inactive.png
/// File path: assets/images/sparky/bumper/a/inactive.png
AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky_bumper/a/inactive.png');
const AssetGenImage('assets/images/sparky/bumper/a/inactive.png');
}
class $AssetsImagesSparkyBumperBGen {
const $AssetsImagesSparkyBumperBGen();
/// File path: assets/images/sparky_bumper/b/active.png
/// File path: assets/images/sparky/bumper/b/active.png
AssetGenImage get active =>
const AssetGenImage('assets/images/sparky_bumper/b/active.png');
const AssetGenImage('assets/images/sparky/bumper/b/active.png');
/// File path: assets/images/sparky_bumper/b/inactive.png
/// File path: assets/images/sparky/bumper/b/inactive.png
AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky_bumper/b/inactive.png');
const AssetGenImage('assets/images/sparky/bumper/b/inactive.png');
}
class $AssetsImagesSparkyBumperCGen {
const $AssetsImagesSparkyBumperCGen();
/// File path: assets/images/sparky_bumper/c/active.png
/// File path: assets/images/sparky/bumper/c/active.png
AssetGenImage get active =>
const AssetGenImage('assets/images/sparky_bumper/c/active.png');
const AssetGenImage('assets/images/sparky/bumper/c/active.png');
/// File path: assets/images/sparky_bumper/c/inactive.png
/// File path: assets/images/sparky/bumper/c/inactive.png
AssetGenImage get inactive =>
const AssetGenImage('assets/images/sparky_bumper/c/inactive.png');
const AssetGenImage('assets/images/sparky/bumper/c/inactive.png');
}
class Assets {

@ -44,6 +44,8 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
anchor: Anchor.center,
)..tint(tint),
);
renderBody = false;
}
@override
@ -64,15 +66,19 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
///
/// The [Ball] will no longer be affected by any forces, including it's
/// weight and those emitted from collisions.
//TODO(allisonryan0002): prevent motion from contact with other balls.
void stop() {
body.setType(BodyType.static);
body
..gravityScale = 0
..linearVelocity = Vector2.zero()
..angularVelocity = 0;
}
/// Allows the [Ball] to be affected by forces.
///
/// If previously [stop]ed, the previous ball's velocity is not kept.
void resume() {
body.setType(BodyType.dynamic);
body.gravityScale = 1;
}
@override
@ -96,7 +102,7 @@ class Ball<T extends Forge2DGame> extends BodyComponent<T>
/// Applies a boost on this [Ball].
void boost(Vector2 impulse) {
body.applyLinearImpulse(impulse);
body.linearVelocity = impulse;
_boostTimer = _boostDuration;
}

@ -20,3 +20,4 @@ export 'spaceship.dart';
export 'spaceship_rail.dart';
export 'spaceship_ramp.dart';
export 'sparky_bumper.dart';
export 'sparky_computer.dart';

@ -27,8 +27,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
: this._(
majorRadius: 2.9,
minorRadius: 2.1,
activeAssetPath: Assets.images.sparkyBumper.a.active.keyName,
inactiveAssetPath: Assets.images.sparkyBumper.a.inactive.keyName,
activeAssetPath: Assets.images.sparky.bumper.a.active.keyName,
inactiveAssetPath: Assets.images.sparky.bumper.a.inactive.keyName,
spriteComponent: SpriteComponent(
anchor: Anchor.center,
position: Vector2(0, -0.25),
@ -40,8 +40,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
: this._(
majorRadius: 2.85,
minorRadius: 2,
activeAssetPath: Assets.images.sparkyBumper.b.active.keyName,
inactiveAssetPath: Assets.images.sparkyBumper.b.inactive.keyName,
activeAssetPath: Assets.images.sparky.bumper.b.active.keyName,
inactiveAssetPath: Assets.images.sparky.bumper.b.inactive.keyName,
spriteComponent: SpriteComponent(
anchor: Anchor.center,
position: Vector2(0, -0.35),
@ -53,8 +53,8 @@ class SparkyBumper extends BodyComponent with InitialPosition {
: this._(
majorRadius: 3,
minorRadius: 2.2,
activeAssetPath: Assets.images.sparkyBumper.c.active.keyName,
inactiveAssetPath: Assets.images.sparkyBumper.c.inactive.keyName,
activeAssetPath: Assets.images.sparky.bumper.c.active.keyName,
inactiveAssetPath: Assets.images.sparky.bumper.c.inactive.keyName,
spriteComponent: SpriteComponent(
anchor: Anchor.center,
position: Vector2(0, -0.4),

@ -0,0 +1,109 @@
// ignore_for_file: avoid_renaming_method_parameters
import 'package:flame/components.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:pinball_components/pinball_components.dart';
/// {@template sparky_computer}
/// A [Blueprint] which creates the [_ComputerBase] and [_ComputerTop].
/// {@endtemplate}
class SparkyComputer extends Forge2DBlueprint {
@override
void build(_) {
final computerBase = _ComputerBase();
final computerTop = _ComputerTop();
addAll([
computerBase,
computerTop,
]);
}
}
class _ComputerBase extends BodyComponent with InitialPosition {
_ComputerBase();
List<FixtureDef> _createFixtureDefs() {
final fixturesDef = <FixtureDef>[];
final leftEdge = EdgeShape()
..set(
Vector2(-15.3, 46),
Vector2(-15.7, 49.6),
);
final leftEdgeFixtureDef = FixtureDef(leftEdge);
fixturesDef.add(leftEdgeFixtureDef);
final topEdge = EdgeShape()
..set(
Vector2(-15.7, 49.6),
Vector2(-11.1, 50.6),
);
final topEdgeFixtureDef = FixtureDef(topEdge);
fixturesDef.add(topEdgeFixtureDef);
final rightEdge = EdgeShape()
..set(
Vector2(-11.1, 50.6),
Vector2(-9.4, 47.2),
);
final rightEdgeFixtureDef = FixtureDef(rightEdge);
fixturesDef.add(rightEdgeFixtureDef);
return fixturesDef;
}
@override
Body createBody() {
final bodyDef = BodyDef()
..userData = this
..position = initialPosition;
final body = world.createBody(bodyDef);
_createFixtureDefs().forEach(body.createFixture);
return body;
}
@override
Future<void> onLoad() async {
await super.onLoad();
await _loadSprite();
renderBody = false;
}
Future<void> _loadSprite() async {
final sprite = await gameRef.loadSprite(
Assets.images.sparky.computer.base.keyName,
);
await add(
SpriteComponent(
sprite: sprite,
size: sprite.originalSize / 10,
anchor: Anchor.center,
position: Vector2(-12.35, -48.35),
),
);
}
}
class _ComputerTop extends SpriteComponent with HasGameRef {
_ComputerTop()
: super(
anchor: Anchor.center,
position: Vector2(-12.85, -49.75),
priority: 1,
);
@override
Future<void> onLoad() async {
await super.onLoad();
final sprite = await gameRef.loadSprite(
Assets.images.sparky.computer.top.keyName,
);
this.sprite = sprite;
size = sprite.originalSize / 10;
}
}

@ -12,7 +12,8 @@ const _attachedErrorMessage = "Can't add to attached Blueprints";
/// A [Blueprint] is a virtual way of grouping [Component]s
/// that are related, but they need to be added directly on
/// the [FlameGame] level.
abstract class Blueprint<T extends FlameGame> {
// TODO(alestiago): refactor with feat/make-blueprint-extend-component.
abstract class Blueprint<T extends FlameGame> extends Component {
final List<Component> _components = [];
final List<Blueprint> _blueprints = [];
@ -34,14 +35,9 @@ abstract class Blueprint<T extends FlameGame> {
_isAttached = true;
}
/// Adds a list of [Component]s to this blueprint.
void addAll(List<Component> components) {
assert(!_isAttached, _attachedErrorMessage);
_components.addAll(components);
}
/// Adds a single [Component] to this blueprint.
void add(Component component) {
@override
Future<void> add(Component component) async {
assert(!_isAttached, _attachedErrorMessage);
_components.add(component);
}

@ -39,9 +39,10 @@ flutter:
- assets/images/spaceship/ramp/
- assets/images/chrome_dino/
- assets/images/kicker/
- assets/images/sparky_bumper/a/
- assets/images/sparky_bumper/b/
- assets/images/sparky_bumper/c/
- assets/images/sparky/computer/
- assets/images/sparky/bumper/a/
- assets/images/sparky/bumper/b/
- assets/images/sparky/bumper/c/
flutter_gen:
line_length: 80

Loading…
Cancel
Save