diff --git a/Glamourer/Configuration.cs b/Glamourer/Configuration.cs index ce2ed08..dbd245f 100644 --- a/Glamourer/Configuration.cs +++ b/Glamourer/Configuration.cs @@ -14,43 +14,54 @@ using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; namespace Glamourer; +public enum HeightDisplayType +{ + None, + Centimetre, + Metre, + Wrong, + WrongFoot, +} + public class Configuration : IPluginConfiguration, ISavable { [JsonIgnore] public readonly EphemeralConfig Ephemeral; - public bool UseRestrictedGearProtection { get; set; } = false; - public bool OpenFoldersByDefault { get; set; } = false; - public bool AutoRedrawEquipOnChanges { get; set; } = false; - public bool EnableAutoDesigns { get; set; } = true; - public bool HideApplyCheckmarks { get; set; } = false; - public bool SmallEquip { get; set; } = false; - public bool UnlockedItemMode { get; set; } = false; - public byte DisableFestivals { get; set; } = 1; - public bool EnableGameContextMenu { get; set; } = true; - public bool HideWindowInCutscene { get; set; } = false; - public bool ShowAutomationSetEditing { get; set; } = true; - public bool ShowAllAutomatedApplicationRules { get; set; } = true; - public bool ShowUnlockedItemWarnings { get; set; } = true; - public bool RevertManualChangesOnZoneChange { get; set; } = false; - public bool ShowQuickBarInTabs { get; set; } = true; - public bool OpenWindowAtStart { get; set; } = false; - public bool ShowWindowWhenUiHidden { get; set; } = false; - public bool UseAdvancedParameters { get; set; } = true; - public bool UseAdvancedDyes { get; set; } = true; - public bool KeepAdvancedDyesAttached { get; set; } = true; - public bool ShowPalettePlusImport { get; set; } = true; - public bool UseFloatForColors { get; set; } = true; - public bool UseRgbForColors { get; set; } = true; - public bool ShowColorConfig { get; set; } = true; - public bool ChangeEntireItem { get; set; } = false; - public bool AlwaysApplyAssociatedMods { get; set; } = false; - public bool AllowDoubleClickToApply { get; set; } = false; - public bool RespectManualOnAutomationUpdate { get; set; } = false; - public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; - public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY); - public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); - public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New; + public bool UseRestrictedGearProtection { get; set; } = false; + public bool OpenFoldersByDefault { get; set; } = false; + public bool AutoRedrawEquipOnChanges { get; set; } = false; + public bool EnableAutoDesigns { get; set; } = true; + public bool HideApplyCheckmarks { get; set; } = false; + public bool SmallEquip { get; set; } = false; + public bool UnlockedItemMode { get; set; } = false; + public byte DisableFestivals { get; set; } = 1; + public bool EnableGameContextMenu { get; set; } = true; + public bool HideWindowInCutscene { get; set; } = false; + public bool ShowAutomationSetEditing { get; set; } = true; + public bool ShowAllAutomatedApplicationRules { get; set; } = true; + public bool ShowUnlockedItemWarnings { get; set; } = true; + public bool RevertManualChangesOnZoneChange { get; set; } = false; + public bool ShowQuickBarInTabs { get; set; } = true; + public bool OpenWindowAtStart { get; set; } = false; + public bool ShowWindowWhenUiHidden { get; set; } = false; + public bool UseAdvancedParameters { get; set; } = true; + public bool UseAdvancedDyes { get; set; } = true; + public bool KeepAdvancedDyesAttached { get; set; } = true; + public bool ShowPalettePlusImport { get; set; } = true; + public bool UseFloatForColors { get; set; } = true; + public bool UseRgbForColors { get; set; } = true; + public bool ShowColorConfig { get; set; } = true; + public bool ChangeEntireItem { get; set; } = false; + public bool AlwaysApplyAssociatedMods { get; set; } = false; + public bool AllowDoubleClickToApply { get; set; } = false; + public bool RespectManualOnAutomationUpdate { get; set; } = false; + + public HeightDisplayType HeightDisplayType { get; set; } = HeightDisplayType.Centimetre; + public RenameField ShowRename { get; set; } = RenameField.BothDataPrio; + public ModifiableHotkey ToggleQuickDesignBar { get; set; } = new(VirtualKey.NO_KEY); + public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); + public ChangeLogDisplayType ChangeLogDisplayType { get; set; } = ChangeLogDisplayType.New; public QdbButtons QdbButtons { get; set; } = QdbButtons.ApplyDesign | QdbButtons.RevertAll | QdbButtons.RevertAutomation | QdbButtons.RevertAdvanced; diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs b/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs index 3e2b453..8668dbe 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.Simple.cs @@ -29,6 +29,27 @@ public partial class CustomizationDrawer ImGui.SameLine(); ImGui.AlignTextToFramePadding(); ImGui.TextUnformatted(_currentOption); + if (_currentIndex is CustomizeIndex.Height) + DrawHeight(); + } + + private void DrawHeight() + { + if (_config.HeightDisplayType is HeightDisplayType.None) + return; + + var height = _heightService.Height(_customize); + ImGui.SameLine(); + + var heightString = _config.HeightDisplayType switch + { + HeightDisplayType.Centimetre => $"({height * 100:F1} cm)", + HeightDisplayType.Metre => $"({height:F2} m)", + HeightDisplayType.Wrong => $"({height * 100 / 2.539:F1} in)", + HeightDisplayType.WrongFoot => $"({(int)(height * 100 / 2.539 / 12)}'{(int)(height * 100 / 2.539) % 12}'')", + _ => $"({height})", + }; + ImGui.TextUnformatted(heightString); } private void DrawPercentageSlider() diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.cs b/Glamourer/Gui/Customization/CustomizationDrawer.cs index 8cb7f91..60251df 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.cs @@ -12,7 +12,13 @@ using Penumbra.GameData.Structs; namespace Glamourer.Gui.Customization; -public partial class CustomizationDrawer(DalamudPluginInterface pi, CustomizeService _service, CodeService _codes, Configuration _config, FavoriteManager _favorites) +public partial class CustomizationDrawer( + DalamudPluginInterface pi, + CustomizeService _service, + CodeService _codes, + Configuration _config, + FavoriteManager _favorites, + HeightService _heightService) : IDisposable { private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f); @@ -20,8 +26,8 @@ public partial class CustomizationDrawer(DalamudPluginInterface pi, CustomizeSer private Exception? _terminate; - private CustomizeArray _customize = CustomizeArray.Default; - private CustomizeSet _set = null!; + private CustomizeArray _customize = CustomizeArray.Default; + private CustomizeSet _set = null!; public CustomizeArray Customize => _customize; @@ -46,7 +52,7 @@ public partial class CustomizationDrawer(DalamudPluginInterface pi, CustomizeSer public bool Draw(CustomizeArray current, bool locked, bool lockedRedraw) { - _withApply = false; + _withApply = false; Init(current, locked, lockedRedraw); return DrawInternal(); diff --git a/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs b/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs index 67ecda7..e9ac60f 100644 --- a/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs +++ b/Glamourer/Gui/Tabs/SettingsTab/SettingsTab.cs @@ -161,6 +161,7 @@ public class SettingsTab( Checkbox("Smaller Equip Display", "Use single-line display without icons and small dye buttons instead of double-line display.", config.SmallEquip, v => config.SmallEquip = v); + DrawHeightUnitSettings(); Checkbox("Show Application Checkboxes", "Show the application checkboxes in the Customization and Equipment panels of the design tab, instead of only showing them under Application Rules.", !config.HideApplyCheckmarks, v => config.HideApplyCheckmarks = !v); @@ -441,4 +442,39 @@ public class SettingsTab( ImGui.TextUnformatted("Rename Fields in Design Context Menu"); ImGuiUtil.HoverTooltip(tt); } + + private void DrawHeightUnitSettings() + { + ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale); + using (var combo = ImRaii.Combo("##heightUnit", HeightDisplayTypeName(config.HeightDisplayType))) + { + if (combo) + foreach (var type in Enum.GetValues()) + { + if (ImGui.Selectable(HeightDisplayTypeName(type), type == config.HeightDisplayType) && type != config.HeightDisplayType) + { + config.HeightDisplayType = type; + config.Save(); + } + } + } + + ImGui.SameLine(); + const string tt = "Select how to display the height of characters in real-world units, if at all."; + ImGuiComponents.HelpMarker(tt); + ImGui.SameLine(); + ImGui.TextUnformatted("Character Height Display Type"); + ImGuiUtil.HoverTooltip(tt); + } + + private string HeightDisplayTypeName(HeightDisplayType type) + => type switch + { + HeightDisplayType.None => "Do Not Display", + HeightDisplayType.Centimetre => "Centimetres (000.0 cm)", + HeightDisplayType.Metre => "Metres (0.00 m)", + HeightDisplayType.Wrong => "Inches (00.0 in)", + HeightDisplayType.WrongFoot => "Feet (0'00'')", + _ => string.Empty, + }; } diff --git a/Glamourer/Services/HeightService.cs b/Glamourer/Services/HeightService.cs new file mode 100644 index 0000000..48f0dd6 --- /dev/null +++ b/Glamourer/Services/HeightService.cs @@ -0,0 +1,23 @@ +using Dalamud.Plugin.Services; +using Dalamud.Utility.Signatures; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using OtterGui.Services; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; + +namespace Glamourer.Services; + +public unsafe class HeightService : IService +{ + [Signature("E8 ?? ?? ?? FF 48 8B 0D ?? ?? ?? ?? 0F 28 F0")] + private readonly delegate* unmanaged[Stdcall] _calculateHeight = null!; + + public HeightService(IGameInteropProvider interop) + => interop.InitializeFromAttributes(this); + + public float Height(CustomizeValue height, SubRace clan, Gender gender, CustomizeValue bodyType) + => _calculateHeight(CharacterUtility.Instance(), height.Value, (byte)clan, (byte)((byte)gender - 1), bodyType.Value); + + public float Height(in CustomizeArray customize) + => Height(customize[CustomizeIndex.Height], customize.Clan, customize.Gender, customize.BodyType); +}