Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
feat: store when the user started playing
Browse files Browse the repository at this point in the history
  • Loading branch information
alestiago committed Feb 16, 2024
1 parent a1c70e0 commit 3f8e524
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 12 deletions.
11 changes: 8 additions & 3 deletions packages/trashy_road/lib/src/game/bloc/game_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:collection';

import 'package:bloc/bloc.dart';
import 'package:clock/clock.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import 'package:tiled/tiled.dart';
Expand Down Expand Up @@ -31,7 +32,12 @@ class GameBloc extends Bloc<GameEvent, GameState> {
GameInteractedEvent event,
Emitter<GameState> emit,
) {
emit(state.copyWith(status: GameStatus.playing));
emit(
state.copyWith(
status: GameStatus.playing,
startedAt: () => state.startedAt ?? clock.now(),
),
);
}

void _onCollectedTrash(
Expand Down Expand Up @@ -82,9 +88,8 @@ class GameBloc extends Bloc<GameEvent, GameState> {
Emitter<GameState> emit,
) {
emit(
state.copyWith(
GameState.initial(map: state.map).copyWith(
status: GameStatus.resetting,
inventory: Inventory.empty(),
),
);
}
Expand Down
7 changes: 7 additions & 0 deletions packages/trashy_road/lib/src/game/bloc/game_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class GameState extends Equatable {
required this.map,
required this.inventory,
this.collectedTrash = 0,
this.startedAt,
}) :
// TODO(alestiago): Remove magic string.
_initialTrash =
Expand Down Expand Up @@ -74,16 +75,21 @@ class GameState extends Equatable {
/// {@macro Inventory}
final Inventory inventory;

/// The time at which the game was started.
final DateTime? startedAt;

GameState copyWith({
GameStatus? status,
Inventory? inventory,
int? collectedTrash,
DateTime? Function()? startedAt,
}) {
return GameState(
status: status ?? this.status,
map: map,
inventory: inventory ?? this.inventory,
collectedTrash: collectedTrash ?? this.collectedTrash,
startedAt: startedAt != null ? startedAt() : this.startedAt,
);
}

Expand All @@ -93,6 +99,7 @@ class GameState extends Equatable {
inventory,
map,
collectedTrash,
startedAt,
];
}

Expand Down
90 changes: 81 additions & 9 deletions packages/trashy_road/test/src/game/bloc/game_bloc_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:clock/clock.dart';
import 'package:flame/game.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:tiled/tiled.dart';
Expand Down Expand Up @@ -29,13 +31,19 @@ void main() {
group('$GameInteractedEvent', () {
blocTest<GameBloc, GameState>(
'playing status after the user interacts with the game',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
act: (bloc) => bloc.add(const GameInteractedEvent()),
expect: () => [
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
],
);
Expand All @@ -54,7 +62,12 @@ void main() {
blocTest<GameBloc, GameState>(
'fills the inventory with trash when the user collects trash '
'and the game is playing',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
act: (bloc) => bloc
..add(const GameInteractedEvent())
..add(const GameCollectedTrashEvent(item: TrashType.plastic)),
Expand All @@ -63,19 +76,26 @@ void main() {
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory(items: const [TrashType.plastic]),
startedAt: DateTime(0),
),
],
);

blocTest<GameBloc, GameState>(
'increments the correct type of trash when the user collects trash '
'and the game is playing',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
act: (bloc) => bloc
..add(const GameInteractedEvent())
..add(const GameCollectedTrashEvent(item: TrashType.plastic))
Expand All @@ -85,17 +105,20 @@ void main() {
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory(items: const [TrashType.plastic]),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory:
Inventory(items: const [TrashType.plastic, TrashType.glass]),
startedAt: DateTime(0),
),
],
);
Expand All @@ -113,7 +136,12 @@ void main() {
blocTest<GameBloc, GameState>(
'empties the inventory with trash when the user deposits trash '
'and the game is playing',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
setUp: () => when(() => trashLayer.objects)
.thenReturn([_MockTiledObject(), _MockTiledObject()]),
act: (bloc) => bloc
Expand All @@ -125,25 +153,33 @@ void main() {
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory(items: const [TrashType.plastic]),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
collectedTrash: 1,
startedAt: DateTime(0),
),
],
);

blocTest<GameBloc, GameState>(
'empties the correct type of trash when the user deposits trash '
'and the game is playing',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
setUp: () => when(() => trashLayer.objects)
.thenReturn([_MockTiledObject(), _MockTiledObject()]),
act: (bloc) => bloc
Expand All @@ -156,30 +192,39 @@ void main() {
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory(items: const [TrashType.plastic]),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory:
Inventory(items: const [TrashType.plastic, TrashType.glass]),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory(items: const [TrashType.glass]),
collectedTrash: 1,
startedAt: DateTime(0),
),
],
);

blocTest<GameBloc, GameState>(
'completes the game when all the trash is deposited ',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
setUp: () => when(() => trashLayer.objects)
.thenReturn([_MockTiledObject(), _MockTiledObject()]),
act: (bloc) => bloc
Expand All @@ -193,29 +238,34 @@ void main() {
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory(items: const [TrashType.plastic]),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory:
Inventory(items: const [TrashType.plastic, TrashType.plastic]),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory(items: const [TrashType.plastic]),
collectedTrash: 1,
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.completed,
inventory: Inventory.empty(),
collectedTrash: 2,
startedAt: DateTime(0),
),
],
);
Expand All @@ -224,7 +274,12 @@ void main() {
group('$GamePausedEvent', () {
blocTest<GameBloc, GameState>(
'pauses the game when the user was previously playing the game',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
act: (bloc) => bloc
..add(const GameInteractedEvent())
..add(const GamePausedEvent()),
Expand All @@ -233,11 +288,13 @@ void main() {
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.paused,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
],
);
Expand All @@ -253,7 +310,12 @@ void main() {

blocTest<GameBloc, GameState>(
'resumes the game when the user was previously paused',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
act: (bloc) => bloc
..add(const GameInteractedEvent())
..add(const GamePausedEvent())
Expand All @@ -263,16 +325,19 @@ void main() {
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.paused,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
],
);
Expand All @@ -281,7 +346,12 @@ void main() {
group('$GameResetEvent', () {
blocTest<GameBloc, GameState>(
'resets the game',
build: () => GameBloc(map: map),
build: () {
return withClock<GameBloc>(
Clock.fixed(DateTime(0)),
() => GameBloc(map: map),
);
},
act: (bloc) => bloc
..add(const GameInteractedEvent())
..add(const GameCollectedTrashEvent(item: TrashType.plastic))
Expand All @@ -291,11 +361,13 @@ void main() {
map: map,
status: GameStatus.playing,
inventory: Inventory.empty(),
startedAt: DateTime(0),
),
GameState(
map: map,
status: GameStatus.playing,
inventory: Inventory(items: const [TrashType.plastic]),
startedAt: DateTime(0),
),
GameState(
map: map,
Expand Down

0 comments on commit 3f8e524

Please sign in to comment.