namespace IslaApocalypse.Core
{
///
/// A row in the TERRAIN material schema — what the natural world is made of.
/// → `Design - Data - Material Schema.md` §1, D-054.
///
/// PROPERTIES-FROM-TYPE: a column run stores only a . Every property
/// lives here and is reached by registry lookup. Nothing is ever stored per-run.
/// → `Design - Data - Column Model.md`.
///
/// ⚠ THERE IS NO MESH-STYLE FIELD ON THIS ROW, AND THERE MUST NEVER BE ONE.
/// Mesh style is per-ORIGIN, not per-material: natural terrain meshes smooth, player-placed
/// blocks mesh as cubes, and the same material can be both. The origin flag lives on the run
/// (), which is where the mesher reads it.
/// → D-054 open notes ("material does not override mesh style"), `Design - Rendering - Mesher.md`.
///
/// APPEND-ONLY. Adding a material is one new row in — never a
/// code change anywhere else. No material logic is hardcoded anywhere.
///
public sealed class TerrainMaterial
{
/// Identity. A registry key, not an ordinal. → .
public MaterialKey Key { get; }
/// Human-facing name. Presentation only — never an identity, never a lookup key.
public string DisplayName { get; }
/// Mining difficulty. Units deliberately unfixed this phase; relative for now.
public float Hardness { get; }
///
/// What a mined voxel gives. SEAM: the resource/item schema is deferred, so this currently
/// names a material key. When resources become their own schema this becomes a resource key
/// — one field's type, not a redesign.
///
public MaterialKey YieldKey { get; }
/// How much one mined voxel gives.
public int YieldPerVoxel { get; }
///
/// How much structural support this material carries as NATURAL terrain.
/// Consumed by `Design - Systems - Structural Integrity.md` (stability scalar, D-055).
/// Natural terrain is a LAZY anchor there — this is the value it anchors with.
///
public float NaturalSupportStrength { get; }
///
/// Runtime-only dense index, assigned by the registry in registration order.
///
/// ⚠⚠ NEVER SERIALIZE THIS. NEVER SEND IT OVER A WIRE. NEVER STORE IT IN A BLUEPRINT.
/// It exists solely so hot loops can index dense arrays instead of hashing strings. It
/// changes whenever registration order changes — which a mod adding a row will do. The
/// identity that persists is , always.
///
public int RuntimeIndex { get; internal set; } = -1;
public TerrainMaterial(
MaterialKey key,
string displayName,
float hardness,
MaterialKey yieldKey,
int yieldPerVoxel,
float naturalSupportStrength)
{
Key = key;
DisplayName = displayName;
Hardness = hardness;
YieldKey = yieldKey;
YieldPerVoxel = yieldPerVoxel;
NaturalSupportStrength = naturalSupportStrength;
}
public override string ToString() => $"TerrainMaterial({Key})";
}
}