Vault Tileset Override Implementation Plan¶
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Vault rooms can render biome-authored custom floor/wall art (BiomeDef.vaultTileSet) with per-role fallback to the biome tileset.
Architecture: Reuses the existing per-tile art resolution: MapGenerator.ResolveTileSets already stamps themed-zone tilesets into _tileSetAt[]; the vault box gets stamped after zones, per role (floor tiles only if the vault set has grounds, wall tiles only if it has walls). Both map entry points (Generate, BuildFromLayout) already receive the BiomeDef, so plumbing is one captured field. Mesh builder, material cache, variant logic, tints and heights all work per-tile already — untouched.
Tech Stack: Unity 6 / URP, existing TileSet/BiomeDef ScriptableObjects, MapGenerator.
Spec: docs/superpowers/specs/2026-07-09-vault-tileset-override-design.md
Global Constraints¶
- No test suite exists (repo convention: verify by running the game). Verification = Unity compiles clean via Unity MCP console + a play-mode regression smoke. No NUnit.
- Never edit scripts while the Unity editor is in play mode.
- Do not modify any
.assetfiles (BiomeDef assets, GameConfig, TileSets), scenes, prefabs, orProjectSettings/— the new field defaults to null, which is exactly the no-override behavior. Do not mutate ScriptableObject instances at runtime in the editor (changes persist to the asset). - Do not touch the frozen web prototype (
game.js,config.js). - New code follows surrounding style: terse, comments only for non-obvious constraints.
- Commit only the files each task names.
Task 1: The override — TileSet helpers, BiomeDef field, MapGenerator stamping¶
Files:
- Modify: unity/raiders/Assets/Scripts/World/TileSet.cs (beside HasAllArt, ~line 38)
- Modify: unity/raiders/Assets/Scripts/Config/BiomeDef.cs (after tileSet, ~line 20)
- Modify: unity/raiders/Assets/Scripts/World/MapGenerator.cs (field ~line 146, Generate ~line 179, BuildFromLayout ~line 208, ResolveTileSets ~line 800)
Interfaces:
- Consumes: existing MapData.Vault (VaultGate.RoomTileBox — includes wall ring + interior + gate tile), TileType.Wall, _tileSetAt[].
- Produces: TileSet.HasGrounds/TileSet.HasWalls (public bools), BiomeDef.vaultTileSet (public TileSet, default null), MapGenerator._vaultTileSet (private). Task 2 relies on none of these by name — it is behavior-only.
- [ ] Step 1: TileSet role helpers
In unity/raiders/Assets/Scripts/World/TileSet.cs, directly after the HasAllArt property (line ~38), add:
/// <summary>Role-level presence — for partial overrides (the vault set) that fall back per role.</summary>
public bool HasGrounds => Count(grounds) > 0;
public bool HasWalls => Count(walls) > 0;
- [ ] Step 2: BiomeDef field
In unity/raiders/Assets/Scripts/Config/BiomeDef.cs, directly after the tileSet field (line ~20, before the [Header("Decorative outer ring …")]), add:
[Tooltip("Vault-room art override (wall ring + interior floor + gate tile). Null → the vault uses this " +
"biome's tileSet. Per-role fallback: an empty grounds/walls array keeps the biome art for that " +
"role (unlike a theme's whole-set override).")]
public TileSet vaultTileSet;
- [ ] Step 3: MapGenerator — captured field + assignment in both entry points
In unity/raiders/Assets/Scripts/World/MapGenerator.cs, after the _tileSetAt field declaration (line ~146):
// the vault room's optional art override (BiomeDef.vaultTileSet), stamped per ROLE by ResolveTileSets.
TileSet _vaultTileSet;
In Generate(MapDefinition def, BiomeDef biome), next to the existing ConfigureOuterRing(biome); call (line ~179), add:
_vaultTileSet = biome.vaultTileSet; // vault-room art override (null = none); per-role fallback in ResolveTileSets
In BuildFromLayout(TutorialStageDef def, BiomeDef biome = null), next to its ConfigureOuterRing(biome); call (line ~208), add:
_vaultTileSet = biome != null ? biome.vaultTileSet : null; // FTUE vault honors biome 0's override too
(Both entry points assign unconditionally, so a re-roll never leaks the previous run's set.)
- [ ] Step 4: MapGenerator — per-role vault stamp in ResolveTileSets
In ResolveTileSets(MapData m) (line ~800), after the themed-zone foreach loop's closing brace and before the method's closing brace, add:
// vault-room override (BiomeDef.vaultTileSet), stamped AFTER zones so it wins any overlap. Per ROLE:
// wall tiles take the set only if it has wall art, floor tiles only if it has ground art — an empty
// array keeps the biome/theme art for that role. The RoomTileBox already spans wall ring + floor + gate.
if (m.Vault != null && _vaultTileSet != null && (_vaultTileSet.HasGrounds || _vaultTileSet.HasWalls))
{
var b = m.Vault.RoomTileBox;
int vy0 = Mathf.Max(0, (int)b.y), vy1 = Mathf.Min(m.H - 1, (int)(b.y + b.height) - 1);
int vx0 = Mathf.Max(0, (int)b.x), vx1 = Mathf.Min(m.W - 1, (int)(b.x + b.width) - 1);
for (int ty = vy0; ty <= vy1; ty++)
for (int tx = vx0; tx <= vx1; tx++)
{
bool wall = m.Get(tx, ty) == TileType.Wall;
if (wall ? _vaultTileSet.HasWalls : _vaultTileSet.HasGrounds)
_tileSetAt[m.Idx(tx, ty)] = _vaultTileSet;
}
}
Note the existing comment above ResolveTileSets (lines ~797-799) describes the resolution order — extend its last line to mention the vault stamp, e.g. append: Vault override (BiomeDef.vaultTileSet) stamps last, per role.
- [ ] Step 5: Verify compile
Use ToolSearch "select:mcp__unity-mcp__Unity_GetConsoleLogs,mcp__unity-mcp__Unity_RunCommand"; trigger a recompile via Unity_RunCommand executing UnityEditor.AssetDatabase.Refresh(); (never System.Reflection in RunCommand code), then check Unity_GetConsoleLogs.
Expected: no compile errors.
- [ ] Step 6: Commit
git add unity/raiders/Assets/Scripts/World/TileSet.cs unity/raiders/Assets/Scripts/Config/BiomeDef.cs unity/raiders/Assets/Scripts/World/MapGenerator.cs
git commit -m "MapGenerator: BiomeDef.vaultTileSet overrides vault-room floor/walls, per-role fallback to biome art"
Task 2: Play-mode regression smoke¶
Files: none modified.
Interfaces: consumes Task 1's behavior only.
No biome asset has a vaultTileSet assigned yet (the field is brand new), so this smoke proves the no-override path is unchanged — the override-active visual check happens when the user authors real vault art.
-
[ ] Step 1: Enter play mode via Unity_RunCommand
UnityEditor.EditorApplication.EnterPlaymode();. Note the console error count before entering (only judge NEW entries — stale play-session noise is known). -
[ ] Step 2: Confirm the map generated with a vault — via Unity_RunCommand: find the
MapGenerator(Object.FindFirstObjectByType<ShadowRaiders.World.MapGenerator>()), assertgen.Map != null; loggen.Map.Vault != null. (A tutorial-save stage may have no vault — that's fine, log it; the null-vault guard is then what's exercised.) -
[ ] Step 3: Check console — no new errors/exceptions mentioning
ResolveTileSets,MapGenerator,NullReference. -
[ ] Step 4: Exit play mode (
UnityEditor.EditorApplication.ExitPlaymode();) — always, even on failure. -
[ ] Step 5: Report — no commit (nothing changed). State that the override-active case awaits vault art + a
vaultTileSetassignment in the editor (user step).
Out of scope (per spec)¶
Per-floor/per-theme vault sets; bush role in the vault set; authoring actual vault art or assigning the field on any biome asset; MapData/pathing/fog/minimap/material/variant changes.