feat: level progression moves to the new domain (slice 10b, step 1)
narrowest safe step: only level identity and progression move. Coins, lives, skills and death stay entirely on GameStateStore and behave as before, so nothing is tracked in two places. GameManager addresses levels by stable LevelId via the catalogue; LevelScenes is gone, so reordering levels no longer invalidates saves. Every level entry goes through EnterLevel, which fixes LevelStarted never firing on the first level or after Continue - ghost recording and splits have been dead outside mid-campaign. GoTo announces after a frame, once the incoming scene is actually current. ChangeSceneToPacked is deferred, so the old code handed GhostEventHandler the scene being torn down. FinishLevel takes LevelTime? - GetCurrentLevelTime can legitimately be 0 and inventing a value is exactly the bad data LevelTime refuses. Continue distinguishes absent from corrupt and refuses to overwrite a corrupt save. Statistics move to the Profile. 69 tests.
This commit is contained in:
@@ -170,6 +170,66 @@ public class ProgressionServiceTests
|
||||
Assert.Equal(LevelTime.Of(30.0), profileStore.Load().Value!.BestTimeFor(First));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resuming_a_saved_playthrough_restores_where_it_had_got_to()
|
||||
{
|
||||
var store = new InMemoryPlaythroughStore();
|
||||
var first = new ProgressionService(
|
||||
Profile.Empty(), store, new InMemoryProfileStore(), TwoLevels(), Skills(), Lives.Of(3));
|
||||
first.StartPlaythrough(Id);
|
||||
first.EnterLevel(First);
|
||||
first.FinishLevel(LevelTime.Of(20.0));
|
||||
|
||||
// A fresh service, as though the game had been restarted.
|
||||
var resumed = new ProgressionService(
|
||||
Profile.Empty(), store, new InMemoryProfileStore(), TwoLevels(), Skills(), Lives.Of(3));
|
||||
|
||||
Assert.True(resumed.LoadPlaythrough(Id));
|
||||
Assert.Equal(Second, resumed.CurrentLevel);
|
||||
Assert.True(resumed.HasCleared(First));
|
||||
Assert.True(resumed.CanEnter(First));
|
||||
Assert.True(resumed.CanEnter(Second));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resuming_when_there_is_no_save_leaves_the_service_not_playing()
|
||||
{
|
||||
var service = new ProgressionService(
|
||||
Profile.Empty(), new InMemoryPlaythroughStore(), new InMemoryProfileStore(),
|
||||
TwoLevels(), Skills(), Lives.Of(3));
|
||||
|
||||
Assert.False(service.LoadPlaythrough(Id));
|
||||
Assert.False(service.IsPlaying);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Finishing_without_a_measured_time_still_settles_the_level_but_records_no_best_time()
|
||||
{
|
||||
// GetCurrentLevelTime returns 0 when the speedrun timer was never started. Passing null says
|
||||
// so honestly rather than banking a zero-second record.
|
||||
var profileStore = new InMemoryProfileStore();
|
||||
var service = new ProgressionService(
|
||||
Profile.Empty(), new InMemoryPlaythroughStore(), profileStore,
|
||||
TwoLevels(), Skills(), Lives.Of(3));
|
||||
service.StartPlaythrough(Id);
|
||||
service.EnterLevel(First);
|
||||
|
||||
service.FinishLevel(null);
|
||||
|
||||
Assert.True(service.HasCleared(First));
|
||||
Assert.Equal(Second, service.CurrentLevel);
|
||||
Assert.Null(profileStore.Load().Value!.BestTimeFor(First));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void An_unreachable_level_cannot_be_entered_even_though_it_is_in_the_campaign()
|
||||
{
|
||||
var service = Service();
|
||||
|
||||
Assert.True(service.CanEnter(First));
|
||||
Assert.False(service.CanEnter(Second));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Picking_up_a_coin_announces_the_new_total_so_the_hud_never_polls()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user