Do the Reworkings

This commit is contained in:
Ottermandias 2022-09-29 23:57:37 +02:00
parent a67cd8ad89
commit e3a58340b3
23 changed files with 1067 additions and 943 deletions

View file

@ -1,17 +1,13 @@
using System;
using System.Reflection;
using System.Reflection;
using Dalamud.Game.Command;
using Dalamud.Hooking;
using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Dalamud.Plugin;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Api;
using Glamourer.Customization;
using Glamourer.Gui;
using Glamourer.Interop;
using Glamourer.State;
using OtterGui.Log;
using Penumbra.GameData;
namespace Glamourer;
@ -32,6 +28,7 @@ public class Glamourer : IDalamudPlugin
public static GlamourerConfig Config = null!;
public static Logger Log = null!;
public static IObjectIdentifier Identifier = null!;
public static PenumbraAttach Penumbra = null!;
@ -55,6 +52,7 @@ public class Glamourer : IDalamudPlugin
try
{
Dalamud.Initialize(pluginInterface);
Log = new Logger();
Customization = CustomizationManager.Create(Dalamud.PluginInterface, Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
RestrictedGear = GameData.RestrictedGear(Dalamud.GameData);

View file

@ -0,0 +1,65 @@
using System.Numerics;
using Glamourer.Customization;
using ImGuiNET;
using OtterGui.Raii;
namespace Glamourer.Gui.Customization;
internal partial class CustomizationDrawer
{
private const string ColorPickerPopupName = "ColorPicker";
private void DrawColorPicker(CustomizationId id)
{
using var _ = SetId(id);
var (current, custom) = GetCurrentCustomization(id);
var color = ImGui.ColorConvertU32ToFloat4(custom.Color);
// Print 1-based index instead of 0.
if (ImGui.ColorButton($"{current + 1}##color", color, ImGuiColorEditFlags.None, _framedIconSize))
ImGui.OpenPopup(ColorPickerPopupName);
ImGui.SameLine();
using (var group = ImRaii.Group())
{
DataInputInt(current);
ImGui.TextUnformatted(_currentOption);
}
DrawColorPickerPopup();
}
private void DrawColorPickerPopup()
{
using var popup = ImRaii.Popup(ColorPickerPopupName, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
return;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
for (var i = 0; i < _currentCount; ++i)
{
var custom = _set.Data(_currentId, i, _customize[CustomizationId.Face]);
if (ImGui.ColorButton((i + 1).ToString(), ImGui.ColorConvertU32ToFloat4(custom.Color)))
{
UpdateValue(custom.Value);
ImGui.CloseCurrentPopup();
}
if (i % 8 != 7)
ImGui.SameLine();
}
}
// Obtain the current customization and print a warning if it is not known.
private (int, CustomizationData) GetCurrentCustomization(CustomizationId id)
{
var current = _set.DataByValue(id, _customize[id], out var custom);
if (!_set.IsAvailable(id) || current >= 0)
return (current, custom!.Value);
Glamourer.Log.Warning($"Read invalid customization value {_customize[id]} for {id}.");
return (0, _set.Data(id, 0));
}
}

View file

@ -0,0 +1,62 @@
using System;
using System.Linq;
using Dalamud.Interface;
using Glamourer.Customization;
using Glamourer.Util;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui.Customization;
internal partial class CustomizationDrawer
{
private void DrawRaceGenderSelector()
{
DrawGenderSelector();
ImGui.SameLine();
using var group = ImRaii.Group();
DrawRaceCombo();
var gender = Glamourer.Customization.GetName(CustomName.Gender);
var clan = Glamourer.Customization.GetName(CustomName.Clan);
ImGui.TextUnformatted($"{gender} & {clan}");
}
private void DrawGenderSelector()
{
using var font = ImRaii.PushFont(UiBuilder.IconFont);
var icon = _customize.Gender == Gender.Male ? FontAwesomeIcon.Mars : FontAwesomeIcon.Venus;
var restricted = _customize.Race == Race.Hrothgar;
if (restricted)
icon = FontAwesomeIcon.MarsDouble;
if (!ImGuiUtil.DrawDisabledButton(icon.ToIconString(), _framedIconSize, string.Empty, restricted, true))
return;
var gender = _customize.Gender == Gender.Male ? Gender.Female : Gender.Male;
if (!_customize.ChangeGender(_equip, gender))
return;
foreach (var actor in _actors.Where(a => a))
Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw, false);
}
private void DrawRaceCombo()
{
ImGui.SetNextItemWidth(_raceSelectorWidth);
using var combo = ImRaii.Combo("##subRaceCombo", _customize.ClanName());
if (!combo)
return;
foreach (var subRace in Enum.GetValues<SubRace>().Skip(1)) // Skip Unknown
{
if (!ImGui.Selectable(CustomizeExtensions.ClanName(subRace, _customize.Gender), subRace == _customize.Clan)
|| !_customize.ChangeRace(_equip, subRace))
continue;
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw, false);
}
}
}

View file

@ -0,0 +1,109 @@
using System;
using System.Linq;
using System.Numerics;
using Glamourer.Customization;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui.Customization;
internal partial class CustomizationDrawer
{
private const string IconSelectorPopup = "Style Picker";
private void DrawIconSelector(CustomizationId id)
{
using var _ = SetId(id);
using var bigGroup = ImRaii.Group();
var label = _currentOption;
var current = _set.DataByValue(id, _currentByte, out var custom);
if (current < 0)
{
label = $"{_currentOption} (Custom #{_customize[id]})";
current = 0;
custom = _set.Data(id, 0);
}
var icon = Glamourer.Customization.GetIcon(custom!.Value.IconId);
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
ImGui.OpenPopup(IconSelectorPopup);
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
ImGui.SameLine();
using (var group = ImRaii.Group())
{
if (_currentId == CustomizationId.Face)
FaceInputInt(current);
else
DataInputInt(current);
ImGui.TextUnformatted($"{label} ({custom.Value.Value})");
}
DrawIconPickerPopup();
}
private void UpdateFace(CustomizationData data)
{
// Hrothgar Hack
var value = _set.Race == Race.Hrothgar ? data.Value + 4 : data.Value;
if (_customize.Face == value)
return;
_customize.Face = value;
foreach (var actor in _actors)
Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw, false);
}
private void FaceInputInt(int currentIndex)
{
++currentIndex;
ImGui.SetNextItemWidth(_inputIntSize);
if (ImGui.InputInt("##text", ref currentIndex, 1, 1))
{
currentIndex = Math.Clamp(currentIndex - 1, 0, _currentCount - 1);
var data = _set.Data(_currentId, currentIndex, _customize.Face);
UpdateFace(data);
}
ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]");
}
private void DrawIconPickerPopup()
{
using var popup = ImRaii.Popup(IconSelectorPopup, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
return;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
for (var i = 0; i < _currentCount; ++i)
{
var custom = _set.Data(_currentId, i, _customize.Face);
var icon = Glamourer.Customization.GetIcon(custom.IconId);
using (var _ = ImRaii.Group())
{
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
{
if (_currentId == CustomizationId.Face)
UpdateFace(custom);
else
UpdateValue(custom.Value);
ImGui.CloseCurrentPopup();
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
var text = custom.Value.ToString();
var textWidth = ImGui.CalcTextSize(text).X;
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (_iconSize.X - textWidth + 2 * ImGui.GetStyle().FramePadding.X) / 2);
ImGui.TextUnformatted(text);
}
if (i % 8 != 7)
ImGui.SameLine();
}
}
}

