Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 200 KiB |
@ -1,39 +1,36 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template android_acres}
|
||||
/// Area positioned on the left side of the board containing the
|
||||
/// [AndroidSpaceship], [SpaceshipRamp], [SpaceshipRail], and [AndroidBumper]s.
|
||||
/// {@endtemplate}
|
||||
class AndroidAcres extends Blueprint {
|
||||
class AndroidAcres extends Component {
|
||||
/// {@macro android_acres}
|
||||
AndroidAcres()
|
||||
: super(
|
||||
components: [
|
||||
children: [
|
||||
SpaceshipRamp(),
|
||||
SpaceshipRail(),
|
||||
AndroidSpaceship(position: Vector2(-26.5, -28.5)),
|
||||
AndroidBumper.a(
|
||||
children: [
|
||||
ScoringBehavior(points: 20000),
|
||||
ScoringBehavior(points: Points.twentyThousand),
|
||||
],
|
||||
)..initialPosition = Vector2(-25, 1.3),
|
||||
AndroidBumper.b(
|
||||
children: [
|
||||
ScoringBehavior(points: 20000),
|
||||
ScoringBehavior(points: Points.twentyThousand),
|
||||
],
|
||||
)..initialPosition = Vector2(-32.8, -9.2),
|
||||
AndroidBumper.cow(
|
||||
children: [
|
||||
ScoringBehavior(points: 20),
|
||||
ScoringBehavior(points: Points.twentyThousand),
|
||||
],
|
||||
)..initialPosition = Vector2(-20.5, -13.8),
|
||||
],
|
||||
blueprints: [
|
||||
SpaceshipRamp(),
|
||||
AndroidSpaceship(position: Vector2(-26.5, -28.5)),
|
||||
SpaceshipRail(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -1,23 +1,39 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template dino_desert}
|
||||
/// Area located next to the [Launcher] containing the [ChromeDino] and
|
||||
/// [DinoWalls].
|
||||
/// {@endtemplate}
|
||||
// TODO(allisonryan0002): use a controller to initiate dino bonus when dino is
|
||||
// fully implemented.
|
||||
class DinoDesert extends Blueprint {
|
||||
class DinoDesert extends Component {
|
||||
/// {@macro dino_desert}
|
||||
DinoDesert()
|
||||
: super(
|
||||
components: [
|
||||
ChromeDino()..initialPosition = Vector2(12.3, -6.9),
|
||||
children: [
|
||||
ChromeDino(
|
||||
children: [
|
||||
ScoringBehavior(points: Points.twoHundredThousand)
|
||||
..applyTo(['inside_mouth']),
|
||||
],
|
||||
blueprints: [
|
||||
)..initialPosition = Vector2(12.6, -6.9),
|
||||
_BarrierBehindDino(),
|
||||
DinoWalls(),
|
||||
Slingshots(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class _BarrierBehindDino extends BodyComponent {
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = EdgeShape()
|
||||
..set(
|
||||
Vector2(25, -14.2),
|
||||
Vector2(25, -7.7),
|
||||
);
|
||||
|
||||
return world.createBody(BodyDef())..createFixtureFromShape(shape);
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,20 @@
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball/game/components/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart' hide Assets;
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template launcher}
|
||||
/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and
|
||||
/// [LaunchRamp].
|
||||
/// Channel on the right side of the board containing the [LaunchRamp],
|
||||
/// [Plunger], and [RocketSpriteComponent].
|
||||
/// {@endtemplate}
|
||||
class Launcher extends Blueprint {
|
||||
class Launcher extends Component {
|
||||
/// {@macro launcher}
|
||||
Launcher()
|
||||
: super(
|
||||
components: [
|
||||
ControlledPlunger(compressionDistance: 10.5)
|
||||
..initialPosition = Vector2(41.1, 43),
|
||||
children: [
|
||||
LaunchRamp(),
|
||||
ControlledPlunger(compressionDistance: 9.2)
|
||||
..initialPosition = Vector2(41.2, 43.7),
|
||||
RocketSpriteComponent()..position = Vector2(43, 62.3),
|
||||
],
|
||||
blueprints: [LaunchRamp()],
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
export 'widgets/widgets.dart';
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 313 B |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 343 B |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 156 KiB |
@ -1,202 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart' hide Timer;
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template chrome_dino}
|
||||
/// Dino that swivels back and forth, opening its mouth to eat a [Ball].
|
||||
///
|
||||
/// Upon eating a [Ball], the dino rotates and spits the [Ball] out in a
|
||||
/// different direction.
|
||||
/// {@endtemplate}
|
||||
class ChromeDino extends BodyComponent with InitialPosition {
|
||||
/// {@macro chrome_dino}
|
||||
ChromeDino()
|
||||
: super(
|
||||
priority: RenderPriority.dino,
|
||||
renderBody: false,
|
||||
);
|
||||
|
||||
/// The size of the dinosaur mouth.
|
||||
static final size = Vector2(5.5, 5);
|
||||
|
||||
/// Anchors the [ChromeDino] to the [RevoluteJoint] that controls its arc
|
||||
/// motion.
|
||||
Future<_ChromeDinoJoint> _anchorToJoint() async {
|
||||
// TODO(allisonryan0002): try moving to anchor after new body is defined.
|
||||
final anchor = _ChromeDinoAnchor()
|
||||
..initialPosition = initialPosition + Vector2(9, -4);
|
||||
|
||||
await add(anchor);
|
||||
|
||||
final jointDef = _ChromeDinoAnchorRevoluteJointDef(
|
||||
chromeDino: this,
|
||||
anchor: anchor,
|
||||
);
|
||||
final joint = _ChromeDinoJoint(jointDef);
|
||||
world.createJoint(joint);
|
||||
|
||||
return joint;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final joint = await _anchorToJoint();
|
||||
const framesInAnimation = 98;
|
||||
const animationFPS = 1 / 24;
|
||||
await add(
|
||||
TimerComponent(
|
||||
period: (framesInAnimation / 2) * animationFPS,
|
||||
onTick: joint._swivel,
|
||||
repeat: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<FixtureDef> _createFixtureDefs() {
|
||||
final fixtureDefs = <FixtureDef>[];
|
||||
|
||||
// TODO(allisonryan0002): Update this shape to better match sprite.
|
||||
final box = PolygonShape()
|
||||
..setAsBox(
|
||||
size.x / 2,
|
||||
size.y / 2,
|
||||
initialPosition + Vector2(-4, 2),
|
||||
-_ChromeDinoJoint._halfSweepingAngle,
|
||||
);
|
||||
final fixtureDef = FixtureDef(box, density: 1);
|
||||
fixtureDefs.add(fixtureDef);
|
||||
|
||||
return fixtureDefs;
|
||||
}
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final bodyDef = BodyDef(
|
||||
position: initialPosition,
|
||||
type: BodyType.dynamic,
|
||||
gravityScale: Vector2.zero(),
|
||||
);
|
||||
|
||||
final body = world.createBody(bodyDef);
|
||||
_createFixtureDefs().forEach(body.createFixture);
|
||||
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
class _ChromeDinoAnchor extends JointAnchor {
|
||||
_ChromeDinoAnchor();
|
||||
|
||||
// TODO(allisonryan0002): if these aren't moved when fixing the rendering, see
|
||||
// if the joint can be created in onMount to resolve render syncing.
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
await addAll([
|
||||
_ChromeDinoMouthSprite(),
|
||||
_ChromeDinoHeadSprite(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template chrome_dino_anchor_revolute_joint_def}
|
||||
/// Hinges a [ChromeDino] to a [_ChromeDinoAnchor].
|
||||
/// {@endtemplate}
|
||||
class _ChromeDinoAnchorRevoluteJointDef extends RevoluteJointDef {
|
||||
/// {@macro chrome_dino_anchor_revolute_joint_def}
|
||||
_ChromeDinoAnchorRevoluteJointDef({
|
||||
required ChromeDino chromeDino,
|
||||
required _ChromeDinoAnchor anchor,
|
||||
}) {
|
||||
initialize(
|
||||
chromeDino.body,
|
||||
anchor.body,
|
||||
chromeDino.body.position + anchor.body.position,
|
||||
);
|
||||
enableLimit = true;
|
||||
lowerAngle = -_ChromeDinoJoint._halfSweepingAngle;
|
||||
upperAngle = _ChromeDinoJoint._halfSweepingAngle;
|
||||
|
||||
enableMotor = true;
|
||||
maxMotorTorque = chromeDino.body.mass * 255;
|
||||
motorSpeed = 2;
|
||||
}
|
||||
}
|
||||
|
||||
class _ChromeDinoJoint extends RevoluteJoint {
|
||||
_ChromeDinoJoint(_ChromeDinoAnchorRevoluteJointDef def) : super(def);
|
||||
|
||||
static const _halfSweepingAngle = 0.1143;
|
||||
|
||||
/// Sweeps the [ChromeDino] up and down repeatedly.
|
||||
void _swivel() {
|
||||
setMotorSpeed(-motorSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
class _ChromeDinoMouthSprite extends SpriteAnimationComponent with HasGameRef {
|
||||
_ChromeDinoMouthSprite()
|
||||
: super(
|
||||
anchor: Anchor(Anchor.center.x + 0.47, Anchor.center.y - 0.29),
|
||||
angle: _ChromeDinoJoint._halfSweepingAngle,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final image = gameRef.images.fromCache(
|
||||
Assets.images.dino.animatronic.mouth.keyName,
|
||||
);
|
||||
|
||||
const amountPerRow = 11;
|
||||
const amountPerColumn = 9;
|
||||
final textureSize = Vector2(
|
||||
image.width / amountPerRow,
|
||||
image.height / amountPerColumn,
|
||||
);
|
||||
size = textureSize / 10;
|
||||
|
||||
final data = SpriteAnimationData.sequenced(
|
||||
amount: (amountPerColumn * amountPerRow) - 1,
|
||||
amountPerRow: amountPerRow,
|
||||
stepTime: 1 / 24,
|
||||
textureSize: textureSize,
|
||||
);
|
||||
animation = SpriteAnimation.fromFrameData(image, data)..currentIndex = 45;
|
||||
}
|
||||
}
|
||||
|
||||
class _ChromeDinoHeadSprite extends SpriteAnimationComponent with HasGameRef {
|
||||
_ChromeDinoHeadSprite()
|
||||
: super(
|
||||
anchor: Anchor(Anchor.center.x + 0.47, Anchor.center.y - 0.29),
|
||||
angle: _ChromeDinoJoint._halfSweepingAngle,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final image = gameRef.images.fromCache(
|
||||
Assets.images.dino.animatronic.head.keyName,
|
||||
);
|
||||
|
||||
const amountPerRow = 11;
|
||||
const amountPerColumn = 9;
|
||||
final textureSize = Vector2(
|
||||
image.width / amountPerRow,
|
||||
image.height / amountPerColumn,
|
||||
);
|
||||
size = textureSize / 10;
|
||||
|
||||
final data = SpriteAnimationData.sequenced(
|
||||
amount: (amountPerColumn * amountPerRow) - 1,
|
||||
amountPerRow: amountPerRow,
|
||||
stepTime: 1 / 24,
|
||||
textureSize: textureSize,
|
||||
);
|
||||
animation = SpriteAnimation.fromFrameData(image, data)..currentIndex = 45;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export 'chrome_dino_chomping_behavior.dart';
|
||||
export 'chrome_dino_mouth_opening_behavior.dart';
|
||||
export 'chrome_dino_spitting_behavior.dart';
|
||||
export 'chrome_dino_swiveling_behavior.dart';
|
@ -0,0 +1,20 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template chrome_dino_chomping_behavior}
|
||||
/// Chomps a [Ball] after it has entered the [ChromeDino]'s mouth.
|
||||
///
|
||||
/// The chomped [Ball] is hidden in the mouth until it is spit out.
|
||||
/// {@endtemplate}
|
||||
class ChromeDinoChompingBehavior extends ContactBehavior<ChromeDino> {
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
if (other is! Ball) return;
|
||||
|
||||
other.firstChild<SpriteComponent>()!.setOpacity(0);
|
||||
parent.bloc.onChomp(other);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template chrome_dino_mouth_opening_behavior}
|
||||
/// Allows a [Ball] to enter the [ChromeDino] mouth when it is open.
|
||||
/// {@endtemplate}
|
||||
class ChromeDinoMouthOpeningBehavior extends ContactBehavior<ChromeDino> {
|
||||
@override
|
||||
void preSolve(Object other, Contact contact, Manifold oldManifold) {
|
||||
super.preSolve(other, contact, oldManifold);
|
||||
if (other is! Ball) return;
|
||||
|
||||
if (parent.bloc.state.isMouthOpen && parent.firstChild<Ball>() == null) {
|
||||
contact.setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template chrome_dino_spitting_behavior}
|
||||
/// Spits the [Ball] from the [ChromeDino] the next time the mouth opens.
|
||||
/// {@endtemplate}
|
||||
class ChromeDinoSpittingBehavior extends Component
|
||||
with ContactCallbacks, ParentIsA<ChromeDino> {
|
||||
bool _waitingForSwivel = true;
|
||||
|
||||
void _onNewState(ChromeDinoState state) {
|
||||
if (state.status == ChromeDinoStatus.chomping) {
|
||||
if (state.isMouthOpen && !_waitingForSwivel) {
|
||||
add(
|
||||
TimerComponent(
|
||||
period: 0.4,
|
||||
onTick: _spit,
|
||||
removeOnFinish: true,
|
||||
),
|
||||
);
|
||||
_waitingForSwivel = true;
|
||||
}
|
||||
if (_waitingForSwivel && !state.isMouthOpen) {
|
||||
_waitingForSwivel = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _spit() {
|
||||
parent.bloc.state.ball!
|
||||
..firstChild<SpriteComponent>()!.setOpacity(1)
|
||||
..body.linearVelocity = Vector2(-50, 0);
|
||||
parent.bloc.onSpit();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
parent.bloc.stream.listen(_onNewState);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template chrome_dino_swivel_behavior}
|
||||
/// Swivels the [ChromeDino] up and down periodically to match its animation
|
||||
/// sequence.
|
||||
/// {@endtemplate}
|
||||
class ChromeDinoSwivelingBehavior extends TimerComponent
|
||||
with ParentIsA<ChromeDino> {
|
||||
/// {@macro chrome_dino_swivel_behavior}
|
||||
ChromeDinoSwivelingBehavior()
|
||||
: super(
|
||||
period: 98 / 48,
|
||||
repeat: true,
|
||||
);
|
||||
|
||||
late final RevoluteJoint _joint;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
final anchor = _ChromeDinoAnchor()
|
||||
..initialPosition = parent.initialPosition + Vector2(9, -4);
|
||||
await add(anchor);
|
||||
|
||||
final jointDef = _ChromeDinoAnchorRevoluteJointDef(
|
||||
chromeDino: parent,
|
||||
anchor: anchor,
|
||||
);
|
||||
_joint = RevoluteJoint(jointDef);
|
||||
parent.world.createJoint(_joint);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
final angle = _joint.jointAngle();
|
||||
|
||||
if (angle < _joint.upperLimit &&
|
||||
angle > _joint.lowerLimit &&
|
||||
parent.bloc.state.isMouthOpen) {
|
||||
parent.bloc.onCloseMouth();
|
||||
} else if ((angle >= _joint.upperLimit || angle <= _joint.lowerLimit) &&
|
||||
!parent.bloc.state.isMouthOpen) {
|
||||
parent.bloc.onOpenMouth();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onTick() {
|
||||
super.onTick();
|
||||
_joint.setMotorSpeed(-_joint.motorSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
class _ChromeDinoAnchor extends JointAnchor
|
||||
with ParentIsA<ChromeDinoSwivelingBehavior> {
|
||||
@override
|
||||
void onMount() {
|
||||
super.onMount();
|
||||
parent.parent.children
|
||||
.whereType<SpriteAnimationComponent>()
|
||||
.forEach((sprite) {
|
||||
sprite.animation!.currentIndex = 45;
|
||||
sprite.changeParent(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _ChromeDinoAnchorRevoluteJointDef extends RevoluteJointDef {
|
||||
_ChromeDinoAnchorRevoluteJointDef({
|
||||
required ChromeDino chromeDino,
|
||||
required _ChromeDinoAnchor anchor,
|
||||
}) {
|
||||
initialize(
|
||||
chromeDino.body,
|
||||
anchor.body,
|
||||
chromeDino.body.position + anchor.body.position,
|
||||
);
|
||||
enableLimit = true;
|
||||
lowerAngle = -ChromeDino.halfSweepingAngle;
|
||||
upperAngle = ChromeDino.halfSweepingAngle;
|
||||
|
||||
enableMotor = true;
|
||||
maxMotorTorque = chromeDino.body.mass * 255;
|
||||
motorSpeed = 2;
|
||||
}
|
||||
}
|
@ -0,0 +1,207 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart' hide Timer;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_components/src/components/chrome_dino/behaviors/behaviors.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
export 'cubit/chrome_dino_cubit.dart';
|
||||
|
||||
/// {@template chrome_dino}
|
||||
/// Dino that swivels back and forth, opening its mouth to eat a [Ball].
|
||||
///
|
||||
/// Upon eating a [Ball], the dino rotates and spits the [Ball] out in the
|
||||
/// opposite direction.
|
||||
/// {@endtemplate}
|
||||
class ChromeDino extends BodyComponent
|
||||
with InitialPosition, ContactCallbacks, ZIndex {
|
||||
/// {@macro chrome_dino}
|
||||
ChromeDino({Iterable<Component>? children})
|
||||
: bloc = ChromeDinoCubit(),
|
||||
super(
|
||||
children: [
|
||||
_ChromeDinoMouthSprite(),
|
||||
_ChromeDinoHeadSprite(),
|
||||
ChromeDinoMouthOpeningBehavior()..applyTo(['mouth_opening']),
|
||||
ChromeDinoSwivelingBehavior(),
|
||||
ChromeDinoChompingBehavior()..applyTo(['inside_mouth']),
|
||||
ChromeDinoSpittingBehavior(),
|
||||
...?children,
|
||||
],
|
||||
renderBody: false,
|
||||
) {
|
||||
zIndex = ZIndexes.dino;
|
||||
}
|
||||
|
||||
/// Creates a [ChromeDino] without any children.
|
||||
///
|
||||
/// This can be used for testing [ChromeDino]'s behaviors in isolation.
|
||||
// TODO(alestiago): Refactor injecting bloc once the following is merged:
|
||||
// https://github.com/flame-engine/flame/pull/1538
|
||||
@visibleForTesting
|
||||
ChromeDino.test({
|
||||
required this.bloc,
|
||||
});
|
||||
|
||||
// TODO(alestiago): Consider refactoring once the following is merged:
|
||||
// https://github.com/flame-engine/flame/pull/1538
|
||||
// ignore: public_member_api_docs
|
||||
final ChromeDinoCubit bloc;
|
||||
|
||||
/// Angle to rotate the dino up or down from the starting horizontal position.
|
||||
static const halfSweepingAngle = 0.1143;
|
||||
|
||||
@override
|
||||
void onRemove() {
|
||||
bloc.close();
|
||||
super.onRemove();
|
||||
}
|
||||
|
||||
List<FixtureDef> _createFixtureDefs() {
|
||||
const mouthAngle = -(halfSweepingAngle + 0.28);
|
||||
final size = Vector2(5.5, 6);
|
||||
|
||||
final topEdge = PolygonShape()
|
||||
..setAsBox(
|
||||
size.x / 2,
|
||||
0.1,
|
||||
initialPosition + Vector2(-4.2, -1.4),
|
||||
mouthAngle,
|
||||
);
|
||||
final topEdgeFixtureDef = FixtureDef(topEdge, density: 100);
|
||||
|
||||
final backEdge = PolygonShape()
|
||||
..setAsBox(
|
||||
0.1,
|
||||
size.y / 2,
|
||||
initialPosition + Vector2(-1.3, 0.5),
|
||||
-halfSweepingAngle,
|
||||
);
|
||||
final backEdgeFixtureDef = FixtureDef(backEdge, density: 100);
|
||||
|
||||
final bottomEdge = PolygonShape()
|
||||
..setAsBox(
|
||||
size.x / 2,
|
||||
0.1,
|
||||
initialPosition + Vector2(-3.5, 4.7),
|
||||
mouthAngle,
|
||||
);
|
||||
final bottomEdgeFixtureDef = FixtureDef(
|
||||
bottomEdge,
|
||||
density: 100,
|
||||
);
|
||||
|
||||
final mouthOpeningEdge = PolygonShape()
|
||||
..setAsBox(
|
||||
0.1,
|
||||
size.y / 2.5,
|
||||
initialPosition + Vector2(-6.4, 2.7),
|
||||
-halfSweepingAngle,
|
||||
);
|
||||
final mouthOpeningEdgeFixtureDef = FixtureDef(
|
||||
mouthOpeningEdge,
|
||||
density: 0.1,
|
||||
userData: 'mouth_opening',
|
||||
);
|
||||
|
||||
final insideSensor = PolygonShape()
|
||||
..setAsBox(
|
||||
0.2,
|
||||
0.2,
|
||||
initialPosition + Vector2(-3.5, 1.5),
|
||||
0,
|
||||
);
|
||||
final insideSensorFixtureDef = FixtureDef(
|
||||
insideSensor,
|
||||
isSensor: true,
|
||||
userData: 'inside_mouth',
|
||||
);
|
||||
|
||||
return [
|
||||
topEdgeFixtureDef,
|
||||
backEdgeFixtureDef,
|
||||
bottomEdgeFixtureDef,
|
||||
mouthOpeningEdgeFixtureDef,
|
||||
insideSensorFixtureDef,
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final bodyDef = BodyDef(
|
||||
position: initialPosition,
|
||||
type: BodyType.dynamic,
|
||||
gravityScale: Vector2.zero(),
|
||||
);
|
||||
final body = world.createBody(bodyDef);
|
||||
_createFixtureDefs().forEach(body.createFixture);
|
||||
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
class _ChromeDinoMouthSprite extends SpriteAnimationComponent with HasGameRef {
|
||||
_ChromeDinoMouthSprite()
|
||||
: super(
|
||||
anchor: Anchor(Anchor.center.x + 0.47, Anchor.center.y - 0.29),
|
||||
angle: ChromeDino.halfSweepingAngle,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final image = gameRef.images.fromCache(
|
||||
Assets.images.dino.animatronic.mouth.keyName,
|
||||
);
|
||||
|
||||
const amountPerRow = 11;
|
||||
const amountPerColumn = 9;
|
||||
final textureSize = Vector2(
|
||||
image.width / amountPerRow,
|
||||
image.height / amountPerColumn,
|
||||
);
|
||||
size = textureSize / 10;
|
||||
|
||||
final data = SpriteAnimationData.sequenced(
|
||||
amount: (amountPerColumn * amountPerRow) - 1,
|
||||
amountPerRow: amountPerRow,
|
||||
stepTime: 1 / 24,
|
||||
textureSize: textureSize,
|
||||
);
|
||||
animation = SpriteAnimation.fromFrameData(image, data);
|
||||
}
|
||||
}
|
||||
|
||||
class _ChromeDinoHeadSprite extends SpriteAnimationComponent with HasGameRef {
|
||||
_ChromeDinoHeadSprite()
|
||||
: super(
|
||||
anchor: Anchor(Anchor.center.x + 0.47, Anchor.center.y - 0.29),
|
||||
angle: ChromeDino.halfSweepingAngle,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final image = gameRef.images.fromCache(
|
||||
Assets.images.dino.animatronic.head.keyName,
|
||||
);
|
||||
|
||||
const amountPerRow = 11;
|
||||
const amountPerColumn = 9;
|
||||
final textureSize = Vector2(
|
||||
image.width / amountPerRow,
|
||||
image.height / amountPerColumn,
|
||||
);
|
||||
size = textureSize / 10;
|
||||
|
||||
final data = SpriteAnimationData.sequenced(
|
||||
amount: (amountPerColumn * amountPerRow) - 1,
|
||||
amountPerRow: amountPerRow,
|
||||
stepTime: 1 / 24,
|
||||
textureSize: textureSize,
|
||||
);
|
||||
animation = SpriteAnimation.fromFrameData(image, data);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
part 'chrome_dino_state.dart';
|
||||
|
||||
class ChromeDinoCubit extends Cubit<ChromeDinoState> {
|
||||
ChromeDinoCubit() : super(const ChromeDinoState.inital());
|
||||
|
||||
void onOpenMouth() {
|
||||
emit(state.copyWith(isMouthOpen: true));
|
||||
}
|
||||
|
||||
void onCloseMouth() {
|
||||
emit(state.copyWith(isMouthOpen: false));
|
||||
}
|
||||
|
||||
void onChomp(Ball ball) {
|
||||
emit(state.copyWith(status: ChromeDinoStatus.chomping, ball: ball));
|
||||
}
|
||||
|
||||
void onSpit() {
|
||||
emit(state.copyWith(status: ChromeDinoStatus.idle));
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
part of 'chrome_dino_cubit.dart';
|
||||
|
||||
enum ChromeDinoStatus {
|
||||
idle,
|
||||
chomping,
|
||||
}
|
||||
|
||||
class ChromeDinoState extends Equatable {
|
||||
const ChromeDinoState({
|
||||
required this.status,
|
||||
required this.isMouthOpen,
|
||||
this.ball,
|
||||
});
|
||||
|
||||
const ChromeDinoState.inital()
|
||||
: this(
|
||||
status: ChromeDinoStatus.idle,
|
||||
isMouthOpen: false,
|
||||
);
|
||||
|
||||
final ChromeDinoStatus status;
|
||||
final bool isMouthOpen;
|
||||
final Ball? ball;
|
||||
|
||||
ChromeDinoState copyWith({
|
||||
ChromeDinoStatus? status,
|
||||
bool? isMouthOpen,
|
||||
Ball? ball,
|
||||
}) {
|
||||
final state = ChromeDinoState(
|
||||
status: status ?? this.status,
|
||||
isMouthOpen: isMouthOpen ?? this.isMouthOpen,
|
||||
ball: ball ?? this.ball,
|
||||
);
|
||||
return state;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
status,
|
||||
isMouthOpen,
|
||||
ball,
|
||||
];
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
part of 'google_letter_cubit.dart';
|
||||
|
||||
/// Indicates the [GoogleLetterCubit]'s current state.
|
||||
enum GoogleLetterState {
|
||||
/// A lit up letter.
|
||||
active,
|
||||
|
||||
/// A dimmed letter.
|
||||
inactive,
|
||||
lit,
|
||||
dimmed,
|
||||
}
|
||||
|
@ -0,0 +1,2 @@
|
||||
export 'kicker_ball_contact_behavior.dart';
|
||||
export 'kicker_blinking_behavior.dart';
|
@ -0,0 +1,14 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
class KickerBallContactBehavior extends ContactBehavior<Kicker> {
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
if (other is! Ball) return;
|
||||
parent.bloc.onBallContacted();
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
/// {@template kicker_blinking_behavior}
|
||||
/// Makes a [Kicker] blink back to [KickerState.lit] when [KickerState.dimmed].
|
||||
/// {@endtemplate}
|
||||
class KickerBlinkingBehavior extends TimerComponent with ParentIsA<Kicker> {
|
||||
/// {@macro kicker_blinking_behavior}
|
||||
KickerBlinkingBehavior() : super(period: 0.05);
|
||||
|
||||
void _onNewState(KickerState state) {
|
||||
switch (state) {
|
||||
case KickerState.lit:
|
||||
break;
|
||||
case KickerState.dimmed:
|
||||
timer
|
||||
..reset()
|
||||
..start();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
timer.stop();
|
||||
parent.bloc.stream.listen(_onNewState);
|
||||
}
|
||||
|
||||
@override
|
||||
void onTick() {
|
||||
super.onTick();
|
||||
timer.stop();
|
||||
parent.bloc.onBlinked();
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
|
||||
part 'kicker_state.dart';
|
||||
|
||||
class KickerCubit extends Cubit<KickerState> {
|
||||
KickerCubit() : super(KickerState.lit);
|
||||
|
||||
void onBallContacted() {
|
||||
emit(KickerState.dimmed);
|
||||
}
|
||||
|
||||
void onBlinked() {
|
||||
emit(KickerState.lit);
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
part of 'kicker_cubit.dart';
|
||||
|
||||
enum KickerState {
|
||||
lit,
|
||||
dimmed,
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template render_priority}
|
||||
/// Priorities for the component rendering order in the pinball game.
|
||||
/// {@endtemplate}
|
||||
// TODO(allisonryan0002): find alternative to section comments.
|
||||
abstract class RenderPriority {
|
||||
static const _base = 0;
|
||||
static const _above = 1;
|
||||
static const _below = -1;
|
||||
|
||||
// Ball
|
||||
|
||||
/// Render priority for the [Ball] while it's on the board.
|
||||
static const int ballOnBoard = _base;
|
||||
|
||||
/// Render priority for the [Ball] while it's on the [SpaceshipRamp].
|
||||
static const int ballOnSpaceshipRamp =
|
||||
_above + spaceshipRampBackgroundRailing;
|
||||
|
||||
/// Render priority for the [Ball] while it's on the [AndroidSpaceship].
|
||||
static const int ballOnSpaceship = _above + spaceshipSaucer;
|
||||
|
||||
/// Render priority for the [Ball] while it's on the [SpaceshipRail].
|
||||
static const int ballOnSpaceshipRail = _above + spaceshipRail;
|
||||
|
||||
/// Render priority for the [Ball] while it's on the [LaunchRamp].
|
||||
static const int ballOnLaunchRamp = launchRamp;
|
||||
|
||||
// Background
|
||||
|
||||
// TODO(allisonryan0002): fix this magic priority. Could bump all priorities
|
||||
// so there are no negatives.
|
||||
static const int boardBackground = 3 * _below + _base;
|
||||
|
||||
// Boundaries
|
||||
|
||||
static const int bottomBoundary = _above + dinoBottomWall;
|
||||
|
||||
static const int outerBoundary = _above + boardBackground;
|
||||
|
||||
static const int outerBottomBoundary = _above + rocket;
|
||||
|
||||
// Bottom Group
|
||||
|
||||
static const int bottomGroup = _above + ballOnBoard;
|
||||
|
||||
// Launcher
|
||||
|
||||
static const int launchRamp = _above + outerBoundary;
|
||||
|
||||
static const int launchRampForegroundRailing = ballOnBoard;
|
||||
|
||||
static const int plunger = _above + launchRamp;
|
||||
|
||||
static const int rocket = _below + bottomBoundary;
|
||||
|
||||
// Dino Land
|
||||
|
||||
static const int dinoTopWall = _above + ballOnBoard;
|
||||
|
||||
static const int dino = _above + dinoTopWall;
|
||||
|
||||
static const int dinoBottomWall = _above + dino;
|
||||
|
||||
static const int slingshot = _above + dinoBottomWall;
|
||||
|
||||
// Flutter Forest
|
||||
|
||||
static const int flutterForest = _above + launchRampForegroundRailing;
|
||||
|
||||
// Sparky Fire Zone
|
||||
|
||||
static const int computerBase = _below + ballOnBoard;
|
||||
|
||||
static const int computerTop = _above + ballOnBoard;
|
||||
|
||||
static const int sparkyAnimatronic = _above + spaceshipRampForegroundRailing;
|
||||
|
||||
static const int sparkyBumper = _above + ballOnBoard;
|
||||
|
||||
static const int turboChargeFlame = _above + ballOnBoard;
|
||||
|
||||
// Android Acres
|
||||
|
||||
static const int spaceshipRail = _above + bottomGroup;
|
||||
|
||||
static const int spaceshipRailExit = _above + ballOnSpaceshipRail;
|
||||
|
||||
static const int spaceshipSaucer = _above + ballOnSpaceshipRail;
|
||||
|
||||
static const int spaceshipLightBeam = _below + spaceshipSaucer;
|
||||
|
||||
static const int androidHead = _above + spaceshipSaucer;
|
||||
|
||||
static const int spaceshipRamp = _above + ballOnBoard;
|
||||
|
||||
static const int spaceshipRampBackgroundRailing = _above + spaceshipRamp;
|
||||
|
||||
static const int spaceshipRampArrow = _above + spaceshipRamp;
|
||||
|
||||
static const int spaceshipRampForegroundRailing =
|
||||
_above + ballOnSpaceshipRamp;
|
||||
|
||||
static const int spaceshipRampBoardOpening = _below + ballOnBoard;
|
||||
|
||||
static const int androidBumper = _above + ballOnBoard;
|
||||
|
||||
// Score Text
|
||||
|
||||
static const int scoreText = _above + spaceshipRampForegroundRailing;
|
||||
|
||||
// Debug information
|
||||
static const int debugInfo = _above + scoreText;
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
enum Points {
|
||||
fiveThousand,
|
||||
twentyThousand,
|
||||
twoHundredThousand,
|
||||
oneMillion,
|
||||
}
|
||||
|
||||
/// {@template score_component}
|
||||
/// A [ScoreComponent] that spawns at a given [position] with a moving
|
||||
/// animation.
|
||||
/// {@endtemplate}
|
||||
class ScoreComponent extends SpriteComponent with HasGameRef, ZIndex {
|
||||
/// {@macro score_component}
|
||||
ScoreComponent({
|
||||
required this.points,
|
||||
required Vector2 position,
|
||||
}) : super(
|
||||
position: position,
|
||||
anchor: Anchor.center,
|
||||
) {
|
||||
zIndex = ZIndexes.score;
|
||||
}
|
||||
|
||||
late final Effect _effect;
|
||||
|
||||
late Points points;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
final sprite = Sprite(
|
||||
gameRef.images.fromCache(points.asset),
|
||||
);
|
||||
this.sprite = sprite;
|
||||
size = sprite.originalSize / 55;
|
||||
|
||||
await add(
|
||||
_effect = MoveEffect.by(
|
||||
Vector2(0, -5),
|
||||
EffectController(duration: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
if (_effect.controller.completed) {
|
||||
removeFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PointsX on Points {
|
||||
int get value {
|
||||
switch (this) {
|
||||
case Points.fiveThousand:
|
||||
return 5000;
|
||||
case Points.twentyThousand:
|
||||
return 20000;
|
||||
case Points.twoHundredThousand:
|
||||
return 200000;
|
||||
case Points.oneMillion:
|
||||
return 1000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension on Points {
|
||||
String get asset {
|
||||
switch (this) {
|
||||
case Points.fiveThousand:
|
||||
return Assets.images.score.fiveThousand.keyName;
|
||||
case Points.twentyThousand:
|
||||
return Assets.images.score.twentyThousand.keyName;
|
||||
case Points.twoHundredThousand:
|
||||
return Assets.images.score.twoHundredThousand.keyName;
|
||||
case Points.oneMillion:
|
||||
return Assets.images.score.oneMillion.keyName;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/effects.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template score_text}
|
||||
/// A [TextComponent] that spawns at a given [position] with a moving animation.
|
||||
/// {@endtemplate}
|
||||
class ScoreText extends TextComponent {
|
||||
/// {@macro score_text}
|
||||
ScoreText({
|
||||
required String text,
|
||||
required Vector2 position,
|
||||
this.color = Colors.black,
|
||||
}) : super(
|
||||
text: text,
|
||||
position: position,
|
||||
anchor: Anchor.center,
|
||||
priority: RenderPriority.scoreText,
|
||||
);
|
||||
|
||||
late final Effect _effect;
|
||||
|
||||
/// The [text]'s [Color].
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
textRenderer = TextPaint(
|
||||
style: TextStyle(
|
||||
fontFamily: PinballFonts.pixeloidMono,
|
||||
color: color,
|
||||
fontSize: 4,
|
||||
),
|
||||
);
|
||||
|
||||
await add(
|
||||
_effect = MoveEffect.by(
|
||||
Vector2(0, -5),
|
||||
EffectController(duration: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
|
||||
if (_effect.controller.completed) {
|
||||
removeFromParent();
|
||||
}
|
||||
}
|
||||
}
|