using System; namespace IslaApocalypse.Core { /// /// A material's IDENTITY. → `Design - Data - Material Schema.md`, D-054. /// /// ⚠⚠ IDENTITY IS A REGISTRY KEY, NEVER A POSITIONAL ORDINAL. /// /// This is deliberately a stable string key and not an enum. The prototype's blueprint format /// keyed on enum ordinals, which made the enum's DECLARATION ORDER a wire format: inserting a /// value in the middle silently reinterpreted every world already generated /// (→ `Design - Tooling - Offline Generation.md`: "anything that changes its format or its enum /// ordinals breaks every world already generated"). The registry is also the mod surface /// (→ D-011, D-043) — a mod adds rows, and rows cannot be given ordinals by a third party. /// /// So: adding a material is one new row, appended anywhere, in any order, by anyone, forever. /// /// No serialization is built this phase. This type exists so that when it is, the identity it /// writes is already the key rather than a position. /// public readonly struct MaterialKey : IEquatable { /// The stable identity. Lowercase, dot-namespaced: "core.limestone", "mod.basalt". public readonly string Value; public MaterialKey(string value) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("A material key may not be null or blank.", nameof(value)); Value = value; } /// The unset key. Distinguishable from any real row; never registered. public static readonly MaterialKey None = default; public bool IsNone => Value == null; public bool Equals(MaterialKey other) => string.Equals(Value, other.Value, StringComparison.Ordinal); public override bool Equals(object obj) => obj is MaterialKey k && Equals(k); public override int GetHashCode() => Value == null ? 0 : StringComparer.Ordinal.GetHashCode(Value); public override string ToString() => Value ?? ""; public static bool operator ==(MaterialKey a, MaterialKey b) => a.Equals(b); public static bool operator !=(MaterialKey a, MaterialKey b) => !a.Equals(b); } }