View file

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Reflection;
using Glamourer.Customization;
using Glamourer.Interop;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Customization;
internal partial class CustomizationDrawer
{
private static readonly Vector4 RedTint = new(0.6f, 0.3f, 0.3f, 1f);
private static readonly ImGuiScene.TextureWrap? LegacyTattoo;
private readonly Vector2 _iconSize;
private readonly Vector2 _framedIconSize;
private readonly float _inputIntSize;
private readonly float _comboSelectorSize;
private readonly float _raceSelectorWidth;
private Customize _customize;
private CharacterEquip _equip;
private IReadOnlyCollection<Actor> _actors = Array.Empty<Actor>();
private CustomizationSet _set = null!;
private CustomizationDrawer()
{
_iconSize = new Vector2(ImGui.GetTextLineHeightWithSpacing() * 2);
_framedIconSize = _iconSize + 2 * ImGui.GetStyle().FramePadding;
_inputIntSize = 2 * _framedIconSize.X + ImGui.GetStyle().ItemSpacing.X;
_comboSelectorSize = 4 * _framedIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X;
_raceSelectorWidth = _inputIntSize + _comboSelectorSize - _framedIconSize.X;
}
static CustomizationDrawer()
=> LegacyTattoo = GetLegacyTattooIcon();
public static void Dispose()
=> LegacyTattoo?.Dispose();
private static ImGuiScene.TextureWrap? GetLegacyTattooIcon()
{
using var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Glamourer.LegacyTattoo.raw");
if (resource == null)
return null;
var rawImage = new byte[resource.Length];
var length = resource.Read(rawImage, 0, (int)resource.Length);
return length == resource.Length
? Dalamud.PluginInterface.UiBuilder.LoadImageRaw(rawImage, 192, 192, 4)
: null;
}
public static void Draw(Customize customize, CharacterEquip equip, IReadOnlyCollection<Actor> actors, bool locked)
{
var d = new CustomizationDrawer()
{
_customize = customize,
_equip = equip,
_actors = actors,
};
if (!ImGui.CollapsingHeader("Character Customization"))
return;
using var disabled = ImRaii.Disabled(locked);
d.DrawRaceGenderSelector();
d._set = Glamourer.Customization.GetList(customize.Clan, customize.Gender);
foreach (var id in d._set.Order[CharaMakeParams.MenuType.Percentage])
d.PercentageSelector(id);
Functions.IteratePairwise(d._set.Order[CharaMakeParams.MenuType.IconSelector], d.DrawIconSelector, ImGui.SameLine);
d.DrawMultiIconSelector();
foreach (var id in d._set.Order[CharaMakeParams.MenuType.ListSelector])
d.DrawListSelector(id);
Functions.IteratePairwise(d._set.Order[CharaMakeParams.MenuType.ColorPicker], d.DrawColorPicker, ImGui.SameLine);
d.Checkbox(d._set.Option(CustomizationId.HighlightsOnFlag), customize.HighlightsOn, b => customize.HighlightsOn = b);
var xPos = d._inputIntSize + d._framedIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X;
ImGui.SameLine(xPos);
d.Checkbox($"{Glamourer.Customization.GetName(CustomName.Reverse)} {d._set.Option(CustomizationId.FacePaint)}",
customize.FacePaintReversed, b => customize.FacePaintReversed = b);
d.Checkbox($"{Glamourer.Customization.GetName(CustomName.IrisSmall)} {Glamourer.Customization.GetName(CustomName.IrisSize)}",
customize.SmallIris, b => customize.SmallIris = b);
if (customize.Race != Race.Hrothgar)
{
ImGui.SameLine(xPos);
d.Checkbox(d._set.Option(CustomizationId.LipColor), customize.Lipstick, b => customize.Lipstick = b);
}
}
public static void Draw(Customize customize, IReadOnlyCollection<Actor> actors, bool locked = false)
=> Draw(customize, CharacterEquip.Null, actors, locked);
public static void Draw(Customize customize, CharacterEquip equip, bool locked = false)
=> Draw(customize, equip, Array.Empty<Actor>(), locked);
public static void Draw(Customize customize, bool locked = false)
=> Draw(customize, CharacterEquip.Null, Array.Empty<Actor>(), locked);
// Set state for drawing of current customization.
private CustomizationId _currentId;
private CustomizationByteValue _currentByte = CustomizationByteValue.Zero;
private int _currentCount;
private string _currentOption = string.Empty;
// Prepare a new customization option.
private ImRaii.Id SetId(CustomizationId id)
{
_currentId = id;
_currentByte = _customize[id];
_currentCount = _set.Count(id, _customize.Face);
_currentOption = _set.Option(id);
return ImRaii.PushId((int)id);
}
// Update the current id with a value,
// also update actors if any.
private void UpdateValue(CustomizationByteValue value)
{
if (_customize[_currentId] == value)
return;
_customize[_currentId] = value;
UpdateActors();
}
// Update all relevant Actors by calling the UpdateCustomize game function.
private void UpdateActors()
{
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
}

View file

@ -0,0 +1,51 @@
using System.Linq;
using System.Numerics;
using Glamourer.Customization;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
namespace Glamourer.Gui.Customization;
internal partial class CustomizationDrawer
{
// Only used for facial features, so fixed ID.
private void DrawMultiIconSelector()
{
using var _ = SetId(CustomizationId.FacialFeaturesTattoos);
using var bigGroup = ImRaii.Group();
DrawMultiIcons();
ImGui.SameLine();
using var group = ImRaii.Group();
ImGui.Dummy(new Vector2(0, ImGui.GetTextLineHeightWithSpacing() + ImGui.GetStyle().ItemSpacing.Y / 2));
_currentCount = 256;
PercentageInputInt();
ImGui.TextUnformatted(_set.Option(CustomizationId.FacialFeaturesTattoos));
}
private void DrawMultiIcons()
{
using var _ = ImRaii.Group();
for (var i = 0; i < _currentCount; ++i)
{
var enabled = _customize.FacialFeatures[i];
var feature = _set.FacialFeature(_customize.Face, i);
var icon = i == _currentCount - 1
? LegacyTattoo ?? Glamourer.Customization.GetIcon(feature.IconId)
: Glamourer.Customization.GetIcon(feature.IconId);
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize, Vector2.Zero, Vector2.One, (int)ImGui.GetStyle().FramePadding.X,
Vector4.Zero, enabled ? Vector4.One : RedTint))
{
_customize.FacialFeatures.Set(i, !enabled);
UpdateActors();
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
if (i % 4 != 3)
ImGui.SameLine();
}
}
}

