mirror of https://github.com/flutter/pinball.git
parent
32c2cd8cd9
commit
a1f1b12798
@ -1,165 +0,0 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'dart:collection';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.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';
|
||||
|
||||
/// {@template controlled_spaceship_ramp}
|
||||
/// [SpaceshipRamp] with a [SpaceshipRampController] attached.
|
||||
/// {@endtemplate}
|
||||
class ControlledSpaceshipRamp extends Component
|
||||
with Controls<SpaceshipRampController>, HasGameRef<PinballGame> {
|
||||
/// {@macro controlled_spaceship_ramp}
|
||||
ControlledSpaceshipRamp() {
|
||||
controller = SpaceshipRampController(this);
|
||||
}
|
||||
|
||||
late final SpaceshipRamp _spaceshipRamp;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
gameRef.addContactCallback(SpaceshipRampSensorBallContactCallback());
|
||||
|
||||
_spaceshipRamp = SpaceshipRamp();
|
||||
await addFromBlueprint(_spaceshipRamp);
|
||||
await addAll([
|
||||
SpaceshipRampSensor(type: SpaceshipRampSensorType.door)
|
||||
..initialPosition = Vector2(1.7, -20),
|
||||
SpaceshipRampSensor(type: SpaceshipRampSensorType.inside)
|
||||
..initialPosition = Vector2(1.7, -21.5),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template spaceship_ramp_controller}
|
||||
/// Controller attached to a [SpaceshipRamp] that handles its game related
|
||||
/// logic.
|
||||
/// {@endtemplate}
|
||||
|
||||
class SpaceshipRampController
|
||||
extends ComponentController<ControlledSpaceshipRamp>
|
||||
with HasGameRef<PinballGame> {
|
||||
/// {@macro spaceship_ramp_controller}
|
||||
SpaceshipRampController(ControlledSpaceshipRamp controlledSpaceshipRamp)
|
||||
: super(controlledSpaceshipRamp);
|
||||
|
||||
final int _oneMillionPointsTarget = 10;
|
||||
|
||||
int _hitsCounter = 0;
|
||||
|
||||
/// When a [Ball] shot the [SpaceshipRamp] it achieve improvements for the
|
||||
/// current game, like multipliers or score.
|
||||
void shot() {
|
||||
_hitsCounter++;
|
||||
|
||||
component._spaceshipRamp.progress();
|
||||
|
||||
gameRef.read<GameBloc>().add(const Scored(points: 5000));
|
||||
|
||||
// TODO(ruimiguel): increase here multiplier at GameBloc.
|
||||
|
||||
if (_hitsCounter % _oneMillionPointsTarget == 0) {
|
||||
// TODO(ruimiguel): One million by bonus??
|
||||
const oneMillion = 1000000;
|
||||
gameRef.read<GameBloc>().add(const Scored(points: oneMillion));
|
||||
gameRef.add(
|
||||
ScoreText(
|
||||
text: oneMillion.toString(),
|
||||
position: Vector2(1.7, -20),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to know when a [Ball] gets into the [SpaceshipRamp] against every ball
|
||||
/// that crosses the opening.
|
||||
@visibleForTesting
|
||||
enum SpaceshipRampSensorType {
|
||||
/// Sensor at the entrance of the opening.
|
||||
door,
|
||||
|
||||
/// Sensor inside the [SpaceshipRamp].
|
||||
inside,
|
||||
}
|
||||
|
||||
/// {@template spaceship_ramp_sensor}
|
||||
/// Small sensor body used to detect when a ball has entered the
|
||||
/// [SpaceshipRamp].
|
||||
/// {@endtemplate}
|
||||
@visibleForTesting
|
||||
class SpaceshipRampSensor extends BodyComponent with InitialPosition, Layered {
|
||||
/// {@macro spaceship_ramp_sensor}
|
||||
SpaceshipRampSensor({required this.type}) : super() {
|
||||
layer = Layer.spaceshipEntranceRamp;
|
||||
renderBody = false;
|
||||
}
|
||||
|
||||
/// Type for the sensor, to know if it's the one at the door or inside ramp.
|
||||
final SpaceshipRampSensorType type;
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = PolygonShape()
|
||||
..setAsBox(
|
||||
2,
|
||||
2,
|
||||
initialPosition,
|
||||
-5 * math.pi / 180,
|
||||
);
|
||||
|
||||
final fixtureDef = FixtureDef(
|
||||
shape,
|
||||
isSensor: true,
|
||||
);
|
||||
final bodyDef = BodyDef(
|
||||
position: initialPosition,
|
||||
userData: this,
|
||||
);
|
||||
|
||||
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template spaceship_ramp_sensor_ball_contact_callback}
|
||||
/// Turbo charges the [Ball] on contact with [SpaceshipRampSensor].
|
||||
/// {@endtemplate}
|
||||
@visibleForTesting
|
||||
class SpaceshipRampSensorBallContactCallback
|
||||
extends ContactCallback<SpaceshipRampSensor, ControlledBall> {
|
||||
/// {@macro spaceship_ramp_sensor_ball_contact_callback}
|
||||
SpaceshipRampSensorBallContactCallback();
|
||||
|
||||
final Set<Ball> _balls = HashSet();
|
||||
|
||||
@override
|
||||
void begin(
|
||||
SpaceshipRampSensor spaceshipRampSensor,
|
||||
ControlledBall ball,
|
||||
__,
|
||||
) {
|
||||
switch (spaceshipRampSensor.type) {
|
||||
case SpaceshipRampSensorType.door:
|
||||
if (!_balls.contains(ball)) {
|
||||
_balls.add(ball);
|
||||
}
|
||||
break;
|
||||
case SpaceshipRampSensorType.inside:
|
||||
if (_balls.contains(ball)) {
|
||||
final parent = spaceshipRampSensor.parent;
|
||||
if (parent is ControlledSpaceshipRamp) {
|
||||
parent.controller.shot();
|
||||
}
|
||||
_balls.remove(ball);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
// ignore_for_file: avoid_renaming_method_parameters
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball/game/components/components.dart';
|
||||
import 'package:pinball/game/components/spaceship_ramp/behaviors/behaviors.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
export 'cubit/android_ramp_sensor_cubit.dart';
|
||||
|
||||
/// {@template android_ramp}
|
||||
/// [AndroidRamp] with a for the [SpaceshipRamp].
|
||||
/// {@endtemplate}
|
||||
class AndroidRamp extends Component with HasGameRef<PinballGame> {
|
||||
/// {@macro android_ramp}
|
||||
AndroidRamp()
|
||||
: super(
|
||||
children: [
|
||||
AndroidRampSensor(type: AndroidRampSensorType.door)
|
||||
..initialPosition = Vector2(1.7, -20),
|
||||
AndroidRampSensor(type: AndroidRampSensorType.inside)
|
||||
..initialPosition = Vector2(1.7, -21.5),
|
||||
AndroidRampBonusBehavior(
|
||||
shotPoints: 5000,
|
||||
bonusPoints: 1000000,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
late final SpaceshipRamp spaceshipRamp = SpaceshipRamp();
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
gameRef.addFromBlueprint(spaceshipRamp);
|
||||
}
|
||||
|
||||
/// Creates a [AndroidRamp] without any children.
|
||||
///
|
||||
/// This can be used for testing [AndroidRamp]'s behaviors in isolation.
|
||||
@visibleForTesting
|
||||
AndroidRamp.test();
|
||||
}
|
||||
|
||||
/// {@template android_ramp_sensor}
|
||||
/// Small sensor body used to detect when a ball has entered the
|
||||
/// [SpaceshipRamp].
|
||||
/// {@endtemplate}
|
||||
@visibleForTesting
|
||||
class AndroidRampSensor extends BodyComponent
|
||||
with ParentIsA<AndroidRamp>, InitialPosition {
|
||||
/// {@macro android_ramp_sensor}
|
||||
AndroidRampSensor({required this.type})
|
||||
: bloc = AndroidRampSensorCubit(),
|
||||
super(
|
||||
children: [
|
||||
RampShotBehavior(),
|
||||
],
|
||||
renderBody: false,
|
||||
);
|
||||
|
||||
/// Creates a [AndroidRampSensor] without any children.
|
||||
///
|
||||
@visibleForTesting
|
||||
AndroidRampSensor.test({
|
||||
required this.type,
|
||||
required this.bloc,
|
||||
});
|
||||
|
||||
/// Type for the sensor, to know if it's the one at the door or inside ramp.
|
||||
final AndroidRampSensorType type;
|
||||
|
||||
// TODO(alestiago): Consider refactoring once the following is merged:
|
||||
// https://github.com/flame-engine/flame/pull/1538
|
||||
// ignore: public_member_api_docs
|
||||
final AndroidRampSensorCubit bloc;
|
||||
|
||||
@override
|
||||
void onRemove() {
|
||||
bloc.close();
|
||||
super.onRemove();
|
||||
}
|
||||
|
||||
@override
|
||||
Body createBody() {
|
||||
final shape = PolygonShape()
|
||||
..setAsBox(
|
||||
2,
|
||||
2,
|
||||
initialPosition,
|
||||
-5 * math.pi / 180,
|
||||
);
|
||||
|
||||
final fixtureDef = FixtureDef(
|
||||
shape,
|
||||
isSensor: true,
|
||||
);
|
||||
final bodyDef = BodyDef(
|
||||
position: initialPosition,
|
||||
userData: this,
|
||||
);
|
||||
|
||||
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export 'ramp_multiplier_bonus_behavior.dart';
|
||||
export 'ramp_shot_behavior.dart';
|
@ -0,0 +1,92 @@
|
||||
import 'dart:collection';
|
||||
|
||||
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_ramp_bonus_behavior}
|
||||
/// When all [DashNestBumper]s are hit at least once, the [GameBonus.dashNest]
|
||||
/// is awarded, and the [DashNestBumper.main] releases a new [Ball].
|
||||
/// {@endtemplate}
|
||||
class AndroidRampBonusBehavior extends Component
|
||||
with ParentIsA<AndroidRamp>, HasGameRef<PinballGame> {
|
||||
/// {@macro android_ramp_bonus_behavior}
|
||||
AndroidRampBonusBehavior({
|
||||
required int shotPoints,
|
||||
required int bonusPoints,
|
||||
}) : _shotPoints = shotPoints,
|
||||
_bonusPoints = bonusPoints;
|
||||
|
||||
final int _shotPoints;
|
||||
final int _bonusPoints;
|
||||
|
||||
final Set<Ball> _balls = HashSet();
|
||||
int _previousHits = 0;
|
||||
|
||||
@override
|
||||
void onMount() {
|
||||
super.onMount();
|
||||
|
||||
final sensors = parent.children.whereType<AndroidRampSensor>();
|
||||
for (final sensor in sensors) {
|
||||
sensor.bloc.stream.listen((state) {
|
||||
switch (state.type) {
|
||||
case AndroidRampSensorType.door:
|
||||
_handleOnDoor(state.ball!);
|
||||
break;
|
||||
case AndroidRampSensorType.inside:
|
||||
_handleOnInside(state.ball!);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _handleOnDoor(Ball ball) {
|
||||
print("_handleOnDoor $ball");
|
||||
print("$_balls");
|
||||
if (!_balls.contains(ball)) {
|
||||
_balls.add(ball);
|
||||
print("added $_balls");
|
||||
}
|
||||
}
|
||||
|
||||
void _handleOnInside(Ball ball) {
|
||||
print("_handleOnInside $ball");
|
||||
print("$_balls");
|
||||
if (_balls.contains(ball)) {
|
||||
_balls.remove(ball);
|
||||
print("removed $_balls");
|
||||
_previousHits++;
|
||||
shot(_previousHits);
|
||||
}
|
||||
}
|
||||
|
||||
final int _oneMillionPointsTarget = 10;
|
||||
|
||||
/// When a [Ball] shot the [SpaceshipRamp] it achieve improvements for the
|
||||
/// current game, like multipliers or score.
|
||||
void shot(int currentHits) {
|
||||
parent.spaceshipRamp.progress();
|
||||
print("SHOT $currentHits");
|
||||
|
||||
print("Score $_shotPoints");
|
||||
gameRef.read<GameBloc>().add(Scored(points: _shotPoints));
|
||||
|
||||
final multiplier = gameRef.read<GameBloc>().state.multiplier;
|
||||
gameRef.read<GameBloc>().add(const MultiplierIncreased());
|
||||
print("Increase multiplier $multiplier");
|
||||
|
||||
if (currentHits % _oneMillionPointsTarget == 0) {
|
||||
print("Score $_oneMillionPointsTarget");
|
||||
gameRef.read<GameBloc>().add(Scored(points: _bonusPoints));
|
||||
gameRef.add(
|
||||
ScoreText(
|
||||
text: _bonusPoints.toString(),
|
||||
position: Vector2(1.7, -20),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
// ignore_for_file: public_member_api_docs
|
||||
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||
import 'package:pinball/game/components/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
import 'package:pinball_flame/pinball_flame.dart';
|
||||
|
||||
class RampShotBehavior extends ContactBehavior<AndroidRampSensor> {
|
||||
@override
|
||||
void beginContact(Object other, Contact contact) {
|
||||
super.beginContact(other, contact);
|
||||
|
||||
if (other is! Ball) return;
|
||||
switch (parent.type) {
|
||||
case AndroidRampSensorType.door:
|
||||
parent.bloc.onDoor(other);
|
||||
break;
|
||||
case AndroidRampSensorType.inside:
|
||||
parent.bloc.onInside(other);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
part 'android_ramp_sensor_state.dart';
|
||||
|
||||
class AndroidRampSensorCubit extends Cubit<AndroidRampSensorState> {
|
||||
AndroidRampSensorCubit() : super(AndroidRampSensorState.initial());
|
||||
|
||||
void onDoor(Ball ball) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
type: AndroidRampSensorType.door,
|
||||
ball: ball,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onInside(Ball ball) {
|
||||
emit(state.copyWith(
|
||||
type: AndroidRampSensorType.inside,
|
||||
ball: ball,
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
part of 'android_ramp_sensor_cubit.dart';
|
||||
|
||||
/// Used to know when a [Ball] gets into the [SpaceshipRamp] against every ball
|
||||
/// that crosses the opening.
|
||||
enum AndroidRampSensorType {
|
||||
/// Sensor at the entrance of the opening.
|
||||
door,
|
||||
|
||||
/// Sensor inside the [SpaceshipRamp].
|
||||
inside,
|
||||
}
|
||||
|
||||
class AndroidRampSensorState extends Equatable {
|
||||
AndroidRampSensorState({
|
||||
required this.type,
|
||||
this.ball,
|
||||
});
|
||||
|
||||
/// {@macro assets_manager_state}
|
||||
AndroidRampSensorState.initial() : this(type: AndroidRampSensorType.door);
|
||||
|
||||
final AndroidRampSensorType type;
|
||||
final Ball? ball;
|
||||
|
||||
AndroidRampSensorState copyWith({
|
||||
AndroidRampSensorType? type,
|
||||
Ball? ball,
|
||||
}) {
|
||||
return AndroidRampSensorState(
|
||||
type: type ?? this.type,
|
||||
ball: ball ?? this.ball,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [type, ball];
|
||||
}
|
Loading…
Reference in new issue