Merge branch 'main' into fix/ramp-opening

fix/ramp-opening
RuiAlonso 3 years ago
commit 28576f9770

@ -51,7 +51,7 @@ class CameraFocusingBehavior extends Component
position: _foci[GameStatus.waiting]?.position ?? Vector2(0, -112),
),
GameStatus.playing: _FocusData(
zoom: size.y / 165,
zoom: size.y / 160,
position: _foci[GameStatus.playing]?.position ?? Vector2(0, -7.8),
),
GameStatus.gameOver: _FocusData(

@ -42,7 +42,7 @@ class _BarrierBehindDino extends BodyComponent {
Body createBody() {
final shape = EdgeShape()
..set(
Vector2(25.3, -14.2),
Vector2(24.2, -14.8),
Vector2(25.3, -7.7),
);

@ -5,7 +5,6 @@ import 'package:pinball/select_character/select_character.dart';
import 'package:pinball_audio/pinball_audio.dart';
import 'package:pinball_components/pinball_components.dart';
import 'package:pinball_flame/pinball_flame.dart';
import 'package:platform_helper/platform_helper.dart';
/// Listens to the [GameBloc] and updates the game accordingly.
class GameBlocStatusListener extends Component
@ -68,31 +67,25 @@ class GameBlocStatusListener extends Component
gameRef.descendants().whereType<Signpost>().single.bloc.onReset();
}
void _addPlungerBehaviors(Plunger plunger) {
final platformHelper = readProvider<PlatformHelper>();
const pullingStrength = 7.0;
final provider =
plunger.firstChild<FlameBlocProvider<PlungerCubit, PlungerState>>()!;
if (platformHelper.isMobile) {
provider.add(
PlungerAutoPullingBehavior(strength: pullingStrength),
);
} else {
provider.addAll(
void _addPlungerBehaviors(Plunger plunger) => plunger
.firstChild<FlameBlocProvider<PlungerCubit, PlungerState>>()!
.addAll(
[
PlungerKeyControllingBehavior(),
PlungerPullingBehavior(strength: pullingStrength),
PlungerPullingBehavior(strength: 7),
PlungerAutoPullingBehavior(),
PlungerKeyControllingBehavior()
],
);
}
}
void _removePlungerBehaviors(Plunger plunger) {
plunger
.descendants()
.whereType<PlungerPullingBehavior>()
.forEach(plunger.remove);
plunger
.descendants()
.whereType<PlungerAutoPullingBehavior>()
.forEach(plunger.remove);
plunger
.descendants()
.whereType<PlungerKeyControllingBehavior>()

@ -155,19 +155,18 @@ class PinballGame extends PinballForge2DGame
@override
void onTapDown(int pointerId, TapDownInfo info) {
if (info.raw.kind == PointerDeviceKind.touch) {
if (info.raw.kind == PointerDeviceKind.touch &&
_gameBloc.state.status.isPlaying) {
final rocket = descendants().whereType<RocketSpriteComponent>().first;
final bounds = rocket.topLeftPosition & rocket.size;
// NOTE: As long as Flame does not have https://github.com/flame-engine/flame/issues/1586
// we need to check it at the highest level manually.
final tappedRocket = bounds.contains(info.eventPosition.game.toOffset());
if (tappedRocket) {
descendants()
.whereType<FlameBlocProvider<PlungerCubit, PlungerState>>()
.first
.bloc
.pulled();
.autoPulled();
} else {
final tappedLeftSide = info.eventPosition.widget.x < canvasSize.x / 2;
focusedBoardSide[pointerId] =

@ -279,10 +279,11 @@ class PinballAudioPlayer {
playSingleAudio: _playSingleAudio,
path: Assets.sfx.sparky,
),
PinballAudio.dino: _SimplePlayAudio(
PinballAudio.dino: _ThrottledAudio(
preCacheSingleAudio: _preCacheSingleAudio,
playSingleAudio: _playSingleAudio,
path: Assets.sfx.dino,
duration: const Duration(seconds: 6),
),
PinballAudio.dash: _SimplePlayAudio(
preCacheSingleAudio: _preCacheSingleAudio,

@ -454,6 +454,32 @@ void main() {
),
).called(1);
});
test('only plays the sound again after 6 seconds', () async {
final clock = _MockClock();
await withClock(clock, () async {
when(clock.now).thenReturn(DateTime(2022));
await Future.wait(
audioPlayer.load().map((loadableBuilder) => loadableBuilder()),
);
audioPlayer
..play(PinballAudio.dino)
..play(PinballAudio.dino);
verify(
() => playSingleAudio
.onCall('packages/pinball_audio/${Assets.sfx.dino}'),
).called(1);
when(clock.now).thenReturn(DateTime(2022, 1, 1, 1, 6));
audioPlayer.play(PinballAudio.dino);
verify(
() => playSingleAudio
.onCall('packages/pinball_audio/${Assets.sfx.dino}'),
).called(1);
});
});
});
group('android', () {

@ -28,14 +28,19 @@ class PlungerPullingBehavior extends Component
}
}
class PlungerAutoPullingBehavior extends PlungerPullingBehavior {
PlungerAutoPullingBehavior({
required double strength,
}) : super(strength: strength);
class PlungerAutoPullingBehavior extends Component
with FlameBlocReader<PlungerCubit, PlungerState> {
late final Plunger _plunger;
@override
Future<void> onLoad() async {
await super.onLoad();
_plunger = parent!.parent! as Plunger;
}
@override
void update(double dt) {
super.update(dt);
if (!bloc.state.isAutoPulling) return;
final joint = _plunger.body.joints.whereType<PrismaticJoint>().single;
final reachedBottom = joint.getJointTranslation() <= joint.getLowerLimit();

@ -8,4 +8,6 @@ class PlungerCubit extends Cubit<PlungerState> {
void pulled() => emit(PlungerState.pulling);
void released() => emit(PlungerState.releasing);
void autoPulled() => emit(PlungerState.autoPulling);
}

@ -4,9 +4,13 @@ enum PlungerState {
pulling,
releasing,
autoPulling,
}
extension PlungerStateX on PlungerState {
bool get isPulling => this == PlungerState.pulling;
bool get isPulling =>
this == PlungerState.pulling || this == PlungerState.autoPulling;
bool get isReleasing => this == PlungerState.releasing;
bool get isAutoPulling => this == PlungerState.autoPulling;
}

@ -132,6 +132,7 @@ class _PlungerSpriteAnimationGroupComponent
animations = {
PlungerState.releasing: pullAnimation.reversed(),
PlungerState.pulling: pullAnimation,
PlungerState.autoPulling: pullAnimation,
};
current = readBloc<PlungerCubit, PlungerState>().state;

@ -3,6 +3,7 @@
import 'dart:async';
import 'package:bloc_test/bloc_test.dart';
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:flame_forge2d/flame_forge2d.dart';
import 'package:flame_test/flame_test.dart';
@ -12,7 +13,7 @@ import 'package:pinball_components/pinball_components.dart';
class _TestGame extends Forge2DGame {
Future<void> pump(
PlungerPullingBehavior behavior, {
Component behavior, {
PlungerCubit? plungerBloc,
}) async {
final plunger = Plunger.test();
@ -85,62 +86,28 @@ void main() {
group('PlungerAutoPullingBehavior', () {
test('can be instantiated', () {
expect(
PlungerAutoPullingBehavior(strength: 0),
PlungerAutoPullingBehavior(),
isA<PlungerAutoPullingBehavior>(),
);
});
flameTester.test('can be loaded', (game) async {
final behavior = PlungerAutoPullingBehavior(strength: 0);
final behavior = PlungerAutoPullingBehavior();
await game.pump(behavior);
expect(game.descendants(), contains(behavior));
});
flameTester.test(
"pulls while joint hasn't reached limit",
(game) async {
final plungerBloc = _MockPlungerCubit();
whenListen<PlungerState>(
plungerBloc,
Stream.value(PlungerState.pulling),
initialState: PlungerState.pulling,
);
const strength = 2.0;
final behavior = PlungerAutoPullingBehavior(
strength: strength,
);
await game.pump(
behavior,
plungerBloc: plungerBloc,
);
final plunger = behavior.ancestors().whereType<Plunger>().single;
final joint = _MockPrismaticJoint();
when(joint.getJointTranslation).thenReturn(2);
when(joint.getLowerLimit).thenReturn(0);
plunger.body.joints.add(joint);
game.update(0);
expect(plunger.body.linearVelocity.x, equals(0));
expect(plunger.body.linearVelocity.y, equals(strength));
},
);
flameTester.test(
'releases when joint reaches limit',
(game) async {
final plungerBloc = _MockPlungerCubit();
whenListen<PlungerState>(
plungerBloc,
Stream.value(PlungerState.pulling),
initialState: PlungerState.pulling,
Stream.value(PlungerState.autoPulling),
initialState: PlungerState.autoPulling,
);
const strength = 2.0;
final behavior = PlungerAutoPullingBehavior(
strength: strength,
);
final behavior = PlungerAutoPullingBehavior();
await game.pump(
behavior,
plungerBloc: plungerBloc,

@ -0,0 +1,25 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pinball_components/pinball_components.dart';
void main() {
group('PlungerCubit', () {
test('can be instantiated', () {
expect(PlungerCubit(), isA<PlungerCubit>());
});
blocTest<PlungerCubit, PlungerState>(
'overrides previous pulling state',
build: PlungerCubit.new,
act: (cubit) => cubit
..pulled()
..autoPulled()
..pulled(),
expect: () => [
PlungerState.pulling,
PlungerState.autoPulling,
PlungerState.pulling,
],
);
});
}

@ -36,7 +36,6 @@ class _TestGame extends Forge2DGame with HasTappables {
Future<void> pump(
Iterable<Component> children, {
PinballAudioPlayer? pinballAudioPlayer,
PlatformHelper? platformHelper,
GoogleWordCubit? googleWordBloc,
}) async {
return ensureAdd(
@ -62,7 +61,7 @@ class _TestGame extends Forge2DGame with HasTappables {
_MockAppLocalizations(),
),
FlameProvider<PlatformHelper>.value(
platformHelper ?? PlatformHelper(),
PlatformHelper(),
),
],
children: children,
@ -80,8 +79,6 @@ class _MockLeaderboardRepository extends Mock implements LeaderboardRepository {
class _MockShareRepository extends Mock implements ShareRepository {}
class _MockPlatformHelper extends Mock implements PlatformHelper {}
class _MockPlungerCubit extends Mock implements PlungerCubit {}
class _MockGoogleWordCubit extends Mock implements GoogleWordCubit {}
@ -278,7 +275,7 @@ void main() {
create: PlungerCubit.new,
children: [
PlungerPullingBehavior(strength: 0),
PlungerAutoPullingBehavior(strength: 0)
PlungerAutoPullingBehavior()
],
),
);
@ -460,10 +457,8 @@ void main() {
);
flameTester.test(
'adds PlungerKeyControllingBehavior to Plunger when on desktop',
'adds PlungerKeyControllingBehavior to Plunger',
(game) async {
final platformHelper = _MockPlatformHelper();
when(() => platformHelper.isMobile).thenReturn(false);
final component = GameBlocStatusListener();
final leaderboardRepository = _MockLeaderboardRepository();
final shareRepository = _MockShareRepository();
@ -482,7 +477,6 @@ void main() {
bloc: _MockSignpostCubit(),
),
],
platformHelper: platformHelper,
);
await plunger.ensureAdd(
FlameBlocProvider<PlungerCubit, PlungerState>(
@ -506,10 +500,8 @@ void main() {
);
flameTester.test(
'adds PlungerPullingBehavior to Plunger when on desktop',
'adds PlungerPullingBehavior to Plunger',
(game) async {
final platformHelper = _MockPlatformHelper();
when(() => platformHelper.isMobile).thenReturn(false);
final component = GameBlocStatusListener();
final leaderboardRepository = _MockLeaderboardRepository();
final shareRepository = _MockShareRepository();
@ -528,7 +520,6 @@ void main() {
bloc: _MockSignpostCubit(),
),
],
platformHelper: platformHelper,
);
await plunger.ensureAdd(
FlameBlocProvider<PlungerCubit, PlungerState>(
@ -549,10 +540,8 @@ void main() {
);
flameTester.test(
'adds PlungerAutoPullingBehavior to Plunger when on mobile',
'adds PlungerAutoPullingBehavior to Plunger',
(game) async {
final platformHelper = _MockPlatformHelper();
when(() => platformHelper.isMobile).thenReturn(true);
final component = GameBlocStatusListener();
final leaderboardRepository = _MockLeaderboardRepository();
final shareRepository = _MockShareRepository();
@ -571,7 +560,6 @@ void main() {
bloc: _MockSignpostCubit(),
),
],
platformHelper: platformHelper,
);
await plunger.ensureAdd(
FlameBlocProvider<PlungerCubit, PlungerState>(

@ -246,9 +246,61 @@ void main() {
});
group('flipper control', () {
flameTester.test('tap control only works if game is playing',
(game) async {
await game.ready();
final gameBloc = game
.descendants()
.whereType<FlameBlocProvider<GameBloc, GameState>>()
.first
.bloc;
final eventPosition = _MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(Vector2.zero());
final raw = _MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
final tapDownEvent = _MockTapDownInfo();
when(() => tapDownEvent.eventPosition).thenReturn(eventPosition);
when(() => tapDownEvent.raw).thenReturn(raw);
final flipperBloc = game
.descendants()
.whereType<Flipper>()
.where((flipper) => flipper.side == BoardSide.left)
.single
.descendants()
.whereType<FlameBlocProvider<FlipperCubit, FlipperState>>()
.first
.bloc;
gameBloc.emit(gameBloc.state.copyWith(status: GameStatus.gameOver));
game.onTapDown(0, tapDownEvent);
await Future<void>.delayed(Duration.zero);
expect(flipperBloc.state, FlipperState.movingDown);
gameBloc.emit(gameBloc.state.copyWith(status: GameStatus.playing));
game.onTapDown(0, tapDownEvent);
await Future<void>.delayed(Duration.zero);
expect(flipperBloc.state, FlipperState.movingUp);
});
flameTester.test('tap down moves left flipper up', (game) async {
await game.ready();
final gameBloc = game
.descendants()
.whereType<FlameBlocProvider<GameBloc, GameState>>()
.first
.bloc;
gameBloc.emit(gameBloc.state.copyWith(status: GameStatus.playing));
final eventPosition = _MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(Vector2.zero());
@ -278,6 +330,14 @@ void main() {
flameTester.test('tap down moves right flipper up', (game) async {
await game.ready();
final gameBloc = game
.descendants()
.whereType<FlameBlocProvider<GameBloc, GameState>>()
.first
.bloc;
gameBloc.emit(gameBloc.state.copyWith(status: GameStatus.playing));
final eventPosition = _MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(game.canvasSize);
@ -307,6 +367,14 @@ void main() {
flameTester.test('tap up moves flipper down', (game) async {
await game.ready();
final gameBloc = game
.descendants()
.whereType<FlameBlocProvider<GameBloc, GameState>>()
.first
.bloc;
gameBloc.emit(gameBloc.state.copyWith(status: GameStatus.playing));
final eventPosition = _MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(Vector2.zero());
@ -332,6 +400,14 @@ void main() {
flameTester.test('tap cancel moves flipper down', (game) async {
await game.ready();
final gameBloc = game
.descendants()
.whereType<FlameBlocProvider<GameBloc, GameState>>()
.first
.bloc;
gameBloc.emit(gameBloc.state.copyWith(status: GameStatus.playing));
final eventPosition = _MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2.zero());
when(() => eventPosition.widget).thenReturn(Vector2.zero());
@ -363,6 +439,14 @@ void main() {
(game) async {
await game.ready();
final gameBloc = game
.descendants()
.whereType<FlameBlocProvider<GameBloc, GameState>>()
.first
.bloc;
gameBloc.emit(gameBloc.state.copyWith(status: GameStatus.playing));
final raw = _MockTapDownDetails();
when(() => raw.kind).thenReturn(PointerDeviceKind.touch);
@ -416,6 +500,14 @@ void main() {
flameTester.test('plunger control tap down emits plunging', (game) async {
await game.ready();
final gameBloc = game
.descendants()
.whereType<FlameBlocProvider<GameBloc, GameState>>()
.first
.bloc;
gameBloc.emit(gameBloc.state.copyWith(status: GameStatus.playing));
final eventPosition = _MockEventPosition();
when(() => eventPosition.game).thenReturn(Vector2(40, 60));
@ -434,7 +526,7 @@ void main() {
.single
.bloc;
expect(plungerBloc.state, PlungerState.pulling);
expect(plungerBloc.state, PlungerState.autoPulling);
});
});
});

Loading…
Cancel
Save