View file

@ -0,0 +1,104 @@
using System;
using System.Linq;
using Glamourer.Customization;
using ImGuiNET;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Raii;
namespace Glamourer.Gui.Customization;
internal partial class CustomizationDrawer
{
private void DrawListSelector(CustomizationId id)
{
using var _ = SetId(id);
using var bigGroup = ImRaii.Group();
ListCombo();
ImGui.SameLine();
ListInputInt();
ImGui.SameLine();
ImGui.TextUnformatted(_currentOption);
}
private void ListCombo()
{
ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale);
using var combo = ImRaii.Combo("##combo", $"{_currentOption} #{_currentByte.Value + 1}");
if (!combo)
return;
for (var i = 0; i < _currentCount; ++i)
{
if (ImGui.Selectable($"{_currentOption} #{i + 1}##combo", i == _currentByte.Value))
UpdateValue((CustomizationByteValue)i);
}
}
private void ListInputInt()
{
var tmp = _currentByte.Value + 1;
ImGui.SetNextItemWidth(_inputIntSize);
if (ImGui.InputInt("##text", ref tmp, 1, 1) && tmp > 0 && tmp <= _currentCount)
UpdateValue((CustomizationByteValue)Math.Clamp(tmp - 1, 0, _currentCount - 1));
ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]");
}
private void PercentageSelector(CustomizationId id)
{
using var _ = SetId(id);
using var bigGroup = ImRaii.Group();
DrawPercentageSlider();
ImGui.SameLine();
PercentageInputInt();
ImGui.SameLine();
ImGui.TextUnformatted(_currentOption);
}
private void DrawPercentageSlider()
{
var tmp = (int)_currentByte.Value;
ImGui.SetNextItemWidth(_comboSelectorSize);
if (ImGui.SliderInt("##slider", ref tmp, 0, _currentCount - 1, "%i", ImGuiSliderFlags.AlwaysClamp))
UpdateValue((CustomizationByteValue)tmp);
}
private void PercentageInputInt()
{
var tmp = (int)_currentByte.Value;
ImGui.SetNextItemWidth(_inputIntSize);
if (ImGui.InputInt("##text", ref tmp, 1, 1))
UpdateValue((CustomizationByteValue)Math.Clamp(tmp, 0, _currentCount - 1));
ImGuiUtil.HoverTooltip($"Input Range: [0, {_currentCount - 1}]");
}
// Draw one of the four checkboxes for single bool customization options.
private void Checkbox(string label, bool current, Action<bool> setter)
{
var tmp = current;
if (ImGui.Checkbox(label, ref tmp) && tmp != current)
{
setter(tmp);
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
}
// Integral input for an icon- or color based item.
private void DataInputInt(int currentIndex)
{
++currentIndex;
ImGui.SetNextItemWidth(_inputIntSize);
if (ImGui.InputInt("##text", ref currentIndex, 1, 1))
{
currentIndex = Math.Clamp(currentIndex - 1, 0, _currentCount - 1);
var data = _set.Data(_currentId, currentIndex, _customize.Face);
UpdateValue(data.Value);
}
ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]");
}
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Dalamud.Interface;
using Glamourer.Customization;
using Glamourer.Interop;
using Glamourer.Structs;
@ -10,8 +9,6 @@ using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using static Glamourer.Interop.Actor;
using static Lumina.Data.Parsing.Layer.LayerCommon;
namespace Glamourer.Gui;
@ -19,13 +16,16 @@ internal partial class Interface
{
//public class EquipmentDrawer
//{
// private static
// private static readonly IReadOnlyDictionary<StainId, Stain> Stains;
//
// private Race _race;
// private Gender _gender;
// private CharacterEquip _equip;
// private IReadOnlyCollection<Actor> _actors = Array.Empty<Actor>();
//
// static EquipmentDrawer()
// => Stains = GameData.Stains(Dalamud.GameData);
//
// public static void Draw(Customize customize, CharacterEquip equip, IReadOnlyCollection<Actor> actors, bool locked)
// {
// var d = new EquipmentDrawer()
@ -44,13 +44,10 @@ internal partial class Interface
// private bool DrawStainSelector(ComboWithFilter<Stain> stainCombo, EquipSlot slot, StainId stainIdx)
// {
// stainCombo.PostPreview = null;
// if (_stains.TryGetValue((byte)stainIdx, out var stain))
// {
// var previewPush = PushColor(stain, ImGuiCol.FrameBg);
// stainCombo.PostPreview = () => ImGui.PopStyleColor(previewPush);
// }
//
// var found = Stains.TryGetValue(stainIdx, out var stain);
// using var color = ImRaii.PushColor(ImGuiCol.FrameBg, stain.RgbaColor, found);
// var change = stainCombo.Draw(string.Empty, out var newStain) && !newStain.RowIndex.Equals(stainIdx);
// if ()
// if (!change && (byte)stainIdx != 0)
// {
// ImGuiUtil.HoverTooltip("Right-click to clear.");

View file

@ -2,6 +2,7 @@
using System.Numerics;
using Dalamud.Interface;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Gui.Customization;
using Glamourer.Interop;
using Glamourer.State;
using ImGuiNET;
@ -57,12 +58,8 @@ internal partial class Interface
if (_currentData.Valid)
_currentSave.Update(_currentData.Objects[0]);
var d = _currentData.Objects[0].DrawObject.Pointer;
var x = (*(delegate* unmanaged<Human*, byte>**)d)[50](d);
ImGui.Text($"{x} {_currentData.Objects[0].ModelId}");
if (x == 1)
CustomizationDrawer.Draw(_currentSave.Data.Customize, _currentSave.Data.Equipment, _currentData.Objects,
_identifier is Actor.SpecialIdentifier);
CustomizationDrawer.Draw(_currentSave.Data.Customize, _currentSave.Data.Equipment, _currentData.Objects,
_identifier is Actor.SpecialIdentifier);
}
private const uint RedHeaderColor = 0xFF1818C0;

