mirror of https://github.com/flutter/pinball.git
commit
425074634f
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
part 'assets_manager_state.dart';
|
||||||
|
|
||||||
|
/// {@template assets_manager_cubit}
|
||||||
|
/// Cubit responsable for pre loading any game assets
|
||||||
|
/// {@endtemplate}
|
||||||
|
class AssetsManagerCubit extends Cubit<AssetsManagerState> {
|
||||||
|
/// {@macro assets_manager_cubit}
|
||||||
|
AssetsManagerCubit(List<Future> loadables)
|
||||||
|
: super(
|
||||||
|
AssetsManagerState.initial(
|
||||||
|
loadables: loadables,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Loads the assets
|
||||||
|
Future<void> load() async {
|
||||||
|
final all = state.loadables.map((loadable) async {
|
||||||
|
await loadable;
|
||||||
|
emit(state.copyWith(loaded: [...state.loaded, loadable]));
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
await Future.wait(all);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
part of 'assets_manager_cubit.dart';
|
||||||
|
|
||||||
|
/// {@template assets_manager_state}
|
||||||
|
/// State used to load the game assets
|
||||||
|
/// {@endtemplate}
|
||||||
|
class AssetsManagerState extends Equatable {
|
||||||
|
/// {@macro assets_manager_state}
|
||||||
|
const AssetsManagerState({
|
||||||
|
required this.loadables,
|
||||||
|
required this.loaded,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// {@macro assets_manager_state}
|
||||||
|
const AssetsManagerState.initial({
|
||||||
|
required List<Future> loadables,
|
||||||
|
}) : this(loadables: loadables, loaded: const []);
|
||||||
|
|
||||||
|
/// List of futures to load
|
||||||
|
final List<Future> loadables;
|
||||||
|
|
||||||
|
/// List of loaded futures
|
||||||
|
final List<Future> loaded;
|
||||||
|
|
||||||
|
/// Returns a value between 0 and 1 to indicate the loading progress
|
||||||
|
double get progress => loaded.length / loadables.length;
|
||||||
|
|
||||||
|
/// Returns a copy of this instance with the given parameters
|
||||||
|
/// updated
|
||||||
|
AssetsManagerState copyWith({
|
||||||
|
List<Future>? loadables,
|
||||||
|
List<Future>? loaded,
|
||||||
|
}) {
|
||||||
|
return AssetsManagerState(
|
||||||
|
loadables: loadables ?? this.loadables,
|
||||||
|
loaded: loaded ?? this.loaded,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [loaded, loadables];
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
// ignore_for_file: avoid_renaming_method_parameters
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:pinball/flame/flame.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
// TODO(ruimiguel): create and add SparkyFireZone component here in other PR.
|
||||||
|
|
||||||
|
// TODO(ruimiguel): make private and remove ignore once SparkyFireZone is done
|
||||||
|
// ignore: public_member_api_docs
|
||||||
|
class ControlledSparkyBumper extends SparkyBumper
|
||||||
|
with Controls<_SparkyBumperController> {
|
||||||
|
// TODO(ruimiguel): make private and remove ignore once SparkyFireZone is done
|
||||||
|
// ignore: public_member_api_docs
|
||||||
|
ControlledSparkyBumper() : super.a() {
|
||||||
|
controller = _SparkyBumperController(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {@template sparky_bumper_controller}
|
||||||
|
/// Controls a [SparkyBumper].
|
||||||
|
/// {@endtemplate}
|
||||||
|
class _SparkyBumperController extends ComponentController<SparkyBumper>
|
||||||
|
with HasGameRef<PinballGame> {
|
||||||
|
/// {@macro sparky_bumper_controller}
|
||||||
|
_SparkyBumperController(ControlledSparkyBumper controlledSparkyBumper)
|
||||||
|
: super(controlledSparkyBumper);
|
||||||
|
|
||||||
|
/// Flag for activated state of the [SparkyBumper].
|
||||||
|
///
|
||||||
|
/// Used to toggle [SparkyBumper]s' state between activated and deactivated.
|
||||||
|
bool isActivated = false;
|
||||||
|
|
||||||
|
/// Registers when a [SparkyBumper] is hit by a [Ball].
|
||||||
|
void hit() {
|
||||||
|
if (isActivated) {
|
||||||
|
component.deactivate();
|
||||||
|
} else {
|
||||||
|
component.activate();
|
||||||
|
}
|
||||||
|
isActivated = !isActivated;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:bloc_test/bloc_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('AssetsManagerCubit', () {
|
||||||
|
final completer1 = Completer<void>();
|
||||||
|
final completer2 = Completer<void>();
|
||||||
|
|
||||||
|
final future1 = completer1.future;
|
||||||
|
final future2 = completer2.future;
|
||||||
|
|
||||||
|
blocTest<AssetsManagerCubit, AssetsManagerState>(
|
||||||
|
'emits the loaded on the order that they load',
|
||||||
|
build: () => AssetsManagerCubit([future1, future2]),
|
||||||
|
act: (cubit) {
|
||||||
|
cubit.load();
|
||||||
|
completer2.complete();
|
||||||
|
completer1.complete();
|
||||||
|
},
|
||||||
|
expect: () => [
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future1, future2],
|
||||||
|
loaded: [future2],
|
||||||
|
),
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future1, future2],
|
||||||
|
loaded: [future2, future1],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
// ignore_for_file: prefer_const_constructors
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('AssetsManagerState', () {
|
||||||
|
test('can be instantiated', () {
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(loadables: const [], loaded: const []),
|
||||||
|
isNotNull,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('has the correct initial state', () {
|
||||||
|
final future = Future<void>.value();
|
||||||
|
expect(
|
||||||
|
AssetsManagerState.initial(loadables: [future]),
|
||||||
|
equals(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future],
|
||||||
|
loaded: const [],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('progress', () {
|
||||||
|
final future1 = Future<void>.value();
|
||||||
|
final future2 = Future<void>.value();
|
||||||
|
|
||||||
|
test('returns 0 when no future is loaded', () {
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future1, future2],
|
||||||
|
loaded: const [],
|
||||||
|
).progress,
|
||||||
|
equals(0),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns the correct value when some of the futures are loaded', () {
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future1, future2],
|
||||||
|
loaded: [future1],
|
||||||
|
).progress,
|
||||||
|
equals(0.5),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns the 1 when all futures are loaded', () {
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future1, future2],
|
||||||
|
loaded: [future1, future2],
|
||||||
|
).progress,
|
||||||
|
equals(1),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('copyWith', () {
|
||||||
|
final future = Future<void>.value();
|
||||||
|
|
||||||
|
test('returns a copy with the updated loadables', () {
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: const [],
|
||||||
|
loaded: const [],
|
||||||
|
).copyWith(loadables: [future]),
|
||||||
|
equals(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future],
|
||||||
|
loaded: const [],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns a copy with the updated loaded', () {
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: const [],
|
||||||
|
loaded: const [],
|
||||||
|
).copyWith(loaded: [future]),
|
||||||
|
equals(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: const [],
|
||||||
|
loaded: [future],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('supports value comparison', () {
|
||||||
|
final future1 = Future<void>.value();
|
||||||
|
final future2 = Future<void>.value();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: const [],
|
||||||
|
loaded: const [],
|
||||||
|
),
|
||||||
|
equals(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: const [],
|
||||||
|
loaded: const [],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future1],
|
||||||
|
loaded: const [],
|
||||||
|
),
|
||||||
|
isNot(
|
||||||
|
equals(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: [future2],
|
||||||
|
loaded: const [],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: const [],
|
||||||
|
loaded: [future1],
|
||||||
|
),
|
||||||
|
isNot(
|
||||||
|
equals(
|
||||||
|
AssetsManagerState(
|
||||||
|
loadables: const [],
|
||||||
|
loaded: [future2],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball/game/game.dart';
|
||||||
|
|
||||||
|
import '../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final flameTester = FlameTester(EmptyPinballGameTest.new);
|
||||||
|
|
||||||
|
group('SparkyFireZone', () {
|
||||||
|
group('bumpers', () {
|
||||||
|
late ControlledSparkyBumper controlledSparkyBumper;
|
||||||
|
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'activate when deactivated bumper is hit',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
controlledSparkyBumper = ControlledSparkyBumper();
|
||||||
|
await game.ensureAdd(controlledSparkyBumper);
|
||||||
|
|
||||||
|
controlledSparkyBumper.controller.hit();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
expect(controlledSparkyBumper.controller.isActivated, isTrue);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.testGameWidget(
|
||||||
|
'deactivate when activated bumper is hit',
|
||||||
|
setUp: (game, tester) async {
|
||||||
|
controlledSparkyBumper = ControlledSparkyBumper();
|
||||||
|
await game.ensureAdd(controlledSparkyBumper);
|
||||||
|
|
||||||
|
controlledSparkyBumper.controller.hit();
|
||||||
|
controlledSparkyBumper.controller.hit();
|
||||||
|
},
|
||||||
|
verify: (game, tester) async {
|
||||||
|
expect(controlledSparkyBumper.controller.isActivated, isFalse);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue