Glamourer/Glamourer/Gui/DesignQuickBar.cs
2023-11-12 13:46:45 +01:00

263 lines
9.7 KiB
C#

using System;
using System.Numerics;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
using Glamourer.Automation;
using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using Glamourer.State;
using ImGuiNET;
using OtterGui;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
namespace Glamourer.Gui;
public class DesignQuickBar : Window, IDisposable
{
private ImGuiWindowFlags GetFlags
=> _config.LockDesignQuickBar
? ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoMove
: ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoFocusOnAppearing;
private readonly Configuration _config;
private readonly DesignCombo _designCombo;
private readonly StateManager _stateManager;
private readonly AutoDesignApplier _autoDesignApplier;
private readonly ObjectManager _objects;
private readonly IKeyState _keyState;
private readonly ImRaii.Style _windowPadding = new();
private readonly ImRaii.Color _windowColor = new();
private DateTime _keyboardToggle = DateTime.UnixEpoch;
public DesignQuickBar(Configuration config, DesignCombo designCombo, StateManager stateManager, IKeyState keyState,
ObjectManager objects, AutoDesignApplier autoDesignApplier)
: base("Glamourer Quick Bar", ImGuiWindowFlags.NoDecoration)
{
_config = config;
_designCombo = designCombo;
_stateManager = stateManager;
_keyState = keyState;
_objects = objects;
_autoDesignApplier = autoDesignApplier;
IsOpen = _config.ShowDesignQuickBar;
DisableWindowSounds = true;
Size = Vector2.Zero;
}
public void Dispose()
=> _windowPadding.Dispose();
public override void PreOpenCheck()
{
CheckHotkeys();
IsOpen = _config.ShowDesignQuickBar;
}
public override void PreDraw()
{
Flags = GetFlags;
Size = new Vector2(12 * ImGui.GetFrameHeight(), ImGui.GetFrameHeight());
_windowPadding.Push(ImGuiStyleVar.WindowPadding, new Vector2(ImGuiHelpers.GlobalScale * 4))
.Push(ImGuiStyleVar.WindowBorderSize, 0);
_windowColor.Push(ImGuiCol.WindowBg, ColorId.QuickDesignBg.Value())
.Push(ImGuiCol.Button, ColorId.QuickDesignButton.Value())
.Push(ImGuiCol.FrameBg, ColorId.QuickDesignFrame.Value());
}
public override void PostDraw()
{
_windowPadding.Dispose();
_windowColor.Dispose();
}
public override void Draw()
=> Draw(ImGui.GetContentRegionAvail().X);
private void Draw(float width)
{
_objects.Update();
using var group = ImRaii.Group();
var spacing = ImGui.GetStyle().ItemInnerSpacing;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
var contentRegion = width;
var buttonSize = new Vector2(ImGui.GetFrameHeight());
var comboSize = contentRegion - 3 * buttonSize.X - 3 * spacing.X;
_designCombo.Draw(comboSize);
PrepareButtons();
ImGui.SameLine();
DrawApplyButton(buttonSize);
ImGui.SameLine();
DrawRevertButton(buttonSize);
ImGui.SameLine();
DrawRevertAutomationButton(buttonSize);
}
private ActorIdentifier _playerIdentifier;
private ActorData _playerData;
private ActorState? _playerState;
private ActorData _targetData;
private ActorIdentifier _targetIdentifier;
private ActorState? _targetState;
private void PrepareButtons()
{
_objects.Update();
(_playerIdentifier, _playerData) = _objects.PlayerData;
(_targetIdentifier, _targetData) = _objects.TargetData;
if (!_stateManager.TryGetValue(_playerIdentifier, out _playerState))
_playerState = null;
if (!_stateManager.TryGetValue(_targetIdentifier, out _targetState))
_targetState = null;
}
private void DrawApplyButton(Vector2 size)
{
var design = _designCombo.Design;
var available = 0;
var tooltip = string.Empty;
if (design == null)
{
tooltip = "No design selected.";
}
else
{
if (_playerIdentifier.IsValid && _playerData.Valid)
{
available |= 1;
tooltip = $"Left-Click: Apply {(_config.IncognitoMode ? design.Incognito : design.Name)} to yourself.";
}
if (_targetIdentifier.IsValid && _targetData.Valid)
{
if (available != 0)
tooltip += '\n';
available |= 2;
tooltip += $"Right-Click: Apply {(_config.IncognitoMode ? design.Incognito : design.Name)} to {_targetIdentifier}.";
}
if (available == 0)
tooltip = "Neither player character nor target available.";
}
var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.PlayCircle, size, tooltip, available);
if (!clicked)
return;
if (state == null && !_stateManager.GetOrCreate(id, data.Objects[0], out state))
{
Glamourer.Messager.NotificationMessage($"Could not apply {design!.Incognito} to {id.Incognito(null)}: Failed to create state.");
return;
}
var (applyGear, applyCustomize) = UiHelpers.ConvertKeysToFlags();
using var _ = design!.TemporarilyRestrictApplication(applyGear, applyCustomize);
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual);
}
public void DrawRevertButton(Vector2 buttonSize)
{
var available = 0;
var tooltip = string.Empty;
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false })
{
available |= 1;
tooltip = "Left-Click: Revert the player character to their game state.";
}
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false })
{
if (available != 0)
tooltip += '\n';
available |= 2;
tooltip += $"Right-Click: Revert {_targetIdentifier} to their game state.";
}
if (available == 0)
tooltip = "Neither player character nor target are available, have state modified by Glamourer, or their state is locked.";
var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.UndoAlt, buttonSize, tooltip, available);
if (clicked)
_stateManager.ResetState(state!, StateChanged.Source.Manual);
}
public void DrawRevertAutomationButton(Vector2 buttonSize)
{
var available = 0;
var tooltip = string.Empty;
if (!_config.EnableAutoDesigns)
{
tooltip = "Automation is not enabled, you can not reset to automation state.";
}
else
{
if (_playerIdentifier.IsValid && _playerState is { IsLocked: false } && _playerData.Valid)
{
available |= 1;
tooltip = "Left-Click: Revert the player character to their automation state.";
}
if (_targetIdentifier.IsValid && _targetState is { IsLocked: false } && _targetData.Valid)
{
if (available != 0)
tooltip += '\n';
available |= 2;
tooltip += $"Right-Click: Revert {_targetIdentifier} to their automation state.";
}
if (available == 0)
tooltip = "Neither player character nor target are available, have state modified by Glamourer, or their state is locked.";
}
var (clicked, id, data, state) = ResolveTarget(FontAwesomeIcon.SyncAlt, buttonSize, tooltip, available);
if (!clicked)
{ }
else
{
foreach (var actor in data.Objects)
{
_autoDesignApplier.ReapplyAutomation(actor, id, state!);
_stateManager.ReapplyState(actor);
}
}
}
private (bool, ActorIdentifier, ActorData, ActorState?) ResolveTarget(FontAwesomeIcon icon, Vector2 buttonSize, string tooltip,
int available)
{
ImGuiUtil.DrawDisabledButton(icon.ToIconString(), buttonSize, tooltip, available == 0, true);
if ((available & 1) == 1 && ImGui.IsItemClicked(ImGuiMouseButton.Left))
return (true, _playerIdentifier, _playerData, _playerState);
if ((available & 2) == 2 && ImGui.IsItemClicked(ImGuiMouseButton.Right))
return (true, _targetIdentifier, _targetData, _targetState);
return (false, ActorIdentifier.Invalid, ActorData.Invalid, null);
}
private void CheckHotkeys()
{
if (_keyboardToggle > DateTime.UtcNow || !CheckKeyState(_config.ToggleQuickDesignBar, false))
return;
_keyboardToggle = DateTime.UtcNow.AddMilliseconds(500);
_config.ShowDesignQuickBar = !_config.ShowDesignQuickBar;
_config.Save();
}
public bool CheckKeyState(ModifiableHotkey key, bool noKey)
{
if (key.Hotkey == VirtualKey.NO_KEY)
return noKey;
return _keyState[key.Hotkey] && key.Modifier1.IsActive() && key.Modifier2.IsActive();
}
}