using AcDream.App.Input; namespace AcDream.App.UI.Layout; /// /// Projects into the imported /// retail gmFloatyPowerBarUI. The movement controller owns the clock; this /// controller owns only DAT state, fill, and window visibility. /// /// /// Retail references: gmPowerbarUI::RecvNotice_BeginPowerbar /// (0x004DA730), RecvNotice_SetPowerbarLevel (0x004DA550), /// RecvNotice_FinishPowerbar (0x004DA580), and /// ClientCombatSystem::CommenceJump (0x0056AF90). /// public sealed class JumpPowerbarController : IRetainedPanelController { public const uint LayoutId = 0x21000072u; public const uint MeterId = 0x10000034u; public const uint JumpModeState = 0x10000042u; private readonly UiMeter _meter; private readonly Func _snapshot; private readonly Action _setWindowVisible; private float _power; private bool _isCharging; private bool _hasSynced; private bool _disposed; private JumpPowerbarController( UiMeter meter, Func snapshot, Action setWindowVisible) { _meter = meter; _snapshot = snapshot; _setWindowVisible = setWindowVisible; _meter.Fill = () => _power; SyncVisibility(force: true); } public static JumpPowerbarController? Bind( ImportedLayout layout, Func snapshot, Action setWindowVisible) { ArgumentNullException.ThrowIfNull(layout); ArgumentNullException.ThrowIfNull(snapshot); ArgumentNullException.ThrowIfNull(setWindowVisible); if (layout.FindElement(MeterId) is not UiMeter meter || !meter.TrySetRetailState(JumpModeState)) return null; return new JumpPowerbarController(meter, snapshot, setWindowVisible); } /// Applies the latest movement-owned charge snapshot. public void Tick() => SyncVisibility(force: false); /// /// Re-applies the state-driven visibility after the window has been mounted. /// This covers a jump key already held during retained-UI construction. /// public void SyncVisibility() => SyncVisibility(force: true); private void SyncVisibility(bool force) { if (_disposed) return; JumpChargeSnapshot state = _snapshot(); _power = state.IsCharging ? Math.Clamp(state.Power, 0f, 1f) : 0f; if (force || !_hasSynced || state.IsCharging != _isCharging) { _isCharging = state.IsCharging; _hasSynced = true; _setWindowVisible(_isCharging); } } public void OnShown() => SyncVisibility(force: false); public void Dispose() { if (_disposed) return; _disposed = true; _power = 0f; _meter.Fill = () => null; } }