using System.Collections.Generic; using System.Numerics; namespace AcDream.App.UI.Layout; /// /// Dat property value kinds carried by a retail StateDesc. The enum mirrors /// BasePropertyType without exposing DatReaderWriter types to the retained UI. /// public enum UiPropertyKind : byte { Enum, Bool, DataId, Float, Integer, StringInfo, Color, Array, Struct, Vector, Bitfield32, Bitfield64, InstanceId, } /// Exact byte representation of a retail ARGB property value. public readonly record struct UiColorValue(byte Blue, byte Green, byte Red, byte Alpha); /// Dat-independent snapshot of a retail StringInfo. public readonly record struct UiStringInfoValue( byte Token, uint StringId, uint TableId, byte Override, byte English, byte Comment); /// /// Lossless, serializable representation of one supported retail base property. /// Only the field selected by is meaningful. Arrays and structs /// recursively preserve their members and each member's master-property id. /// public sealed class UiPropertyValue { public UiPropertyKind Kind; public uint MasterPropertyId; public ulong UnsignedValue; public int IntegerValue; public float FloatValue; public bool BoolValue; public UiStringInfoValue StringInfoValue; public UiColorValue ColorValue; public Vector3 VectorValue; public List ArrayValue = new(); public Dictionary StructValue = new(); public UiPropertyValue Clone() { var clone = new UiPropertyValue { Kind = Kind, MasterPropertyId = MasterPropertyId, UnsignedValue = UnsignedValue, IntegerValue = IntegerValue, FloatValue = FloatValue, BoolValue = BoolValue, StringInfoValue = StringInfoValue, ColorValue = ColorValue, VectorValue = VectorValue, }; foreach (var item in ArrayValue) clone.ArrayValue.Add(item.Clone()); foreach (var (key, value) in StructValue) clone.StructValue[key] = value.Clone(); return clone; } } /// /// State-scoped retail properties. Presence of a key is authoritative: an explicit /// false, zero, centered justification, or empty collection still overrides a base /// value during inheritance. /// public sealed class UiPropertyBag { public Dictionary Values = new(); public bool TryGetValue(uint id, out UiPropertyValue value) => Values.TryGetValue(id, out value!); public UiPropertyBag Clone() { var clone = new UiPropertyBag(); foreach (var (key, value) in Values) clone.Values[key] = value.Clone(); return clone; } public static UiPropertyBag Merge(UiPropertyBag baseProperties, UiPropertyBag derivedProperties) { var merged = baseProperties.Clone(); foreach (var (key, value) in derivedProperties.Values) merged.Values[key] = value.Clone(); return merged; } } /// Primary render-surface media for a retail UI state. public readonly record struct UiImageMedia(uint File, int DrawMode); /// /// Dat-independent state descriptor. DirectState uses /// so it cannot collide with UIStateId.Undef == 0. /// public sealed class UiStateInfo { public const uint DirectStateId = uint.MaxValue; public uint Id; public string Name = ""; public bool PassToChildren; public uint IncorporationFlags; public UiImageMedia? Image; public UiCursorMedia? Cursor; public UiPropertyBag Properties = new(); public UiStateInfo Clone() => new() { Id = Id, Name = Name, PassToChildren = PassToChildren, IncorporationFlags = IncorporationFlags, Image = Image, Cursor = Cursor, Properties = Properties.Clone(), }; public static UiStateInfo Merge(UiStateInfo baseState, UiStateInfo derivedState) => new() { Id = derivedState.Id, Name = derivedState.Name, PassToChildren = derivedState.PassToChildren, IncorporationFlags = derivedState.IncorporationFlags, Image = derivedState.Image ?? baseState.Image, Cursor = derivedState.Cursor ?? baseState.Cursor, Properties = UiPropertyBag.Merge(baseState.Properties, derivedState.Properties), }; }