using System; using System.Globalization; namespace AcDream.Core.Items; /// /// Shared owner for retail GenItemHolder::splitSize and /// GenItemHolder::maxSplitSize. The toolbar entry/slider edits this state and /// item-holder operations consume the same value. /// public sealed class StackSplitQuantityState { public uint Value { get; private set; } = 1u; public uint Maximum { get; private set; } = 1u; public float Ratio => Value / (float)Maximum; public event Action? Changed; public void Reset(uint stackSize, uint? initialValue = null) { uint maximum = Math.Max(stackSize, 1u); Set(initialValue ?? maximum, maximum); } public void SetFromText(string? text) { // Retail uses wcstoul, whose invalid/empty result is zero, then clamps to one. uint parsed = uint.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out uint value) ? value : 0u; SetValue(parsed); } /// /// Ports gmToolbarUI::ListenToElementMessage @ 0x004BEFE0 and /// UIElement_Scrollbar::SetScrollbarPosition @ 0x00470EC0. The scrollbar /// first truncates its normalized position to an integer in [0,1000], then the /// toolbar converts it to 1 + floor(position * max / 1000) and clamps. /// public void SetFromSliderRatio(float ratio) { float clamped = Math.Clamp(ratio, 0f, 1f); uint positionMillis = (uint)MathF.Truncate(clamped * 1000f); ulong scaled = (ulong)positionMillis * Maximum; uint value = 1u + (uint)(scaled / 1000u); SetValue(value); } public void SetValue(uint value) => Set(value, Maximum); /// /// Ports ItemHolder::GetObjectSplitSize @ 0x00586F00: only the /// currently selected object consumes GenItemHolder::splitSize. /// Every other object operation uses that object's full stack size. /// public uint GetObjectSplitSize(uint objectId, uint selectedObjectId, uint objectStackSize) { if (objectId == selectedObjectId) return Value; return Math.Max(objectStackSize, 1u); } private void Set(uint value, uint maximum) { maximum = Math.Max(maximum, 1u); value = Math.Clamp(value, 1u, maximum); if (Value == value && Maximum == maximum) return; Value = value; Maximum = maximum; Changed?.Invoke(); } }