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.
54 lines
2.1 KiB
C#
54 lines
2.1 KiB
C#
using Godot;
|
|
using Mr.BrickAdventures.Autoloads;
|
|
using Mr.BrickAdventures.Progression;
|
|
|
|
namespace Mr.BrickAdventures.scripts.Events;
|
|
|
|
/// <summary>
|
|
/// Records lifetime statistics on the Profile.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Statistics moved to the Profile alongside level progression, because they outlive a Playthrough —
|
|
/// see docs/adr/0001-three-progression-scopes.md. <see cref="StatName"/> is a closed set, so a typo is
|
|
/// now a compile error rather than a silently invented sixth statistic.
|
|
/// </remarks>
|
|
public partial class StatisticsEventHandler : Node
|
|
{
|
|
public override void _Ready()
|
|
{
|
|
EventBus.Instance.CoinCollected += OnCoinCollected;
|
|
EventBus.Instance.EnemyDefeated += OnEnemyDefeated;
|
|
EventBus.Instance.PlayerDied += OnPlayerDied;
|
|
EventBus.Instance.ChildRescued += OnChildRescued;
|
|
}
|
|
|
|
public override void _ExitTree()
|
|
{
|
|
if (EventBus.Instance == null) return;
|
|
|
|
EventBus.Instance.CoinCollected -= OnCoinCollected;
|
|
EventBus.Instance.EnemyDefeated -= OnEnemyDefeated;
|
|
EventBus.Instance.PlayerDied -= OnPlayerDied;
|
|
EventBus.Instance.ChildRescued -= OnChildRescued;
|
|
}
|
|
|
|
private static void Record(StatName stat, int amount = 1) =>
|
|
ProgressionRoot.Instance?.RecordStat(stat, amount);
|
|
|
|
// Record rejects non-positive amounts, so a zero-value coin is skipped rather than throwing.
|
|
private void OnCoinCollected(int amount, Vector2 position)
|
|
{
|
|
if (amount > 0) Record(StatName.CoinsCollected, amount);
|
|
}
|
|
|
|
private void OnEnemyDefeated(Node enemy, Vector2 position) => Record(StatName.EnemiesDefeated);
|
|
|
|
private void OnPlayerDied(Vector2 position) => Record(StatName.Deaths);
|
|
|
|
private void OnChildRescued(Vector2 position) => Record(StatName.ChildrenRescued);
|
|
|
|
// LevelsCompleted is recorded by ProgressionService.FinishLevel, not here. It counts completions
|
|
// including Replays — deliberately a different fact from ClearedLevels.Count — and keeping it next
|
|
// to the settlement is what stops the two drifting apart the way the old pair did.
|
|
}
|