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:
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Progression;
|
||||
using Mr.BrickAdventures.Progression.Adapters;
|
||||
@@ -23,6 +24,13 @@ public partial class ProgressionRoot : Node
|
||||
{
|
||||
public static ProgressionRoot Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The one slot the game currently uses. Phase B replaces this with a picker over several
|
||||
/// Playthroughs; until then a fixed id keeps the file name stable across runs.
|
||||
/// </summary>
|
||||
public static readonly PlaythroughId SoleSlot =
|
||||
PlaythroughId.Of(new Guid("a17e0000-0000-4000-8000-000000000001"));
|
||||
|
||||
[Export] public LevelCatalogue Catalogue { get; set; }
|
||||
|
||||
/// <summary>Lives a new Playthrough starts with. A tuning value, so it is authored here rather than baked into the domain.</summary>
|
||||
@@ -109,6 +117,39 @@ public partial class ProgressionRoot : Node
|
||||
|
||||
public void StartPlaythrough(PlaythroughId id) => Run(() => _service.StartPlaythrough(id));
|
||||
|
||||
public bool LoadPlaythrough(PlaythroughId id)
|
||||
{
|
||||
if (_service == null) return false;
|
||||
|
||||
var loaded = _service.LoadPlaythrough(id);
|
||||
Publish();
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public LoadOutcome PeekPlaythrough(PlaythroughId id) =>
|
||||
_service?.PeekPlaythrough(id) ?? LoadOutcome.Absent;
|
||||
|
||||
public void RecordStat(StatName stat, int amount = 1) => _service?.RecordStat(stat, amount);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Reads
|
||||
|
||||
public bool IsPlaying => _service?.IsPlaying ?? false;
|
||||
public bool CanEnter(LevelId level) => _service?.CanEnter(level) ?? false;
|
||||
public LevelId? CurrentLevel => _service?.CurrentLevel;
|
||||
public Coins TotalCoins => _service?.TotalCoins ?? Coins.Zero;
|
||||
public Lives Lives => _service?.Lives ?? Lives.Of(0);
|
||||
public bool IsRunOver => _service?.IsRunOver ?? false;
|
||||
public SkillLevel? LevelOf(SkillId skill) => _service?.LevelOf(skill);
|
||||
public bool IsOwned(SkillId skill) => _service?.IsOwned(skill) ?? false;
|
||||
public bool CanAfford(SkillId skill) => _service?.CanAfford(skill) ?? false;
|
||||
public bool HasCleared(LevelId level) => _service?.HasCleared(level) ?? false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operations, continued
|
||||
|
||||
public bool EnterLevel(LevelId level)
|
||||
{
|
||||
if (_service == null) return false;
|
||||
@@ -122,7 +163,7 @@ public partial class ProgressionRoot : Node
|
||||
|
||||
public void HandlePlayerDeath() => Run(() => _service.HandlePlayerDeath());
|
||||
|
||||
public void FinishLevel(LevelTime time) => Run(() => _service.FinishLevel(time));
|
||||
public void FinishLevel(LevelTime? time) => Run(() => _service.FinishLevel(time));
|
||||
|
||||
public PurchaseOutcome PurchaseSkill(SkillId skill)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user