mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +01:00
Test state done.
This commit is contained in:
parent
129f9e070f
commit
b37167f2dd
18 changed files with 521 additions and 156 deletions
|
|
@ -77,7 +77,11 @@ public partial class GlamourerIpc
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateManager.ApplyDesign(design, state, StateChanged.Source.Ipc);
|
if (state.CanUnlock(0xDEADBEEF))
|
||||||
|
{
|
||||||
|
_stateManager.ApplyDesign(design, state, StateChanged.Source.Ipc, 0xDEADBEEF);
|
||||||
|
state.Lock(0xDEADBEEF);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,9 @@ public partial class GlamourerIpc
|
||||||
foreach (var id in actors)
|
foreach (var id in actors)
|
||||||
{
|
{
|
||||||
if (_stateManager.TryGetValue(id, out var state))
|
if (_stateManager.TryGetValue(id, out var state))
|
||||||
_stateManager.ResetState(state);
|
{
|
||||||
|
_stateManager.ResetState(state, 0xDEADBEEF);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
||||||
public bool IncognitoMode { get; set; } = false;
|
public bool IncognitoMode { get; set; } = false;
|
||||||
public bool UnlockDetailMode { get; set; } = true;
|
public bool UnlockDetailMode { get; set; } = true;
|
||||||
public bool SkipInvalidCustomizations { get; set; } = false;
|
public bool SkipInvalidCustomizations { get; set; } = false;
|
||||||
|
public bool HideApplyCheckmarks { get; set; } = false;
|
||||||
public MainWindow.TabType SelectedTab { get; set; } = MainWindow.TabType.Settings;
|
public MainWindow.TabType SelectedTab { get; set; } = MainWindow.TabType.Settings;
|
||||||
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
|
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace Glamourer.Gui;
|
||||||
|
|
||||||
public enum ColorId
|
public enum ColorId
|
||||||
{
|
{
|
||||||
|
NormalDesign,
|
||||||
CustomizationDesign,
|
CustomizationDesign,
|
||||||
StateDesign,
|
StateDesign,
|
||||||
EquipmentDesign,
|
EquipmentDesign,
|
||||||
|
|
@ -25,8 +26,9 @@ public static class Colors
|
||||||
=> color switch
|
=> color switch
|
||||||
{
|
{
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
ColorId.NormalDesign => (0xFFFFFFFF, "Normal Design", "A design with no specific traits." ),
|
||||||
ColorId.CustomizationDesign => (0xFFC000C0, "Customization Design", "A design that only changes customizations on a character." ),
|
ColorId.CustomizationDesign => (0xFFC000C0, "Customization Design", "A design that only changes customizations on a character." ),
|
||||||
ColorId.StateDesign => (0xFF00C0C0, "State Design", "A design that only changes meta state on a character." ),
|
ColorId.StateDesign => (0xFF00C0C0, "State Design", "A design that does not change equipment or customizations on a character." ),
|
||||||
ColorId.EquipmentDesign => (0xFF00C000, "Equipment Design", "A design that only changes equipment on a character." ),
|
ColorId.EquipmentDesign => (0xFF00C000, "Equipment Design", "A design that only changes equipment on a character." ),
|
||||||
ColorId.ActorAvailable => (0xFF18C018, "Actor Available", "The header in the Actor tab panel if the currently selected actor exists in the game world at least once." ),
|
ColorId.ActorAvailable => (0xFF18C018, "Actor Available", "The header in the Actor tab panel if the currently selected actor exists in the game world at least once." ),
|
||||||
ColorId.ActorUnavailable => (0xFF1818C0, "Actor Unavailable", "The Header in the Actor tab panel if the currently selected actor does not exist in the game world." ),
|
ColorId.ActorUnavailable => (0xFF1818C0, "Actor Unavailable", "The Header in the Actor tab panel if the currently selected actor does not exist in the game world." ),
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,13 @@ public partial class CustomizationDrawer
|
||||||
using (var group = ImRaii.Group())
|
using (var group = ImRaii.Group())
|
||||||
{
|
{
|
||||||
DataInputInt(current);
|
DataInputInt(current);
|
||||||
|
if (_withApply)
|
||||||
|
{
|
||||||
|
ApplyCheckbox();
|
||||||
|
ImGui.SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted(custom.Color == 0 ? $"{_currentOption} (Custom #{custom.Value})" : _currentOption);
|
ImGui.TextUnformatted(custom.Color == 0 ? $"{_currentOption} (Custom #{custom.Value})" : _currentOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
|
|
@ -19,6 +20,17 @@ public partial class CustomizationDrawer
|
||||||
DrawRaceCombo();
|
DrawRaceCombo();
|
||||||
var gender = _service.AwaitedService.GetName(CustomName.Gender);
|
var gender = _service.AwaitedService.GetName(CustomName.Gender);
|
||||||
var clan = _service.AwaitedService.GetName(CustomName.Clan);
|
var clan = _service.AwaitedService.GetName(CustomName.Clan);
|
||||||
|
if (_withApply)
|
||||||
|
{
|
||||||
|
if (UiHelpers.DrawCheckbox("##applyGender", "Apply gender of this design.", _currentApply, out var applyGender, _locked))
|
||||||
|
ChangeApply = applyGender ? ChangeApply | CustomizeFlag.Gender : ChangeApply & ~CustomizeFlag.Gender;
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (UiHelpers.DrawCheckbox("##applyClan", "Apply clan of this design.", _currentApply, out var applyClan, _locked))
|
||||||
|
ChangeApply = applyClan ? ChangeApply | CustomizeFlag.Clan : ChangeApply & ~CustomizeFlag.Clan;
|
||||||
|
ImGui.SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted($"{gender} & {clan}");
|
ImGui.TextUnformatted($"{gender} & {clan}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,7 +44,8 @@ public partial class CustomizationDrawer
|
||||||
_ => FontAwesomeIcon.Question,
|
_ => FontAwesomeIcon.Question,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!ImGuiUtil.DrawDisabledButton(icon.ToIconString(), _framedIconSize, string.Empty, icon is not FontAwesomeIcon.Mars and not FontAwesomeIcon.Venus, true))
|
if (!ImGuiUtil.DrawDisabledButton(icon.ToIconString(), _framedIconSize, string.Empty,
|
||||||
|
icon is not FontAwesomeIcon.Mars and not FontAwesomeIcon.Venus, true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Changed |= _service.ChangeGender(ref _customize, icon is FontAwesomeIcon.Mars ? Gender.Female : Gender.Male);
|
Changed |= _service.ChangeGender(ref _customize, icon is FontAwesomeIcon.Mars ? Gender.Female : Gender.Male);
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,12 @@ public partial class CustomizationDrawer
|
||||||
else
|
else
|
||||||
DataInputInt(current);
|
DataInputInt(current);
|
||||||
|
|
||||||
|
if (_withApply)
|
||||||
|
{
|
||||||
|
ApplyCheckbox();
|
||||||
|
ImGui.SameLine();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.TextUnformatted($"{label} ({custom.Value.Value})");
|
ImGui.TextUnformatted($"{label} ({custom.Value.Value})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,16 +121,45 @@ public partial class CustomizationDrawer
|
||||||
DrawMultiIcons();
|
DrawMultiIcons();
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using var group = ImRaii.Group();
|
using var group = ImRaii.Group();
|
||||||
ImGui.Dummy(new Vector2(0, ImGui.GetTextLineHeightWithSpacing() + ImGui.GetStyle().ItemSpacing.Y / 2));
|
|
||||||
|
|
||||||
_currentCount = 256;
|
_currentCount = 256;
|
||||||
PercentageInputInt();
|
if (_withApply)
|
||||||
|
{
|
||||||
|
ApplyCheckbox(CustomizeIndex.FacialFeature1);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + _spacing.X);
|
||||||
|
ApplyCheckbox(CustomizeIndex.FacialFeature2);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ApplyCheckbox(CustomizeIndex.FacialFeature3);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ApplyCheckbox(CustomizeIndex.FacialFeature4);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.TextUnformatted(_set.Option(CustomizeIndex.LegacyTattoo));
|
PercentageInputInt();
|
||||||
if (_set.DataByValue(CustomizeIndex.Face, _customize.Face, out _, _customize.Face) < 0)
|
if (_set.DataByValue(CustomizeIndex.Face, _customize.Face, out _, _customize.Face) < 0)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted("(Using Face 1)");
|
ImGui.TextUnformatted("(Using Face 1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + _spacing.Y);
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted(_set.Option(CustomizeIndex.LegacyTattoo));
|
||||||
|
|
||||||
|
if (_withApply)
|
||||||
|
{
|
||||||
|
ApplyCheckbox(CustomizeIndex.FacialFeature5);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + _spacing.X);
|
||||||
|
ApplyCheckbox(CustomizeIndex.FacialFeature6);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ApplyCheckbox(CustomizeIndex.FacialFeature7);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ApplyCheckbox(CustomizeIndex.LegacyTattoo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawMultiIcons()
|
private void DrawMultiIcons()
|
||||||
{
|
{
|
||||||
var options = _set.Order[CharaMakeParams.MenuType.IconCheckmark];
|
var options = _set.Order[CharaMakeParams.MenuType.IconCheckmark];
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,14 @@ public partial class CustomizationDrawer
|
||||||
DrawPercentageSlider();
|
DrawPercentageSlider();
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
PercentageInputInt();
|
PercentageInputInt();
|
||||||
|
if (_withApply)
|
||||||
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
ApplyCheckbox();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted(_currentOption);
|
ImGui.TextUnformatted(_currentOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,7 +67,14 @@ public partial class CustomizationDrawer
|
||||||
ListCombo();
|
ListCombo();
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ListInputInt();
|
ListInputInt();
|
||||||
|
if (_withApply)
|
||||||
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
ApplyCheckbox();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted(_currentOption);
|
ImGui.TextUnformatted(_currentOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,10 +107,41 @@ public partial class CustomizationDrawer
|
||||||
{
|
{
|
||||||
using var id = SetId(idx);
|
using var id = SetId(idx);
|
||||||
var tmp = _currentByte != CustomizeValue.Zero;
|
var tmp = _currentByte != CustomizeValue.Zero;
|
||||||
if (ImGui.Checkbox(_currentOption, ref tmp))
|
if (_withApply)
|
||||||
|
{
|
||||||
|
switch (UiHelpers.DrawMetaToggle(_currentOption, string.Empty, tmp, _currentApply, out var newValue, out var newApply, _locked))
|
||||||
|
{
|
||||||
|
case DataChange.Item:
|
||||||
|
ChangeApply = newApply ? ChangeApply | _currentFlag : ChangeApply & ~_currentFlag;
|
||||||
|
_customize.Set(idx, tmp ? CustomizeValue.Max : CustomizeValue.Zero);
|
||||||
|
Changed |= _currentFlag;
|
||||||
|
break;
|
||||||
|
case DataChange.ApplyItem:
|
||||||
|
ChangeApply = newApply ? ChangeApply | _currentFlag : ChangeApply & ~_currentFlag;
|
||||||
|
break;
|
||||||
|
case DataChange.Item | DataChange.ApplyItem:
|
||||||
|
_customize.Set(idx, tmp ? CustomizeValue.Max : CustomizeValue.Zero);
|
||||||
|
Changed |= _currentFlag;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ImGui.Checkbox(_currentOption, ref tmp))
|
||||||
{
|
{
|
||||||
_customize.Set(idx, tmp ? CustomizeValue.Max : CustomizeValue.Zero);
|
_customize.Set(idx, tmp ? CustomizeValue.Max : CustomizeValue.Zero);
|
||||||
Changed |= _currentFlag;
|
Changed |= _currentFlag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ApplyCheckbox()
|
||||||
|
{
|
||||||
|
if (UiHelpers.DrawCheckbox("##apply", $"Apply the {_currentOption} customization in this design.", _currentApply, out _, _locked))
|
||||||
|
ToggleApply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyCheckbox(CustomizeIndex index)
|
||||||
|
{
|
||||||
|
SetId(index);
|
||||||
|
if (UiHelpers.DrawCheckbox("##apply", $"Apply the {_currentOption} customization in this design.", _currentApply, out _, _locked))
|
||||||
|
ToggleApply();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ namespace Glamourer.Gui.Customization;
|
||||||
public partial class CustomizationDrawer : IDisposable
|
public partial class CustomizationDrawer : IDisposable
|
||||||
{
|
{
|
||||||
private readonly CodeService _codes;
|
private readonly CodeService _codes;
|
||||||
|
private readonly Configuration _config;
|
||||||
|
|
||||||
private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f);
|
private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f);
|
||||||
private readonly ImGuiScene.TextureWrap? _legacyTattoo;
|
private readonly ImGuiScene.TextureWrap? _legacyTattoo;
|
||||||
|
|
@ -29,23 +30,28 @@ public partial class CustomizationDrawer : IDisposable
|
||||||
|
|
||||||
public CustomizeFlag CurrentFlag { get; private set; }
|
public CustomizeFlag CurrentFlag { get; private set; }
|
||||||
public CustomizeFlag Changed { get; private set; }
|
public CustomizeFlag Changed { get; private set; }
|
||||||
|
public CustomizeFlag ChangeApply { get; private set; }
|
||||||
|
|
||||||
public bool RequiresRedraw
|
public bool RequiresRedraw
|
||||||
=> Changed.RequiresRedraw();
|
=> Changed.RequiresRedraw();
|
||||||
|
|
||||||
private bool _locked = false;
|
private bool _locked = false;
|
||||||
|
private Vector2 _defaultSpacing;
|
||||||
|
private Vector2 _spacing;
|
||||||
private Vector2 _iconSize;
|
private Vector2 _iconSize;
|
||||||
private Vector2 _framedIconSize;
|
private Vector2 _framedIconSize;
|
||||||
private float _inputIntSize;
|
private float _inputIntSize;
|
||||||
private float _comboSelectorSize;
|
private float _comboSelectorSize;
|
||||||
private float _raceSelectorWidth;
|
private float _raceSelectorWidth;
|
||||||
|
private bool _withApply;
|
||||||
|
|
||||||
private readonly CustomizationService _service;
|
private readonly CustomizationService _service;
|
||||||
|
|
||||||
public CustomizationDrawer(DalamudPluginInterface pi, CustomizationService service, CodeService codes)
|
public CustomizationDrawer(DalamudPluginInterface pi, CustomizationService service, CodeService codes, Configuration config)
|
||||||
{
|
{
|
||||||
_service = service;
|
_service = service;
|
||||||
_codes = codes;
|
_codes = codes;
|
||||||
|
_config = config;
|
||||||
_legacyTattoo = GetLegacyTattooIcon(pi);
|
_legacyTattoo = GetLegacyTattooIcon(pi);
|
||||||
_customize = Customize.Default;
|
_customize = Customize.Default;
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +64,17 @@ public partial class CustomizationDrawer : IDisposable
|
||||||
public bool Draw(Customize current, bool locked)
|
public bool Draw(Customize current, bool locked)
|
||||||
{
|
{
|
||||||
CurrentFlag = CustomizeFlagExtensions.All;
|
CurrentFlag = CustomizeFlagExtensions.All;
|
||||||
|
_withApply = false;
|
||||||
|
Init(current, locked);
|
||||||
|
|
||||||
|
return DrawInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Draw(Customize current, CustomizeFlag apply, bool locked)
|
||||||
|
{
|
||||||
|
CurrentFlag = CustomizeFlagExtensions.All;
|
||||||
|
ChangeApply = apply;
|
||||||
|
_withApply = !_config.HideApplyCheckmarks;
|
||||||
Init(current, locked);
|
Init(current, locked);
|
||||||
return DrawInternal();
|
return DrawInternal();
|
||||||
}
|
}
|
||||||
|
|
@ -75,6 +92,7 @@ public partial class CustomizationDrawer : IDisposable
|
||||||
private CustomizeIndex _currentIndex;
|
private CustomizeIndex _currentIndex;
|
||||||
private CustomizeFlag _currentFlag;
|
private CustomizeFlag _currentFlag;
|
||||||
private CustomizeValue _currentByte = CustomizeValue.Zero;
|
private CustomizeValue _currentByte = CustomizeValue.Zero;
|
||||||
|
private bool _currentApply;
|
||||||
private int _currentCount;
|
private int _currentCount;
|
||||||
private string _currentOption = string.Empty;
|
private string _currentOption = string.Empty;
|
||||||
|
|
||||||
|
|
@ -83,6 +101,7 @@ public partial class CustomizationDrawer : IDisposable
|
||||||
{
|
{
|
||||||
_currentIndex = index;
|
_currentIndex = index;
|
||||||
_currentFlag = index.ToFlag();
|
_currentFlag = index.ToFlag();
|
||||||
|
_currentApply = ChangeApply.HasFlag(_currentFlag);
|
||||||
_currentByte = _customize[index];
|
_currentByte = _customize[index];
|
||||||
_currentCount = _set.Count(index, _customize.Face);
|
_currentCount = _set.Count(index, _customize.Face);
|
||||||
_currentOption = _set.Option(index);
|
_currentOption = _set.Option(index);
|
||||||
|
|
@ -99,8 +118,23 @@ public partial class CustomizationDrawer : IDisposable
|
||||||
Changed |= _currentFlag;
|
Changed |= _currentFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the current Apply value.
|
||||||
|
private void ToggleApply()
|
||||||
|
{
|
||||||
|
_currentApply = !_currentApply;
|
||||||
|
ChangeApply = _currentApply ? ChangeApply | _currentFlag : ChangeApply & ~_currentFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DrawWetnessState(bool currentValue, out bool newValue, bool locked)
|
||||||
|
=> UiHelpers.DrawCheckbox("Force Wetness", "Force the character to be wet or not.", currentValue, out newValue, locked);
|
||||||
|
|
||||||
|
public DataChange DrawWetnessState(bool currentValue, bool currentApply, out bool newValue, out bool newApply, bool locked)
|
||||||
|
=> UiHelpers.DrawMetaToggle("Force Wetness", "Change the characters forced wetness state: Disabled, Enabled or Don't Apply.",
|
||||||
|
currentValue, currentApply, out newValue, out newApply, locked);
|
||||||
|
|
||||||
private bool DrawInternal()
|
private bool DrawInternal()
|
||||||
{
|
{
|
||||||
|
using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _spacing);
|
||||||
using var disabled = ImRaii.Disabled(_locked);
|
using var disabled = ImRaii.Disabled(_locked);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -124,7 +158,7 @@ public partial class CustomizationDrawer : IDisposable
|
||||||
Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.ColorPicker], DrawColorPicker, ImGui.SameLine);
|
Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.ColorPicker], DrawColorPicker, ImGui.SameLine);
|
||||||
|
|
||||||
Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.Checkmark], DrawCheckbox,
|
Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.Checkmark], DrawCheckbox,
|
||||||
() => ImGui.SameLine(_inputIntSize + _framedIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X));
|
() => ImGui.SameLine(_comboSelectorSize - _framedIconSize.X + _spacing.X));
|
||||||
return Changed != 0;
|
return Changed != 0;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -144,9 +178,8 @@ public partial class CustomizationDrawer : IDisposable
|
||||||
using var id = ImRaii.PushId(i);
|
using var id = ImRaii.PushId(i);
|
||||||
int value = _customize.Data.Data[i];
|
int value = _customize.Data.Data[i];
|
||||||
ImGui.SetNextItemWidth(40 * ImGuiHelpers.GlobalScale);
|
ImGui.SetNextItemWidth(40 * ImGuiHelpers.GlobalScale);
|
||||||
if (!ImGui.InputInt(string.Empty, ref value, 0, 0))
|
if (ImGui.InputInt(string.Empty, ref value, 0, 0))
|
||||||
continue;
|
{
|
||||||
|
|
||||||
var newValue = (byte)Math.Clamp(value, 0, byte.MaxValue);
|
var newValue = (byte)Math.Clamp(value, 0, byte.MaxValue);
|
||||||
if (newValue != _customize.Data.Data[i])
|
if (newValue != _customize.Data.Data[i])
|
||||||
foreach (var flag in Enum.GetValues<CustomizeIndex>())
|
foreach (var flag in Enum.GetValues<CustomizeIndex>())
|
||||||
|
|
@ -158,16 +191,19 @@ public partial class CustomizationDrawer : IDisposable
|
||||||
|
|
||||||
_customize.Data.Data[i] = newValue;
|
_customize.Data.Data[i] = newValue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Changed != 0;
|
return Changed != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateSizes()
|
private void UpdateSizes()
|
||||||
{
|
{
|
||||||
_iconSize = new Vector2(ImGui.GetTextLineHeightWithSpacing() * 2);
|
_defaultSpacing = ImGui.GetStyle().ItemSpacing;
|
||||||
|
_spacing = ImGui.GetStyle().ItemSpacing with { X = ImGui.GetStyle().ItemInnerSpacing.X };
|
||||||
|
_iconSize = new Vector2(ImGui.GetTextLineHeight() * 2 + ImGui.GetStyle().ItemSpacing.Y + 2 * ImGui.GetStyle().FramePadding.Y);
|
||||||
_framedIconSize = _iconSize + 2 * ImGui.GetStyle().FramePadding;
|
_framedIconSize = _iconSize + 2 * ImGui.GetStyle().FramePadding;
|
||||||
_inputIntSize = 2 * _framedIconSize.X + ImGui.GetStyle().ItemSpacing.X;
|
_inputIntSize = 2 * _framedIconSize.X + 1 * _spacing.X;
|
||||||
_comboSelectorSize = 4 * _framedIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X;
|
_comboSelectorSize = 4 * _framedIconSize.X + 3 * _spacing.X;
|
||||||
_raceSelectorWidth = _inputIntSize + _comboSelectorSize - _framedIconSize.X;
|
_raceSelectorWidth = _inputIntSize + _comboSelectorSize - _framedIconSize.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ namespace Glamourer.Gui.Equipment;
|
||||||
|
|
||||||
public class EquipmentDrawer
|
public class EquipmentDrawer
|
||||||
{
|
{
|
||||||
|
private const float DefaultWidth = 280;
|
||||||
|
|
||||||
private readonly ItemManager _items;
|
private readonly ItemManager _items;
|
||||||
private readonly FilterComboColors _stainCombo;
|
private readonly FilterComboColors _stainCombo;
|
||||||
private readonly StainData _stainData;
|
private readonly StainData _stainData;
|
||||||
|
|
@ -27,14 +29,16 @@ public class EquipmentDrawer
|
||||||
private readonly Dictionary<FullEquipType, WeaponCombo> _weaponCombo;
|
private readonly Dictionary<FullEquipType, WeaponCombo> _weaponCombo;
|
||||||
private readonly CodeService _codes;
|
private readonly CodeService _codes;
|
||||||
private readonly TextureService _textures;
|
private readonly TextureService _textures;
|
||||||
|
private readonly Configuration _config;
|
||||||
|
|
||||||
public EquipmentDrawer(DataManager gameData, ItemManager items, CodeService codes, TextureService textures)
|
public EquipmentDrawer(DataManager gameData, ItemManager items, CodeService codes, TextureService textures, Configuration config)
|
||||||
{
|
{
|
||||||
_items = items;
|
_items = items;
|
||||||
_codes = codes;
|
_codes = codes;
|
||||||
_textures = textures;
|
_textures = textures;
|
||||||
|
_config = config;
|
||||||
_stainData = items.Stains;
|
_stainData = items.Stains;
|
||||||
_stainCombo = new FilterComboColors(280,
|
_stainCombo = new FilterComboColors(DefaultWidth - 20,
|
||||||
_stainData.Data.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))));
|
_stainData.Data.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))));
|
||||||
_itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, textures)).ToArray();
|
_itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e, textures)).ToArray();
|
||||||
_weaponCombo = new Dictionary<FullEquipType, WeaponCombo>(FullEquipTypeExtensions.WeaponTypes.Count * 2);
|
_weaponCombo = new Dictionary<FullEquipType, WeaponCombo>(FullEquipTypeExtensions.WeaponTypes.Count * 2);
|
||||||
|
|
@ -55,7 +59,7 @@ public class EquipmentDrawer
|
||||||
public void Prepare()
|
public void Prepare()
|
||||||
{
|
{
|
||||||
_iconSize = new Vector2(2 * ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.Y);
|
_iconSize = new Vector2(2 * ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.Y);
|
||||||
_comboLength = 300 * ImGuiHelpers.GlobalScale;
|
_comboLength = DefaultWidth * ImGuiHelpers.GlobalScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool VerifyRestrictedGear(EquipSlot slot, EquipItem gear, Gender gender, Race race)
|
private bool VerifyRestrictedGear(EquipSlot slot, EquipItem gear, Gender gender, Race race)
|
||||||
|
|
@ -67,45 +71,35 @@ public class EquipmentDrawer
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum EquipChange : byte
|
|
||||||
{
|
|
||||||
None = 0x00,
|
|
||||||
Item = 0x01,
|
|
||||||
Stain = 0x02,
|
|
||||||
ApplyItem = 0x04,
|
|
||||||
ApplyStain = 0x08,
|
|
||||||
Item2 = 0x10,
|
|
||||||
Stain2 = 0x20,
|
|
||||||
ApplyItem2 = 0x40,
|
|
||||||
ApplyStain2 = 0x80,
|
|
||||||
}
|
|
||||||
|
|
||||||
public EquipChange DrawEquip(EquipSlot slot, in DesignData designData, out EquipItem rArmor, out StainId rStain, EquipFlag? cApply,
|
public DataChange DrawEquip(EquipSlot slot, in DesignData designData, out EquipItem rArmor, out StainId rStain, EquipFlag? cApply,
|
||||||
out bool rApply, out bool rApplyStain, bool locked)
|
out bool rApply, out bool rApplyStain, bool locked)
|
||||||
=> DrawEquip(slot, designData.Item(slot), out rArmor, designData.Stain(slot), out rStain, cApply, out rApply, out rApplyStain, locked,
|
=> DrawEquip(slot, designData.Item(slot), out rArmor, designData.Stain(slot), out rStain, cApply, out rApply, out rApplyStain, locked,
|
||||||
designData.Customize.Gender, designData.Customize.Race);
|
designData.Customize.Gender, designData.Customize.Race);
|
||||||
|
|
||||||
public EquipChange DrawEquip(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain, EquipFlag? cApply,
|
public DataChange DrawEquip(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain, EquipFlag? cApply,
|
||||||
out bool rApply, out bool rApplyStain, bool locked, Gender gender = Gender.Unknown, Race race = Race.Unknown)
|
out bool rApply, out bool rApplyStain, bool locked, Gender gender = Gender.Unknown, Race race = Race.Unknown)
|
||||||
{
|
{
|
||||||
|
if (_config.HideApplyCheckmarks)
|
||||||
|
cApply = null;
|
||||||
|
|
||||||
if (!locked && _codes.EnabledArtisan)
|
if (!locked && _codes.EnabledArtisan)
|
||||||
return DrawEquipArtisan(slot, cArmor, out rArmor, cStain, out rStain, cApply, out rApply, out rApplyStain);
|
return DrawEquipArtisan(slot, cArmor, out rArmor, cStain, out rStain, cApply, out rApply, out rApplyStain);
|
||||||
|
|
||||||
var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y };
|
var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y };
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
|
||||||
|
|
||||||
var changes = EquipChange.None;
|
var changes = DataChange.None;
|
||||||
cArmor.DrawIcon(_textures, _iconSize);
|
cArmor.DrawIcon(_textures, _iconSize);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using var group = ImRaii.Group();
|
using var group = ImRaii.Group();
|
||||||
if (DrawItem(slot, cArmor, out rArmor, out var label, locked))
|
if (DrawItem(slot, cArmor, out rArmor, out var label, locked))
|
||||||
changes |= EquipChange.Item;
|
changes |= DataChange.Item;
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawApply(slot, cApply.Value, out rApply, locked))
|
if (DrawApply(slot, cApply.Value, out rApply, locked))
|
||||||
changes |= EquipChange.ApplyItem;
|
changes |= DataChange.ApplyItem;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -115,12 +109,12 @@ public class EquipmentDrawer
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(label);
|
ImGui.TextUnformatted(label);
|
||||||
if (DrawStain(slot, cStain, out rStain, locked))
|
if (DrawStain(slot, cStain, out rStain, locked))
|
||||||
changes |= EquipChange.Stain;
|
changes |= DataChange.Stain;
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawApplyStain(slot, cApply.Value, out rApplyStain, locked))
|
if (DrawApplyStain(slot, cApply.Value, out rApplyStain, locked))
|
||||||
changes |= EquipChange.ApplyStain;
|
changes |= DataChange.ApplyStain;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -136,18 +130,21 @@ public class EquipmentDrawer
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EquipChange DrawWeapons(in DesignData designData, out EquipItem rMainhand, out EquipItem rOffhand, out StainId rMainhandStain,
|
public DataChange DrawWeapons(in DesignData designData, out EquipItem rMainhand, out EquipItem rOffhand, out StainId rMainhandStain,
|
||||||
out StainId rOffhandStain, EquipFlag? cApply, out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand,
|
out StainId rOffhandStain, EquipFlag? cApply, out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand,
|
||||||
out bool rApplyOffhandStain, bool locked)
|
out bool rApplyOffhandStain, bool locked)
|
||||||
=> DrawWeapons(designData.Item(EquipSlot.MainHand), out rMainhand, designData.Item(EquipSlot.OffHand), out rOffhand,
|
=> DrawWeapons(designData.Item(EquipSlot.MainHand), out rMainhand, designData.Item(EquipSlot.OffHand), out rOffhand,
|
||||||
designData.Stain(EquipSlot.MainHand), out rMainhandStain, designData.Stain(EquipSlot.OffHand), out rOffhandStain, cApply,
|
designData.Stain(EquipSlot.MainHand), out rMainhandStain, designData.Stain(EquipSlot.OffHand), out rOffhandStain, cApply,
|
||||||
out rApplyMainhand, out rApplyMainhandStain, out rApplyOffhand, out rApplyOffhandStain, locked);
|
out rApplyMainhand, out rApplyMainhandStain, out rApplyOffhand, out rApplyOffhandStain, locked);
|
||||||
|
|
||||||
public EquipChange DrawWeapons(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
public DataChange DrawWeapons(EquipItem cMainhand, out EquipItem rMainhand, EquipItem cOffhand, out EquipItem rOffhand,
|
||||||
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain, EquipFlag? cApply,
|
StainId cMainhandStain, out StainId rMainhandStain, StainId cOffhandStain, out StainId rOffhandStain, EquipFlag? cApply,
|
||||||
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked)
|
out bool rApplyMainhand, out bool rApplyMainhandStain, out bool rApplyOffhand, out bool rApplyOffhandStain, bool locked)
|
||||||
{
|
{
|
||||||
var changes = EquipChange.None;
|
if (_config.HideApplyCheckmarks)
|
||||||
|
cApply = null;
|
||||||
|
|
||||||
|
var changes = DataChange.None;
|
||||||
|
|
||||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
|
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
|
||||||
ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y });
|
ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y });
|
||||||
|
|
@ -159,11 +156,11 @@ public class EquipmentDrawer
|
||||||
rOffhand = cOffhand;
|
rOffhand = cOffhand;
|
||||||
if (DrawMainhand(cMainhand, cApply.HasValue, out rMainhand, out var mainhandLabel, locked))
|
if (DrawMainhand(cMainhand, cApply.HasValue, out rMainhand, out var mainhandLabel, locked))
|
||||||
{
|
{
|
||||||
changes |= EquipChange.Item;
|
changes |= DataChange.Item;
|
||||||
if (rMainhand.Type.ValidOffhand() != cMainhand.Type.ValidOffhand())
|
if (rMainhand.Type.ValidOffhand() != cMainhand.Type.ValidOffhand())
|
||||||
{
|
{
|
||||||
rOffhand = _items.GetDefaultOffhand(rMainhand);
|
rOffhand = _items.GetDefaultOffhand(rMainhand);
|
||||||
changes |= EquipChange.Item2;
|
changes |= DataChange.Item2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,7 +168,7 @@ public class EquipmentDrawer
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawApply(EquipSlot.MainHand, cApply.Value, out rApplyMainhand, locked))
|
if (DrawApply(EquipSlot.MainHand, cApply.Value, out rApplyMainhand, locked))
|
||||||
changes |= EquipChange.ApplyItem;
|
changes |= DataChange.ApplyItem;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -182,12 +179,12 @@ public class EquipmentDrawer
|
||||||
ImGui.TextUnformatted(mainhandLabel);
|
ImGui.TextUnformatted(mainhandLabel);
|
||||||
|
|
||||||
if (DrawStain(EquipSlot.MainHand, cMainhandStain, out rMainhandStain, locked))
|
if (DrawStain(EquipSlot.MainHand, cMainhandStain, out rMainhandStain, locked))
|
||||||
changes |= EquipChange.Stain;
|
changes |= DataChange.Stain;
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawApplyStain(EquipSlot.MainHand, cApply.Value, out rApplyMainhandStain, locked))
|
if (DrawApplyStain(EquipSlot.MainHand, cApply.Value, out rApplyMainhandStain, locked))
|
||||||
changes |= EquipChange.ApplyStain;
|
changes |= DataChange.ApplyStain;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -208,12 +205,12 @@ public class EquipmentDrawer
|
||||||
using (var group = ImRaii.Group())
|
using (var group = ImRaii.Group())
|
||||||
{
|
{
|
||||||
if (DrawOffhand(rMainhand, rOffhand, out rOffhand, out var offhandLabel, locked))
|
if (DrawOffhand(rMainhand, rOffhand, out rOffhand, out var offhandLabel, locked))
|
||||||
changes |= EquipChange.Item2;
|
changes |= DataChange.Item2;
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawApply(EquipSlot.OffHand, cApply.Value, out rApplyOffhand, locked))
|
if (DrawApply(EquipSlot.OffHand, cApply.Value, out rApplyOffhand, locked))
|
||||||
changes |= EquipChange.ApplyItem2;
|
changes |= DataChange.ApplyItem2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -224,12 +221,12 @@ public class EquipmentDrawer
|
||||||
ImGui.TextUnformatted(offhandLabel);
|
ImGui.TextUnformatted(offhandLabel);
|
||||||
|
|
||||||
if (DrawStain(EquipSlot.OffHand, cOffhandStain, out rOffhandStain, locked))
|
if (DrawStain(EquipSlot.OffHand, cOffhandStain, out rOffhandStain, locked))
|
||||||
changes |= EquipChange.Stain2;
|
changes |= DataChange.Stain2;
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawApplyStain(EquipSlot.OffHand, cApply.Value, out rApplyOffhandStain, locked))
|
if (DrawApplyStain(EquipSlot.OffHand, cApply.Value, out rApplyOffhandStain, locked))
|
||||||
changes |= EquipChange.ApplyStain2;
|
changes |= DataChange.ApplyStain2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -240,8 +237,29 @@ public class EquipmentDrawer
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool DrawHatState(bool currentValue, out bool newValue, bool locked)
|
||||||
|
=> UiHelpers.DrawCheckbox("Hat Visible", "Hide or show the characters head gear.", currentValue, out newValue, locked);
|
||||||
|
|
||||||
public bool DrawMainhand(EquipItem current, bool drawAll, out EquipItem weapon, out string label, bool locked)
|
public DataChange DrawHatState(bool currentValue, bool currentApply, out bool newValue, out bool newApply, bool locked)
|
||||||
|
=> UiHelpers.DrawMetaToggle("Hat Visible", "Change the visibility of the characters head gear: Hidden, Visible or Don't Apply.",
|
||||||
|
currentValue, currentApply, out newValue, out newApply, locked);
|
||||||
|
|
||||||
|
public bool DrawVisorState(bool currentValue, out bool newValue, bool locked)
|
||||||
|
=> UiHelpers.DrawCheckbox("Visor Toggled", "Toggle the visor state of the characters head gear.", currentValue, out newValue, locked);
|
||||||
|
|
||||||
|
public DataChange DrawVisorState(bool currentValue, bool currentApply, out bool newValue, out bool newApply, bool locked)
|
||||||
|
=> UiHelpers.DrawMetaToggle("Visor Toggled", "Change the toggled state of the characters head gear: Normal, Toggled or Don't Apply.",
|
||||||
|
currentValue, currentApply, out newValue, out newApply, locked);
|
||||||
|
|
||||||
|
public bool DrawWeaponState(bool currentValue, out bool newValue, bool locked)
|
||||||
|
=> UiHelpers.DrawCheckbox("Weapon Visible", "Hide or show the characters weapons when not drawn.", currentValue, out newValue, locked);
|
||||||
|
|
||||||
|
public DataChange DrawWeaponState(bool currentValue, bool currentApply, out bool newValue, out bool newApply, bool locked)
|
||||||
|
=> UiHelpers.DrawMetaToggle("Weapon Visible",
|
||||||
|
"Change the visibility of the characters weapons when not drawn: Hidden, Visible or Don't Apply.", currentValue, currentApply,
|
||||||
|
out newValue, out newApply, locked);
|
||||||
|
|
||||||
|
private bool DrawMainhand(EquipItem current, bool drawAll, out EquipItem weapon, out string label, bool locked)
|
||||||
{
|
{
|
||||||
weapon = current;
|
weapon = current;
|
||||||
if (!_weaponCombo.TryGetValue(drawAll ? FullEquipType.Unknown : current.Type, out var combo))
|
if (!_weaponCombo.TryGetValue(drawAll ? FullEquipType.Unknown : current.Type, out var combo))
|
||||||
|
|
@ -259,7 +277,7 @@ public class EquipmentDrawer
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DrawOffhand(EquipItem mainhand, EquipItem current, out EquipItem weapon, out string label, bool locked)
|
private bool DrawOffhand(EquipItem mainhand, EquipItem current, out EquipItem weapon, out string label, bool locked)
|
||||||
{
|
{
|
||||||
weapon = current;
|
weapon = current;
|
||||||
if (!_weaponCombo.TryGetValue(current.Type, out var combo))
|
if (!_weaponCombo.TryGetValue(current.Type, out var combo))
|
||||||
|
|
@ -291,11 +309,11 @@ public class EquipmentDrawer
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DrawApply(EquipSlot slot, EquipFlag flags, out bool enabled, bool locked)
|
private bool DrawApply(EquipSlot slot, EquipFlag flags, out bool enabled, bool locked)
|
||||||
=> UiHelpers.DrawCheckbox($"##apply{slot}", "Apply this item when applying the Design.", flags.HasFlag(slot.ToFlag()), out enabled,
|
=> UiHelpers.DrawCheckbox($"##apply{slot}", "Apply this item when applying the Design.", flags.HasFlag(slot.ToFlag()), out enabled,
|
||||||
locked);
|
locked);
|
||||||
|
|
||||||
public bool DrawApplyStain(EquipSlot slot, EquipFlag flags, out bool enabled, bool locked)
|
private bool DrawApplyStain(EquipSlot slot, EquipFlag flags, out bool enabled, bool locked)
|
||||||
=> UiHelpers.DrawCheckbox($"##applyStain{slot}", "Apply this dye when applying the Design.", flags.HasFlag(slot.ToStainFlag()),
|
=> UiHelpers.DrawCheckbox($"##applyStain{slot}", "Apply this dye when applying the Design.", flags.HasFlag(slot.ToStainFlag()),
|
||||||
out enabled, locked);
|
out enabled, locked);
|
||||||
|
|
||||||
|
|
@ -397,23 +415,23 @@ public class EquipmentDrawer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EquipChange DrawEquipArtisan(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain,
|
private DataChange DrawEquipArtisan(EquipSlot slot, EquipItem cArmor, out EquipItem rArmor, StainId cStain, out StainId rStain,
|
||||||
EquipFlag? cApply, out bool rApply, out bool rApplyStain)
|
EquipFlag? cApply, out bool rApply, out bool rApplyStain)
|
||||||
{
|
{
|
||||||
var changes = EquipChange.None;
|
var changes = DataChange.None;
|
||||||
if (DrawStainArtisan(slot, cStain, out rStain))
|
if (DrawStainArtisan(slot, cStain, out rStain))
|
||||||
changes |= EquipChange.Stain;
|
changes |= DataChange.Stain;
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawArmorArtisan(slot, cArmor, out rArmor))
|
if (DrawArmorArtisan(slot, cArmor, out rArmor))
|
||||||
changes |= EquipChange.Item;
|
changes |= DataChange.Item;
|
||||||
if (cApply.HasValue)
|
if (cApply.HasValue)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawApply(slot, cApply.Value, out rApply, false))
|
if (DrawApply(slot, cApply.Value, out rApply, false))
|
||||||
changes |= EquipChange.ApplyItem;
|
changes |= DataChange.ApplyItem;
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (DrawApplyStain(slot, cApply.Value, out rApplyStain, false))
|
if (DrawApplyStain(slot, cApply.Value, out rApplyStain, false))
|
||||||
changes |= EquipChange.ApplyStain;
|
changes |= DataChange.ApplyStain;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -107,46 +107,77 @@ public class ActorPanel
|
||||||
|
|
||||||
private void DrawHumanPanel()
|
private void DrawHumanPanel()
|
||||||
{
|
{
|
||||||
if (_customizationDrawer.Draw(_state!.ModelData.Customize, false))
|
DrawCustomizationsHeader();
|
||||||
|
DrawEquipmentHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawCustomizationsHeader()
|
||||||
|
{
|
||||||
|
if (!ImGui.CollapsingHeader("Customizations"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_customizationDrawer.Draw(_state!.ModelData.Customize, _state.IsLocked))
|
||||||
_stateManager.ChangeCustomize(_state, _customizationDrawer.Customize, _customizationDrawer.Changed, StateChanged.Source.Manual);
|
_stateManager.ChangeCustomize(_state, _customizationDrawer.Customize, _customizationDrawer.Changed, StateChanged.Source.Manual);
|
||||||
|
|
||||||
|
if (_customizationDrawer.DrawWetnessState(_state!.ModelData.IsWet(), out var newWetness, _state.IsLocked))
|
||||||
|
_stateManager.ChangeWetness(_state, newWetness, StateChanged.Source.Manual);
|
||||||
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawEquipmentHeader()
|
||||||
|
{
|
||||||
|
if (!ImGui.CollapsingHeader("Equipment"))
|
||||||
|
return;
|
||||||
|
|
||||||
_equipmentDrawer.Prepare();
|
_equipmentDrawer.Prepare();
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
{
|
{
|
||||||
var changes = _equipmentDrawer.DrawEquip(slot, _state.ModelData, out var newArmor, out var newStain, null, out _, out _,
|
var changes = _equipmentDrawer.DrawEquip(slot, _state!.ModelData, out var newArmor, out var newStain, null, out _, out _,
|
||||||
_state.IsLocked);
|
_state.IsLocked);
|
||||||
switch (changes)
|
switch (changes)
|
||||||
{
|
{
|
||||||
case EquipmentDrawer.EquipChange.Item:
|
case DataChange.Item:
|
||||||
_stateManager.ChangeItem(_state, slot, newArmor, StateChanged.Source.Manual);
|
_stateManager.ChangeItem(_state, slot, newArmor, StateChanged.Source.Manual);
|
||||||
break;
|
break;
|
||||||
case EquipmentDrawer.EquipChange.Stain:
|
case DataChange.Stain:
|
||||||
_stateManager.ChangeStain(_state, slot, newStain, StateChanged.Source.Manual);
|
_stateManager.ChangeStain(_state, slot, newStain, StateChanged.Source.Manual);
|
||||||
break;
|
break;
|
||||||
case EquipmentDrawer.EquipChange.Item | EquipmentDrawer.EquipChange.Stain:
|
case DataChange.Item | DataChange.Stain:
|
||||||
_stateManager.ChangeEquip(_state, slot, newArmor, newStain, StateChanged.Source.Manual);
|
_stateManager.ChangeEquip(_state, slot, newArmor, newStain, StateChanged.Source.Manual);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var weaponChanges = _equipmentDrawer.DrawWeapons(_state.ModelData, out var newMainhand, out var newOffhand, out var newMainhandStain,
|
var weaponChanges = _equipmentDrawer.DrawWeapons(_state!.ModelData, out var newMainhand, out var newOffhand,
|
||||||
|
out var newMainhandStain,
|
||||||
out var newOffhandStain, null, out _, out _, out _, out _, _state.IsLocked);
|
out var newOffhandStain, null, out _, out _, out _, out _, _state.IsLocked);
|
||||||
|
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Item))
|
if (weaponChanges.HasFlag(DataChange.Item))
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Stain))
|
if (weaponChanges.HasFlag(DataChange.Stain))
|
||||||
_stateManager.ChangeEquip(_state, EquipSlot.MainHand, newMainhand, newMainhandStain, StateChanged.Source.Manual);
|
_stateManager.ChangeEquip(_state, EquipSlot.MainHand, newMainhand, newMainhandStain, StateChanged.Source.Manual);
|
||||||
else
|
else
|
||||||
_stateManager.ChangeItem(_state, EquipSlot.MainHand, newMainhand, StateChanged.Source.Manual);
|
_stateManager.ChangeItem(_state, EquipSlot.MainHand, newMainhand, StateChanged.Source.Manual);
|
||||||
else if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Stain))
|
else if (weaponChanges.HasFlag(DataChange.Stain))
|
||||||
_stateManager.ChangeStain(_state, EquipSlot.MainHand, newMainhandStain, StateChanged.Source.Manual);
|
_stateManager.ChangeStain(_state, EquipSlot.MainHand, newMainhandStain, StateChanged.Source.Manual);
|
||||||
|
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Item2))
|
if (weaponChanges.HasFlag(DataChange.Item2))
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Stain2))
|
if (weaponChanges.HasFlag(DataChange.Stain2))
|
||||||
_stateManager.ChangeEquip(_state, EquipSlot.OffHand, newOffhand, newOffhandStain, StateChanged.Source.Manual);
|
_stateManager.ChangeEquip(_state, EquipSlot.OffHand, newOffhand, newOffhandStain, StateChanged.Source.Manual);
|
||||||
else
|
else
|
||||||
_stateManager.ChangeItem(_state, EquipSlot.OffHand, newOffhand, StateChanged.Source.Manual);
|
_stateManager.ChangeItem(_state, EquipSlot.OffHand, newOffhand, StateChanged.Source.Manual);
|
||||||
else if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Stain2))
|
else if (weaponChanges.HasFlag(DataChange.Stain2))
|
||||||
_stateManager.ChangeStain(_state, EquipSlot.OffHand, newOffhandStain, StateChanged.Source.Manual);
|
_stateManager.ChangeStain(_state, EquipSlot.OffHand, newOffhandStain, StateChanged.Source.Manual);
|
||||||
|
|
||||||
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
|
if (_equipmentDrawer.DrawHatState(_state!.ModelData.IsHatVisible(), out var newHatState, _state!.IsLocked))
|
||||||
|
_stateManager.ChangeHatState(_state, newHatState, StateChanged.Source.Manual);
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (_equipmentDrawer.DrawVisorState(_state!.ModelData.IsVisorToggled(), out var newVisorState, _state!.IsLocked))
|
||||||
|
_stateManager.ChangeVisorState(_state, newVisorState, StateChanged.Source.Manual);
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (_equipmentDrawer.DrawWeaponState(_state!.ModelData.IsWeaponVisible(), out var newWeaponState, _state!.IsLocked))
|
||||||
|
_stateManager.ChangeWeaponState(_state, newWeaponState, StateChanged.Source.Manual);
|
||||||
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawMonsterPanel()
|
private void DrawMonsterPanel()
|
||||||
|
|
@ -217,6 +248,7 @@ public class ActorPanel
|
||||||
Icon = FontAwesomeIcon.Clipboard,
|
Icon = FontAwesomeIcon.Clipboard,
|
||||||
OnClick = SetFromClipboard,
|
OnClick = SetFromClipboard,
|
||||||
Visible = _state != null,
|
Visible = _state != null,
|
||||||
|
Disabled = _state?.IsLocked ?? true,
|
||||||
};
|
};
|
||||||
|
|
||||||
private HeaderDrawer.Button ExportToClipboardButton()
|
private HeaderDrawer.Button ExportToClipboardButton()
|
||||||
|
|
@ -301,15 +333,19 @@ public class ActorPanel
|
||||||
|
|
||||||
private void RevertButtons()
|
private void RevertButtons()
|
||||||
{
|
{
|
||||||
if (ImGui.Button("Revert to Game"))
|
if (ImGuiUtil.DrawDisabledButton("Revert to Game", Vector2.Zero, "Revert the character to its actual state in the game.",
|
||||||
|
_state!.IsLocked))
|
||||||
_stateManager.ResetState(_state!);
|
_stateManager.ResetState(_state!);
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGui.Button("Reapply State"))
|
if (ImGuiUtil.DrawDisabledButton("Reapply State", Vector2.Zero, "Try to reapply the configured state if something went wrong.",
|
||||||
|
_state!.IsLocked))
|
||||||
_stateManager.ReapplyState(_actor);
|
_stateManager.ReapplyState(_actor);
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiUtil.DrawDisabledButton("Reapply Automation", Vector2.Zero, string.Empty, !_config.EnableAutoDesigns))
|
if (ImGuiUtil.DrawDisabledButton("Reapply Automation", Vector2.Zero,
|
||||||
|
"Try to revert the character to the state it would have using automated designs.",
|
||||||
|
!_config.EnableAutoDesigns || _state!.IsLocked))
|
||||||
{
|
{
|
||||||
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!);
|
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!);
|
||||||
_stateManager.ReapplyState(_actor);
|
_stateManager.ReapplyState(_actor);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
using Dalamud.Game.ClientState.Keys;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Internal.Notifications;
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using Glamourer.Customization;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
|
using OtterGui.Classes;
|
||||||
using OtterGui.Filesystem;
|
using OtterGui.Filesystem;
|
||||||
using OtterGui.FileSystem.Selector;
|
using OtterGui.FileSystem.Selector;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
|
|
@ -39,7 +41,9 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
|
||||||
=> base.SelectedLeaf;
|
=> base.SelectedLeaf;
|
||||||
|
|
||||||
public struct DesignState
|
public struct DesignState
|
||||||
{ }
|
{
|
||||||
|
public ColorId Color;
|
||||||
|
}
|
||||||
|
|
||||||
public DesignFileSystemSelector(DesignManager designManager, DesignFileSystem fileSystem, KeyState keyState, DesignChanged @event,
|
public DesignFileSystemSelector(DesignManager designManager, DesignFileSystem fileSystem, KeyState keyState, DesignChanged @event,
|
||||||
Configuration config, DesignConverter converter)
|
Configuration config, DesignConverter converter)
|
||||||
|
|
@ -55,6 +59,7 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
|
||||||
AddButton(ImportDesignButton, 10);
|
AddButton(ImportDesignButton, 10);
|
||||||
AddButton(CloneDesignButton, 20);
|
AddButton(CloneDesignButton, 20);
|
||||||
AddButton(DeleteButton, 1000);
|
AddButton(DeleteButton, 1000);
|
||||||
|
SetFilterTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void DrawPopups()
|
protected override void DrawPopups()
|
||||||
|
|
@ -66,6 +71,7 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
|
||||||
{
|
{
|
||||||
var flag = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags;
|
var flag = selected ? ImGuiTreeNodeFlags.Selected | LeafFlags : LeafFlags;
|
||||||
var name = IncognitoMode ? leaf.Value.Incognito : leaf.Value.Name.Text;
|
var name = IncognitoMode ? leaf.Value.Incognito : leaf.Value.Name.Text;
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Text, state.Color.Value());
|
||||||
using var _ = ImRaii.TreeNode(name, flag);
|
using var _ = ImRaii.TreeNode(name, flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,6 +109,8 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
|
||||||
case DesignChanged.Type.RemovedMod:
|
case DesignChanged.Type.RemovedMod:
|
||||||
case DesignChanged.Type.Created:
|
case DesignChanged.Type.Created:
|
||||||
case DesignChanged.Type.Deleted:
|
case DesignChanged.Type.Deleted:
|
||||||
|
case DesignChanged.Type.ApplyCustomize:
|
||||||
|
case DesignChanged.Type.ApplyEquip:
|
||||||
SetFilterDirty();
|
SetFilterDirty();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -188,4 +196,91 @@ public sealed class DesignFileSystemSelector : FileSystemSelector<Design, Design
|
||||||
|
|
||||||
_newName = string.Empty;
|
_newName = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Filters
|
||||||
|
|
||||||
|
private const StringComparison IgnoreCase = StringComparison.OrdinalIgnoreCase;
|
||||||
|
private LowerString _designFilter = LowerString.Empty;
|
||||||
|
private int _filterType = -1;
|
||||||
|
|
||||||
|
private void SetFilterTooltip()
|
||||||
|
{
|
||||||
|
FilterTooltip = "Filter designs for those where their full paths or names contain the given substring.\n"
|
||||||
|
+ "Enter m:[string] to filter for designs with with a mod association containing the string.\n"
|
||||||
|
+ "Enter t:[string] to filter for designs set to specific tags.\n"
|
||||||
|
+ "Enter n:[string] to filter only for design names and no paths.";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Appropriately identify and set the string filter and its type. </summary>
|
||||||
|
protected override bool ChangeFilter(string filterValue)
|
||||||
|
{
|
||||||
|
(_designFilter, _filterType) = filterValue.Length switch
|
||||||
|
{
|
||||||
|
0 => (LowerString.Empty, -1),
|
||||||
|
> 1 when filterValue[1] == ':' =>
|
||||||
|
filterValue[0] switch
|
||||||
|
{
|
||||||
|
'n' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 1),
|
||||||
|
'N' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 1),
|
||||||
|
'm' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 2),
|
||||||
|
'M' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 2),
|
||||||
|
't' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 3),
|
||||||
|
'T' => filterValue.Length == 2 ? (LowerString.Empty, -1) : (new LowerString(filterValue[2..]), 3),
|
||||||
|
_ => (new LowerString(filterValue), 0),
|
||||||
|
},
|
||||||
|
_ => (new LowerString(filterValue), 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The overwritten filter method also computes the state.
|
||||||
|
/// Folders have default state and are filtered out on the direct string instead of the other options.
|
||||||
|
/// If any filter is set, they should be hidden by default unless their children are visible,
|
||||||
|
/// or they contain the path search string.
|
||||||
|
/// </summary>
|
||||||
|
protected override bool ApplyFiltersAndState(FileSystem<Design>.IPath path, out DesignState state)
|
||||||
|
{
|
||||||
|
if (path is DesignFileSystem.Folder f)
|
||||||
|
{
|
||||||
|
state = default;
|
||||||
|
return FilterValue.Length > 0 && !f.FullName().Contains(FilterValue, IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApplyFiltersAndState((DesignFileSystem.Leaf)path, out state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Apply the string filters. </summary>
|
||||||
|
private bool ApplyStringFilters(DesignFileSystem.Leaf leaf, Design design)
|
||||||
|
{
|
||||||
|
return _filterType switch
|
||||||
|
{
|
||||||
|
-1 => false,
|
||||||
|
0 => !(_designFilter.IsContained(leaf.FullName()) || design.Name.Contains(_designFilter)),
|
||||||
|
1 => !design.Name.Contains(_designFilter),
|
||||||
|
2 => !design.AssociatedMods.Any(kvp => _designFilter.IsContained(kvp.Key.Name)),
|
||||||
|
3 => !design.Tags.Any(_designFilter.IsContained),
|
||||||
|
_ => false, // Should never happen
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Combined wrapper for handling all filters and setting state. </summary>
|
||||||
|
private bool ApplyFiltersAndState(DesignFileSystem.Leaf leaf, out DesignState state)
|
||||||
|
{
|
||||||
|
var applyEquip = leaf.Value.ApplyEquip != 0;
|
||||||
|
var applyCustomize = (leaf.Value.ApplyCustomize & ~(CustomizeFlag.BodyType | CustomizeFlag.Race)) != 0;
|
||||||
|
|
||||||
|
state.Color = (applyEquip, applyCustomize) switch
|
||||||
|
{
|
||||||
|
(false, false) => ColorId.StateDesign,
|
||||||
|
(false, true) => ColorId.CustomizationDesign,
|
||||||
|
(true, false) => ColorId.EquipmentDesign,
|
||||||
|
(true, true) => ColorId.NormalDesign,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ApplyStringFilters(leaf, leaf.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,36 +95,6 @@ public class DesignPanel
|
||||||
private string SelectionName
|
private string SelectionName
|
||||||
=> _selector.Selected == null ? "No Selection" : _selector.IncognitoMode ? _selector.Selected.Incognito : _selector.Selected.Name.Text;
|
=> _selector.Selected == null ? "No Selection" : _selector.IncognitoMode ? _selector.Selected.Incognito : _selector.Selected.Name.Text;
|
||||||
|
|
||||||
private void DrawMetaData()
|
|
||||||
{
|
|
||||||
if (!ImGui.CollapsingHeader("MetaData"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (var group1 = ImRaii.Group())
|
|
||||||
{
|
|
||||||
var apply = _selector.Selected!.DesignData.IsHatVisible();
|
|
||||||
if (ImGui.Checkbox("Hat Visible", ref apply))
|
|
||||||
_manager.ChangeMeta(_selector.Selected, ActorState.MetaIndex.HatState, apply);
|
|
||||||
|
|
||||||
apply = _selector.Selected.DesignData.IsWeaponVisible();
|
|
||||||
if (ImGui.Checkbox("Weapon Visible", ref apply))
|
|
||||||
_manager.ChangeMeta(_selector.Selected, ActorState.MetaIndex.WeaponState, apply);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine(ImGui.GetContentRegionAvail().X / 2);
|
|
||||||
|
|
||||||
using (var group2 = ImRaii.Group())
|
|
||||||
{
|
|
||||||
var apply = _selector.Selected.DesignData.IsVisorToggled();
|
|
||||||
if (ImGui.Checkbox("Visor Toggled", ref apply))
|
|
||||||
_manager.ChangeMeta(_selector.Selected, ActorState.MetaIndex.VisorState, apply);
|
|
||||||
|
|
||||||
apply = _selector.Selected.DesignData.IsWet();
|
|
||||||
if (ImGui.Checkbox("Force Wetness", ref apply))
|
|
||||||
_manager.ChangeMeta(_selector.Selected, ActorState.MetaIndex.Wetness, apply);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawEquipment()
|
private void DrawEquipment()
|
||||||
{
|
{
|
||||||
if (!ImGui.CollapsingHeader("Equipment"))
|
if (!ImGui.CollapsingHeader("Equipment"))
|
||||||
|
|
@ -135,41 +105,82 @@ public class DesignPanel
|
||||||
{
|
{
|
||||||
var changes = _equipmentDrawer.DrawEquip(slot, _selector.Selected!.DesignData, out var newArmor, out var newStain,
|
var changes = _equipmentDrawer.DrawEquip(slot, _selector.Selected!.DesignData, out var newArmor, out var newStain,
|
||||||
_selector.Selected.ApplyEquip, out var newApply, out var newApplyStain, _selector.Selected!.WriteProtected());
|
_selector.Selected.ApplyEquip, out var newApply, out var newApplyStain, _selector.Selected!.WriteProtected());
|
||||||
if (changes.HasFlag(EquipmentDrawer.EquipChange.Item))
|
if (changes.HasFlag(DataChange.Item))
|
||||||
_manager.ChangeEquip(_selector.Selected, slot, newArmor);
|
_manager.ChangeEquip(_selector.Selected, slot, newArmor);
|
||||||
if (changes.HasFlag(EquipmentDrawer.EquipChange.Stain))
|
if (changes.HasFlag(DataChange.Stain))
|
||||||
_manager.ChangeStain(_selector.Selected, slot, newStain);
|
_manager.ChangeStain(_selector.Selected, slot, newStain);
|
||||||
if (changes.HasFlag(EquipmentDrawer.EquipChange.ApplyItem))
|
if (changes.HasFlag(DataChange.ApplyItem))
|
||||||
_manager.ChangeApplyEquip(_selector.Selected, slot, newApply);
|
_manager.ChangeApplyEquip(_selector.Selected, slot, newApply);
|
||||||
if (changes.HasFlag(EquipmentDrawer.EquipChange.ApplyStain))
|
if (changes.HasFlag(DataChange.ApplyStain))
|
||||||
_manager.ChangeApplyStain(_selector.Selected, slot, newApplyStain);
|
_manager.ChangeApplyStain(_selector.Selected, slot, newApplyStain);
|
||||||
}
|
}
|
||||||
|
|
||||||
var weaponChanges = _equipmentDrawer.DrawWeapons(_selector.Selected!.DesignData, out var newMainhand, out var newOffhand,
|
var weaponChanges = _equipmentDrawer.DrawWeapons(_selector.Selected!.DesignData, out var newMainhand, out var newOffhand,
|
||||||
out var newMainhandStain, out var newOffhandStain, _selector.Selected.ApplyEquip, out var applyMain, out var applyMainStain,
|
out var newMainhandStain, out var newOffhandStain, _selector.Selected.ApplyEquip, out var applyMain, out var applyMainStain,
|
||||||
out var applyOff, out var applyOffStain, _selector.Selected!.WriteProtected());
|
out var applyOff, out var applyOffStain, _selector.Selected!.WriteProtected());
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Item))
|
|
||||||
|
if (weaponChanges.HasFlag(DataChange.Item))
|
||||||
_manager.ChangeWeapon(_selector.Selected, EquipSlot.MainHand, newMainhand);
|
_manager.ChangeWeapon(_selector.Selected, EquipSlot.MainHand, newMainhand);
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Stain))
|
if (weaponChanges.HasFlag(DataChange.Stain))
|
||||||
_manager.ChangeStain(_selector.Selected, EquipSlot.MainHand, newMainhandStain);
|
_manager.ChangeStain(_selector.Selected, EquipSlot.MainHand, newMainhandStain);
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.ApplyItem))
|
if (weaponChanges.HasFlag(DataChange.ApplyItem))
|
||||||
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.MainHand, applyMain);
|
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.MainHand, applyMain);
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.ApplyStain))
|
if (weaponChanges.HasFlag(DataChange.ApplyStain))
|
||||||
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.MainHand, applyMainStain);
|
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.MainHand, applyMainStain);
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Item2))
|
if (weaponChanges.HasFlag(DataChange.Item2))
|
||||||
_manager.ChangeWeapon(_selector.Selected, EquipSlot.OffHand, newOffhand);
|
_manager.ChangeWeapon(_selector.Selected, EquipSlot.OffHand, newOffhand);
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.Stain2))
|
if (weaponChanges.HasFlag(DataChange.Stain2))
|
||||||
_manager.ChangeStain(_selector.Selected, EquipSlot.OffHand, newOffhandStain);
|
_manager.ChangeStain(_selector.Selected, EquipSlot.OffHand, newOffhandStain);
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.ApplyItem2))
|
if (weaponChanges.HasFlag(DataChange.ApplyItem2))
|
||||||
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.OffHand, applyOff);
|
_manager.ChangeApplyEquip(_selector.Selected, EquipSlot.OffHand, applyOff);
|
||||||
if (weaponChanges.HasFlag(EquipmentDrawer.EquipChange.ApplyStain2))
|
if (weaponChanges.HasFlag(DataChange.ApplyStain2))
|
||||||
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.OffHand, applyOffStain);
|
_manager.ChangeApplyStain(_selector.Selected, EquipSlot.OffHand, applyOffStain);
|
||||||
|
|
||||||
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
|
DrawEquipmentMetaToggles();
|
||||||
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawEquipmentMetaToggles()
|
||||||
|
{
|
||||||
|
var hatChanges = _equipmentDrawer.DrawHatState(_selector.Selected!.DesignData.IsHatVisible(),
|
||||||
|
_selector.Selected.DoApplyHatVisible(),
|
||||||
|
out var newHatState, out var newHatApply, _selector.Selected.WriteProtected());
|
||||||
|
ApplyChanges(ActorState.MetaIndex.HatState, hatChanges, newHatState, newHatApply);
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
var visorChanges = _equipmentDrawer.DrawVisorState(_selector.Selected!.DesignData.IsVisorToggled(),
|
||||||
|
_selector.Selected.DoApplyVisorToggle(),
|
||||||
|
out var newVisorState, out var newVisorApply, _selector.Selected.WriteProtected());
|
||||||
|
ApplyChanges(ActorState.MetaIndex.VisorState, visorChanges, newVisorState, newVisorApply);
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
var weaponChanges = _equipmentDrawer.DrawWeaponState(_selector.Selected!.DesignData.IsWeaponVisible(),
|
||||||
|
_selector.Selected.DoApplyWeaponVisible(),
|
||||||
|
out var newWeaponState, out var newWeaponApply, _selector.Selected.WriteProtected());
|
||||||
|
ApplyChanges(ActorState.MetaIndex.WeaponState, weaponChanges, newWeaponState, newWeaponApply);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCustomize()
|
private void DrawCustomize()
|
||||||
{
|
{
|
||||||
if (ImGui.CollapsingHeader("Customization"))
|
if (!ImGui.CollapsingHeader("Customization"))
|
||||||
_customizationDrawer.Draw(_selector.Selected!.DesignData.Customize, _selector.Selected!.WriteProtected());
|
return;
|
||||||
|
|
||||||
|
_customizationDrawer.Draw(_selector.Selected!.DesignData.Customize, _selector.Selected.ApplyCustomize,
|
||||||
|
_selector.Selected!.WriteProtected());
|
||||||
|
|
||||||
|
if (_customizationDrawer.ChangeApply != _selector.Selected.ApplyCustomize)
|
||||||
|
foreach (var idx in Enum.GetValues<CustomizeIndex>())
|
||||||
|
{
|
||||||
|
var flag = idx.ToFlag();
|
||||||
|
var newValue = _customizationDrawer.ChangeApply.HasFlag(flag);
|
||||||
|
_manager.ChangeApplyCustomize(_selector.Selected, idx, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
var wetnessChanges = _customizationDrawer.DrawWetnessState(_selector.Selected!.DesignData.IsWet(),
|
||||||
|
_selector.Selected!.DoApplyWetness(), out var newWetnessState, out var newWetnessApply, _selector.Selected!.WriteProtected());
|
||||||
|
ApplyChanges(ActorState.MetaIndex.Wetness, wetnessChanges, newWetnessState, newWetnessApply);
|
||||||
|
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawApplicationRules()
|
private void DrawApplicationRules()
|
||||||
|
|
@ -285,7 +296,6 @@ public class DesignPanel
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DrawButtonRow();
|
DrawButtonRow();
|
||||||
DrawMetaData();
|
|
||||||
DrawCustomize();
|
DrawCustomize();
|
||||||
DrawEquipment();
|
DrawEquipment();
|
||||||
_designDetails.Draw();
|
_designDetails.Draw();
|
||||||
|
|
@ -354,4 +364,21 @@ public class DesignPanel
|
||||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||||
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual);
|
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ApplyChanges(ActorState.MetaIndex index, DataChange change, bool value, bool apply)
|
||||||
|
{
|
||||||
|
switch (change)
|
||||||
|
{
|
||||||
|
case DataChange.Item:
|
||||||
|
_manager.ChangeMeta(_selector.Selected!, index, value);
|
||||||
|
break;
|
||||||
|
case DataChange.ApplyItem:
|
||||||
|
_manager.ChangeApplyMeta(_selector.Selected!, index, apply);
|
||||||
|
break;
|
||||||
|
case DataChange.Item | DataChange.ApplyItem:
|
||||||
|
_manager.ChangeApplyMeta(_selector.Selected!, index, apply);
|
||||||
|
_manager.ChangeMeta(_selector.Selected!, index, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,10 @@ public class SettingsTab : ITab
|
||||||
Checkbox("Auto-Reload Gear",
|
Checkbox("Auto-Reload Gear",
|
||||||
"Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection.",
|
"Automatically reload equipment pieces on your own character when changing any mod options in Penumbra in their associated collection.",
|
||||||
_config.AutoRedrawEquipOnChanges, _autoRedraw.SetState);
|
_config.AutoRedrawEquipOnChanges, _autoRedraw.SetState);
|
||||||
|
|
||||||
|
Checkbox("Hide Application Checkboxes",
|
||||||
|
"Hide the application checkboxes in the Customization and Equipment panels of the design tab, and only show them under Application Rules.",
|
||||||
|
_config.HideApplyCheckmarks, v => _config.HideApplyCheckmarks = v);
|
||||||
if (Widget.DoubleModifierSelector("Design Deletion Modifier",
|
if (Widget.DoubleModifierSelector("Design Deletion Modifier",
|
||||||
"A modifier you need to hold while clicking the Delete Design button for it to take effect.", 100 * ImGuiHelpers.GlobalScale,
|
"A modifier you need to hold while clicking the Delete Design button for it to take effect.", 100 * ImGuiHelpers.GlobalScale,
|
||||||
_config.DeleteDesignModifier, v => _config.DeleteDesignModifier = v))
|
_config.DeleteDesignModifier, v => _config.DeleteDesignModifier = v))
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
|
|
@ -8,6 +9,20 @@ using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer.Gui;
|
namespace Glamourer.Gui;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum DataChange : byte
|
||||||
|
{
|
||||||
|
None = 0x00,
|
||||||
|
Item = 0x01,
|
||||||
|
Stain = 0x02,
|
||||||
|
ApplyItem = 0x04,
|
||||||
|
ApplyStain = 0x08,
|
||||||
|
Item2 = 0x10,
|
||||||
|
Stain2 = 0x20,
|
||||||
|
ApplyItem2 = 0x40,
|
||||||
|
ApplyStain2 = 0x80,
|
||||||
|
}
|
||||||
|
|
||||||
public static class UiHelpers
|
public static class UiHelpers
|
||||||
{
|
{
|
||||||
public static void DrawIcon(this EquipItem item, TextureService textures, Vector2 size)
|
public static void DrawIcon(this EquipItem item, TextureService textures, Vector2 size)
|
||||||
|
|
@ -40,15 +55,32 @@ public static class UiHelpers
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool DrawVisor(bool current, out bool on, bool locked)
|
public static DataChange DrawMetaToggle(string label, string tooltip, bool currentValue, bool currentApply, out bool newValue, out bool newApply,
|
||||||
=> DrawCheckbox("##visorToggled", string.Empty, current, out on, locked);
|
bool locked)
|
||||||
|
{
|
||||||
public static bool DrawHat(bool current, out bool on, bool locked)
|
var flags = currentApply ? currentValue ? 3 : 0 : 2;
|
||||||
=> DrawCheckbox("##hatVisible", string.Empty, current, out on, locked);
|
bool ret;
|
||||||
|
using (var disabled = ImRaii.Disabled(locked))
|
||||||
public static bool DrawWeapon(bool current, out bool on, bool locked)
|
{
|
||||||
=> DrawCheckbox("##weaponVisible", string.Empty, current, out on, locked);
|
ret = ImGui.CheckboxFlags(label, ref flags, 3);
|
||||||
|
}
|
||||||
public static bool DrawWetness(bool current, out bool on, bool locked)
|
|
||||||
=> DrawCheckbox("##wetness", string.Empty, current, out on, locked);
|
ImGuiUtil.HoverTooltip(tooltip);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
(newValue, newApply, var change) = (currentValue, currentApply) switch
|
||||||
|
{
|
||||||
|
(false, false) => (false, true, DataChange.ApplyItem),
|
||||||
|
(false, true) => (true, true, DataChange.Item),
|
||||||
|
(true, false) => (false, false, DataChange.Item), // Should not happen
|
||||||
|
(true, true) => (false, false, DataChange.Item | DataChange.ApplyItem),
|
||||||
|
};
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
newValue = currentValue;
|
||||||
|
newApply = currentApply;
|
||||||
|
return DataChange.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ public unsafe class WeaponService : IDisposable
|
||||||
_loadWeaponHook.Original(drawData, slot, weapon.Value, redrawOnEquality, unk2, skipGameObject, unk4);
|
_loadWeaponHook.Original(drawData, slot, weapon.Value, redrawOnEquality, unk2, skipGameObject, unk4);
|
||||||
if (tmpWeapon.Value != weapon.Value)
|
if (tmpWeapon.Value != weapon.Value)
|
||||||
_loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4);
|
_loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4);
|
||||||
Glamourer.Log.Information(
|
Glamourer.Log.Excessive(
|
||||||
$"Weapon reloaded for 0x{actor.Address:X} ({actor.Utf8Name}) with attributes {slot} {weapon.Value:X14}, {redrawOnEquality}, {unk2}, {skipGameObject}, {unk4}");
|
$"Weapon reloaded for 0x{actor.Address:X} ({actor.Utf8Name}) with attributes {slot} {weapon.Value:X14}, {redrawOnEquality}, {unk2}, {skipGameObject}, {unk4}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -165,10 +165,10 @@ public class StateEditor
|
||||||
{
|
{
|
||||||
(var setter, oldValue) = index switch
|
(var setter, oldValue) = index switch
|
||||||
{
|
{
|
||||||
ActorState.MetaIndex.Wetness => ((Func<bool, bool>)state.ModelData.SetIsWet, state.ModelData.IsWet()),
|
ActorState.MetaIndex.Wetness => ((Func<bool, bool>) (v => state.ModelData.SetIsWet(v)), state.ModelData.IsWet()),
|
||||||
ActorState.MetaIndex.HatState => ((Func<bool, bool>)state.ModelData.SetHatVisible, state.ModelData.IsHatVisible()),
|
ActorState.MetaIndex.HatState => ((Func<bool, bool>) (v => state.ModelData.SetHatVisible(v)), state.ModelData.IsHatVisible()),
|
||||||
ActorState.MetaIndex.VisorState => ((Func<bool, bool>)state.ModelData.SetVisor, state.ModelData.IsVisorToggled()),
|
ActorState.MetaIndex.VisorState => ((Func<bool, bool>) (v => state.ModelData.SetVisor(v)), state.ModelData.IsVisorToggled()),
|
||||||
ActorState.MetaIndex.WeaponState => ((Func<bool, bool>)state.ModelData.SetWeaponVisible, state.ModelData.IsWeaponVisible()),
|
ActorState.MetaIndex.WeaponState => ((Func<bool, bool>) (v => state.ModelData.SetWeaponVisible(v)), state.ModelData.IsWeaponVisible()),
|
||||||
_ => throw new Exception("Invalid MetaIndex."),
|
_ => throw new Exception("Invalid MetaIndex."),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.Wetness, value, source, out var old, key))
|
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.Wetness, value, source, out var old, key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var actors = _applier.ChangeVisor(state, true);
|
var actors = _applier.ChangeWetness(state, true);
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set Wetness in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
$"Set Wetness in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Other, state[ActorState.MetaIndex.Wetness], state, actors, (old, value, ActorState.MetaIndex.Wetness));
|
_event.Invoke(StateChanged.Type.Other, state[ActorState.MetaIndex.Wetness], state, actors, (old, value, ActorState.MetaIndex.Wetness));
|
||||||
|
|
@ -382,21 +382,29 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
||||||
_applier.ChangeHatState(actors, state.ModelData.IsHatVisible());
|
|
||||||
_applier.ChangeWeaponState(actors, state.ModelData.IsWeaponVisible());
|
|
||||||
_applier.ChangeVisor(actors, state.ModelData.IsVisorToggled());
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.ModelData.IsHatVisible());
|
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.ModelData.IsHatVisible());
|
||||||
foreach (var slot in EquipSlotExtensions.WeaponSlots)
|
foreach (var slot in EquipSlotExtensions.WeaponSlots)
|
||||||
_applier.ChangeWeapon(actors, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
_applier.ChangeWeapon(actors, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.ModelData.IsHuman)
|
||||||
|
{
|
||||||
|
_applier.ChangeHatState(actors, state.ModelData.IsHatVisible());
|
||||||
|
_applier.ChangeWeaponState(actors, state.ModelData.IsWeaponVisible());
|
||||||
|
_applier.ChangeVisor(actors, state.ModelData.IsVisorToggled());
|
||||||
|
}
|
||||||
|
|
||||||
return actors;
|
return actors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetState(ActorState state)
|
public void ResetState(ActorState state, uint key = 0)
|
||||||
{
|
{
|
||||||
var redraw = state.ModelData.ModelId != state.BaseData.ModelId || !state.ModelData.IsHuman
|
if (!state.Unlock(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var redraw = state.ModelData.ModelId != state.BaseData.ModelId
|
||||||
|
|| !state.ModelData.IsHuman
|
||||||
|| Customize.Compare(state.ModelData.Customize, state.BaseData.Customize).RequiresRedraw();
|
|| Customize.Compare(state.ModelData.Customize, state.BaseData.Customize).RequiresRedraw();
|
||||||
state.ModelData = state.BaseData;
|
state.ModelData = state.BaseData;
|
||||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue