feat(levels): level catalogue with stable ids (slice 7)
LevelResource gains a stable Id and swaps the string ScenePath for an editor-tracked PackedScene. The old path field was never read and had rotted - forest_1 pointed at level_forest_5.tscn, which doesn't exist, and borrowed LEVEL_5_NAME so it would have displayed "VILLAGE - 5". LevelCatalogue lists levels in campaign order and produces the domain's Campaign; an authoring mistake fails at boot because Campaign.Of rejects blank and duplicate ids. Not a domain port - nothing in the domain resolves a level to a scene. All 8 scenes now have a resource; campaign keeps the existing 6 (tutorial_1, village_1..5). village_6 and forest_1 are catalogued but deliberately out of the campaign. GameManager.LevelScenes stays until slice 10 rewrites GameManager.
This commit is contained in:
54
scripts/Resources/LevelCatalogue.cs
Normal file
54
scripts/Resources/LevelCatalogue.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.Progression;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// The authored list of Levels in campaign order, and the bridge from a <see cref="LevelId"/> back to
|
||||
/// the Godot scene that implements it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Not a domain port. Nothing in the progression domain resolves a Level to a scene — the domain only
|
||||
/// needs the ordering, which it takes as a <see cref="Campaign"/>. Scene lookup is wanted solely by
|
||||
/// the composition root, which is engine-side anyway.
|
||||
/// </remarks>
|
||||
[GlobalClass]
|
||||
public partial class LevelCatalogue : Resource
|
||||
{
|
||||
/// <summary>
|
||||
/// Levels in campaign order. A Level absent from this list is not reachable, even if its
|
||||
/// <see cref="LevelResource"/> exists.
|
||||
/// </summary>
|
||||
[Export]
|
||||
public Array<LevelResource> Levels { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The ordering the domain works from. Throws if the authored data is inconsistent — a blank or
|
||||
/// duplicated id cannot produce a <see cref="Campaign"/>, so a mistake here fails at boot rather
|
||||
/// than corrupting a save later.
|
||||
/// </summary>
|
||||
public Campaign ToCampaign()
|
||||
{
|
||||
var ids = new List<LevelId>();
|
||||
|
||||
for (var i = 0; i < Levels.Count; i++)
|
||||
{
|
||||
var level = Levels[i];
|
||||
if (level == null)
|
||||
throw new System.InvalidOperationException(
|
||||
$"LevelCatalogue has an empty slot at index {i}.");
|
||||
|
||||
ids.Add(LevelId.Of(level.Id));
|
||||
}
|
||||
|
||||
return Campaign.Of(ids);
|
||||
}
|
||||
|
||||
public LevelResource Find(LevelId id) =>
|
||||
Levels.FirstOrDefault(level => level != null && level.Id == id.Value);
|
||||
|
||||
public PackedScene SceneFor(LevelId id) => Find(id)?.Scene;
|
||||
}
|
||||
Reference in New Issue
Block a user