namespace AcDream.App.UI; public enum InteractionModeKind { None, Use, Examine, UseItemOnTarget, } public readonly record struct InteractionMode( InteractionModeKind Kind, uint SourceObjectId = 0) { public static InteractionMode None => new(InteractionModeKind.None); } public readonly record struct InteractionModeTransition( InteractionMode Previous, InteractionMode Current); /// /// Single App-layer owner for retail's pointer interaction mode. Core selection /// remains session truth; this state represents temporary UI orchestration. /// public sealed class InteractionState { public InteractionMode Current { get; private set; } = InteractionMode.None; public event Action? Changed; public bool EnterUse() => Set(new InteractionMode(InteractionModeKind.Use)); public bool EnterExamine() => Set(new InteractionMode(InteractionModeKind.Examine)); public bool EnterUseItemOnTarget(uint sourceObjectId) { if (sourceObjectId == 0) throw new ArgumentOutOfRangeException(nameof(sourceObjectId)); return Set(new InteractionMode(InteractionModeKind.UseItemOnTarget, sourceObjectId)); } public bool Clear() => Set(InteractionMode.None); private bool Set(InteractionMode mode) { if (Current == mode) return false; InteractionMode previous = Current; Current = mode; Changed?.Invoke(new InteractionModeTransition(previous, mode)); return true; } }