View file

@ -1,402 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface;
using Dalamud.Logging;
using Glamourer.Customization;
using Glamourer.Interop;
using Glamourer.Util;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Race = Penumbra.GameData.Enums.Race;
namespace Glamourer.Gui;
internal partial class Interface
{
private class CustomizationDrawer
{
private Customize _customize;
private CharacterEquip _equip;
private IReadOnlyCollection<Actor> _actors = Array.Empty<Actor>();
private CustomizationSet _set = null!;
public static void Draw(Customize customize, CharacterEquip equip, IReadOnlyCollection<Actor> actors, bool locked)
{
var d = new CustomizationDrawer()
{
_customize = customize,
_equip = equip,
_actors = actors,
};
if (!ImGui.CollapsingHeader("Character Customization"))
return;
using var disabled = ImRaii.Disabled(locked);
d.DrawRaceGenderSelector();
d._set = Glamourer.Customization.GetList(customize.Clan, customize.Gender);
foreach (var id in d._set.Order[CharaMakeParams.MenuType.Percentage])
d.PercentageSelector(id);
Functions.IteratePairwise(d._set.Order[CharaMakeParams.MenuType.IconSelector], d.DrawIconSelector, ImGui.SameLine);
d.DrawMultiIconSelector();
foreach (var id in d._set.Order[CharaMakeParams.MenuType.ListSelector])
d.DrawListSelector(id);
Functions.IteratePairwise(d._set.Order[CharaMakeParams.MenuType.ColorPicker], d.DrawColorPicker, ImGui.SameLine);
d.Checkbox(d._set.Option(CustomizationId.HighlightsOnFlag), customize.HighlightsOn, b => customize.HighlightsOn = b);
var xPos = _inputIntSize + _framedIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X;
ImGui.SameLine(xPos);
d.Checkbox($"{Glamourer.Customization.GetName(CustomName.Reverse)} {d._set.Option(CustomizationId.FacePaint)}",
customize.FacePaintReversed, b => customize.FacePaintReversed = b);
d.Checkbox($"{Glamourer.Customization.GetName(CustomName.IrisSmall)} {Glamourer.Customization.GetName(CustomName.IrisSize)}",
customize.SmallIris, b => customize.SmallIris = b);
if (customize.Race != Race.Hrothgar)
{
ImGui.SameLine(xPos);
d.Checkbox(d._set.Option(CustomizationId.LipColor), customize.Lipstick, b => customize.Lipstick = b);
}
}
private void DrawRaceGenderSelector()
{
DrawGenderSelector();
ImGui.SameLine();
using var group = ImRaii.Group();
DrawRaceCombo();
var gender = Glamourer.Customization.GetName(CustomName.Gender);
var clan = Glamourer.Customization.GetName(CustomName.Clan);
ImGui.TextUnformatted($"{gender} & {clan}");
}
private void DrawGenderSelector()
{
using var font = ImRaii.PushFont(UiBuilder.IconFont);
var icon = _customize.Gender == Gender.Male ? FontAwesomeIcon.Mars : FontAwesomeIcon.Venus;
var restricted = _customize.Race == Race.Hrothgar;
if (restricted)
icon = FontAwesomeIcon.MarsDouble;
if (!ImGuiUtil.DrawDisabledButton(icon.ToIconString(), _framedIconSize, string.Empty, restricted, true))
return;
var gender = _customize.Gender == Gender.Male ? Gender.Female : Gender.Male;
if (!_customize.ChangeGender(_equip, gender))
return;
foreach (var actor in _actors.Where(a => a))
Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw, false);
}
private void DrawRaceCombo()
{
ImGui.SetNextItemWidth(_raceSelectorWidth);
using var combo = ImRaii.Combo("##subRaceCombo", _customize.ClanName());
if (!combo)
return;
foreach (var subRace in Enum.GetValues<SubRace>().Skip(1)) // Skip Unknown
{
if (ImGui.Selectable(CustomizeExtensions.ClanName(subRace, _customize.Gender), subRace == _customize.Clan)
&& _customize.ChangeRace(_equip, subRace))
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw, false);
}
}
private void Checkbox(string label, bool current, Action<bool> setter)
{
var tmp = current;
if (ImGui.Checkbox($"##{label}", ref tmp) && tmp != current)
{
setter(tmp);
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
ImGui.SameLine();
ImGui.TextUnformatted(label);
}
private void PercentageSelector(CustomizationId id)
{
using var bigGroup = ImRaii.Group();
using var _ = ImRaii.PushId((int)id);
int value = _customize[id];
var count = _set.Count(id);
ImGui.SetNextItemWidth(_comboSelectorSize);
void OnChange(int v)
{
_customize[id] = (byte)v;
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
if (ImGui.SliderInt("##slider", ref value, 0, count - 1, "%i", ImGuiSliderFlags.AlwaysClamp))
OnChange(value);
ImGui.SameLine();
InputInt("##input", --value, 0, count - 1, OnChange);
ImGui.SameLine();
ImGui.TextUnformatted(_set.OptionName[(int)id]);
}
private static void InputInt(string label, int startValue, int minValue, int maxValue, Action<int> setter)
{
var tmp = startValue + 1;
ImGui.SetNextItemWidth(_inputIntSize);
if (ImGui.InputInt(label, ref tmp, 1, 1, ImGuiInputTextFlags.EnterReturnsTrue)
&& tmp != startValue + 1
&& tmp >= minValue
&& tmp <= maxValue)
setter(tmp);
ImGuiUtil.HoverTooltip($"Input Range: [{minValue}, {maxValue}]");
}
private void DrawIconSelector(CustomizationId id)
{
const string popupName = "Style Picker";
using var bigGroup = ImRaii.Group();
using var _ = ImRaii.PushId((int)id);
var count = _set.Count(id, _customize.Face);
var label = _set.Option(id);
var current = _set.DataByValue(id, _customize[id], out var custom);
if (current < 0)
{
label = $"{label} (Custom #{_customize[id]})";
current = 0;
custom = _set.Data(id, 0);
}
var icon = Glamourer.Customization.GetIcon(custom!.Value.IconId);
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
ImGui.OpenPopup(popupName);
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
void OnChange(int v)
{
var value = _set.Data(id, v - 1).Value;
// Hrothgar hack
if (_set.Race == Race.Hrothgar && id == CustomizationId.Face)
value += 4;
if (_customize[id] == value)
return;
_customize[id] = value;
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
ImGui.SameLine();
using var group = ImRaii.Group();
InputInt("##text", current, 1, count, OnChange);
ImGui.TextUnformatted($"{label} ({custom.Value.Value})");
DrawIconPickerPopup(popupName, id, OnChange);
}
private void DrawIconPickerPopup(string label, CustomizationId id, Action<int> setter)
{
using var popup = ImRaii.Popup(label, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
return;
var count = _set.Count(id, _customize.Face);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
for (var i = 0; i < count; ++i)
{
var custom = _set.Data(id, i, _customize.Face);
var icon = Glamourer.Customization.GetIcon(custom.IconId);
using var group = ImRaii.Group();
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
{
setter(custom.Value);
ImGui.CloseCurrentPopup();
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
var text = custom.Value.ToString();
var textWidth = ImGui.CalcTextSize(text).X;
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + (_iconSize.X - textWidth + 2 * ImGui.GetStyle().FramePadding.X) / 2);
ImGui.TextUnformatted(text);
group.Dispose();
if (i % 8 != 7)
ImGui.SameLine();
}
}
private void DrawColorPicker(CustomizationId id)
{
const string popupName = "Color Picker";
using var _ = ImRaii.PushId((int)id);
var count = _set.Count(id);
var label = _set.Option(id);
var (current, custom) = GetCurrentCustomization(id);
if (ImGui.ColorButton($"{current + 1}##color", ImGui.ColorConvertU32ToFloat4(custom.Color), ImGuiColorEditFlags.None,
_framedIconSize))
ImGui.OpenPopup(popupName);
ImGui.SameLine();
void OnChange(int v)
{
_customize[id] = _set.Data(id, v).Value;
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
using (var group = ImRaii.Group())
{
InputInt("##text", current, 1, count, OnChange);
ImGui.TextUnformatted(label);
}
DrawColorPickerPopup(popupName, id, OnChange);
}
private (int, Customization.Customization) GetCurrentCustomization(CustomizationId id)
{
var current = _set.DataByValue(id, _customize[id], out var custom);
if (_set.IsAvailable(id) && current < 0)
{
PluginLog.Warning($"Read invalid customization value {_customize[id]} for {id}.");
current = 0;
custom = _set.Data(id, 0);
}
return (current, custom!.Value);
}
private void DrawColorPickerPopup(string label, CustomizationId id, Action<int> setter)
{
using var popup = ImRaii.Popup(label, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
return;
var count = _set.Count(id);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
for (var i = 0; i < count; ++i)
{
var custom = _set.Data(id, i);
if (ImGui.ColorButton((i + 1).ToString(), ImGui.ColorConvertU32ToFloat4(custom.Color)))
{
setter(custom.Value);
ImGui.CloseCurrentPopup();
}
if (i % 8 != 7)
ImGui.SameLine();
}
}
private void DrawMultiIconSelector()
{
using var bigGroup = ImRaii.Group();
using var _ = ImRaii.PushId((int)CustomizationId.FacialFeaturesTattoos);
void OnChange(int v)
{
_customize[CustomizationId.FacialFeaturesTattoos] = (byte)v;
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
DrawMultiIcons();
ImGui.SameLine();
int value = _customize[CustomizationId.FacialFeaturesTattoos];
using var group = ImRaii.Group();
ImGui.Dummy(new Vector2(0, ImGui.GetTextLineHeightWithSpacing() + ImGui.GetStyle().ItemSpacing.Y / 2));
InputInt(string.Empty, --value, 0, 255, OnChange);
ImGui.TextUnformatted(_set.Option(CustomizationId.FacialFeaturesTattoos));
}
private void DrawMultiIcons()
{
using var _ = ImRaii.Group();
var face = _customize.Face;
var ret = false;
var count = _set.Count(CustomizationId.FacialFeaturesTattoos);
for (var i = 0; i < count; ++i)
{
var enabled = _customize.FacialFeatures[i];
var feature = _set.FacialFeature(face, i);
var icon = i == count - 1
? LegacyTattoo ?? Glamourer.Customization.GetIcon(feature.IconId)
: Glamourer.Customization.GetIcon(feature.IconId);
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize, Vector2.Zero, Vector2.One, (int)ImGui.GetStyle().FramePadding.X,
Vector4.Zero, enabled ? Vector4.One : RedTint))
{
_customize.FacialFeatures.Set(i, !enabled);
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
if (i % 4 != 3)
ImGui.SameLine();
}
}
private void DrawListSelector(CustomizationId id)
{
using var _ = ImRaii.PushId((int)id);
using var bigGroup = ImRaii.Group();
int current = _customize[id];
var count = _set.Count(id);
void OnChange(int v)
{
_customize[id] = (byte)v;
foreach (var actor in _actors.Where(a => a && a.DrawObject))
Glamourer.RedrawManager.UpdateCustomize(actor.DrawObject, _customize);
}
ImGui.SetNextItemWidth(_comboSelectorSize * ImGui.GetIO().FontGlobalScale);
using (var combo = ImRaii.Combo("##combo", $"{_set.Option(id)} #{current + 1}"))
{
if (combo)
for (var i = 0; i < count; ++i)
{
if (!ImGui.Selectable($"{_set.Option(id)} #{i + 1}##combo", i == current) || i == current)
continue;
OnChange(i);
}
}
ImGui.SameLine();
InputInt("##text", current, 1, count, OnChange);
ImGui.SameLine();
ImGui.TextUnformatted(_set.Option(id));
}
}
}

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using Glamourer.Gui.Customization;
using Glamourer.Interop;
using Glamourer.State;
using ImGuiNET;

View file

@ -1,6 +1,4 @@
using System;
using System.Numerics;
using System.Reflection;
using System.Numerics;
using Dalamud.Interface;
using ImGuiNET;
@ -8,41 +6,12 @@ namespace Glamourer.Gui;
internal partial class Interface
{
private static readonly ImGuiScene.TextureWrap? LegacyTattoo = GetLegacyTattooIcon();
private static readonly Vector4 RedTint = new(0.6f, 0.3f, 0.3f, 1f);
private static Vector2 _iconSize = Vector2.Zero;
private static Vector2 _framedIconSize = Vector2.Zero;
private static Vector2 _spacing = Vector2.Zero;
private static float _actorSelectorWidth;
private static float _inputIntSize;
private static float _comboSelectorSize;
private static float _raceSelectorWidth;
private static void UpdateState()
{
// General
_spacing = _spacing with { Y = ImGui.GetTextLineHeightWithSpacing() / 2 };
_actorSelectorWidth = 200 * ImGuiHelpers.GlobalScale;
// Customize
_iconSize = new Vector2(ImGui.GetTextLineHeightWithSpacing() * 2);
_framedIconSize = _iconSize + 2 * ImGui.GetStyle().FramePadding;
_inputIntSize = 2 * _framedIconSize.X + ImGui.GetStyle().ItemSpacing.X;
_comboSelectorSize = 4 * _framedIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X;
_raceSelectorWidth = _inputIntSize + _comboSelectorSize - _framedIconSize.X;
}
private static ImGuiScene.TextureWrap? GetLegacyTattooIcon()
{
using var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Glamourer.LegacyTattoo.raw");
if (resource == null)
return null;
var rawImage = new byte[resource.Length];
var length = resource.Read(rawImage, 0, (int)resource.Length);
return length == resource.Length
? Dalamud.PluginInterface.UiBuilder.LoadImageRaw(rawImage, 192, 192, 4)
: null;
}
}

View file

@ -3,6 +3,7 @@ using System.Linq;
using System.Numerics;
using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Glamourer.Gui.Customization;
using ImGuiNET;
using OtterGui.Raii;
@ -56,6 +57,7 @@ internal partial class Interface : Window, IDisposable
public void Dispose()
{
Dalamud.PluginInterface.UiBuilder.OpenConfigUi -= Toggle;
CustomizationDrawer.Dispose();
}
private static string GetLabel()

View file

@ -159,7 +159,7 @@ public struct CharacterData
public interface ICharacterData
{
public CharacterData Data { get; }
public ref CharacterData Data { get; }
//public bool ApplyModel();
//public bool ApplyCustomize(Customize target);

View file

@ -8,8 +8,8 @@ namespace Glamourer.State;
public unsafe class CurrentDesign : ICharacterData
{
public CharacterData Data
=> _drawData;
public ref CharacterData Data
=> ref _drawData;
private CharacterData _drawData;
private CharacterData _initialData;

View file

@ -90,12 +90,12 @@ public static unsafe class CustomizeExtensions
customize.Load(newCustomize);
}
public static bool ChangeCustomization(this Customize customize, CharacterEquip equip, CustomizationId id, byte value)
public static bool ChangeCustomization(this Customize customize, CharacterEquip equip, CustomizationId id, CustomizationByteValue value)
{
switch (id)
{
case CustomizationId.Race: return customize.ChangeRace(equip, (SubRace)value);
case CustomizationId.Gender: return customize.ChangeGender(equip, (Gender)value);
case CustomizationId.Race: return customize.ChangeRace(equip, (SubRace)value.Value);
case CustomizationId.Gender: return customize.ChangeGender(equip, (Gender)value.Value);
}
if (customize[id] == value)
@ -113,17 +113,17 @@ public static unsafe class CustomizeExtensions
{
switch (id)
{
case CustomizationId.Race: break;
case CustomizationId.Clan: break;
case CustomizationId.BodyType: break;
case CustomizationId.Gender: break;
case CustomizationId.FacialFeaturesTattoos: break;
case CustomizationId.HighlightsOnFlag: break;
case CustomizationId.Face: break;
case CustomizationId.Race: break;
case CustomizationId.Clan: break;
case CustomizationId.BodyType: break;
case CustomizationId.Gender: break;
case CustomizationId.FacialFeaturesTattoos: break;
case CustomizationId.HighlightsOnFlag: break;
case CustomizationId.Face: break;
default:
var count = set.Count(id);
if (set.DataByValue(id, customize[id], out _) < 0)
customize[id] = count == 0 ? (byte)0 : set.Data(id, 0).Value;
customize[id] = count == 0 ? CustomizationByteValue.Zero : set.Data(id, 0).Value;
break;
}
}
@ -131,7 +131,7 @@ public static unsafe class CustomizeExtensions
private static void FixRestrictedGear(Customize customize, CharacterEquip equip, Gender gender, Race race)
{
if (race == customize.Race && gender == customize.Gender)
if (!equip || race == customize.Race && gender == customize.Gender)
return;
foreach (var slot in EquipSlotExtensions.EqdpSlots)