We have config at home.

This commit is contained in:
Ottermandias 2023-06-18 13:38:45 +02:00
parent d10cb3137f
commit 80ab57e96d
13 changed files with 487 additions and 62 deletions

View file

@ -13,7 +13,15 @@ namespace Glamourer;
public class Configuration : IPluginConfiguration, ISavable
{
public bool UseRestrictedGearProtection = true;
public bool UseRestrictedGearProtection { get; set; } = true;
public MainWindow.TabType SelectedTab { get; set; } = MainWindow.TabType.Settings;
#if DEBUG
public bool DebugMode { get; set; } = true;
#else
public bool DebugMode { get; set; } = false;
#endif
public int Version { get; set; } = Constants.CurrentVersion;
@ -30,7 +38,7 @@ public class Configuration : IPluginConfiguration, ISavable
}
public void Save()
=> _saveService.QueueSave(this);
=> _saveService.DelaySave(this);
public void Load(ConfigMigrationService migrator)
{
@ -68,8 +76,8 @@ public class Configuration : IPluginConfiguration, ISavable
public void Save(StreamWriter writer)
{
using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
var serializer = new JsonSerializer { Formatting = Formatting.Indented };
using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
var serializer = new JsonSerializer { Formatting = Formatting.Indented };
serializer.Serialize(jWriter, this);
}
@ -77,4 +85,4 @@ public class Configuration : IPluginConfiguration, ISavable
{
public const int CurrentVersion = 2;
}
}
}

View file

@ -19,7 +19,7 @@ public class Design : ISavable
internal Design(ItemManager items)
{
SetDefaultEquipment(items);
DesignData.SetDefaultEquipment(items);
}
// Metadata
@ -35,20 +35,6 @@ public class Design : ISavable
internal DesignData DesignData;
public void SetDefaultEquipment(ItemManager items)
{
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
DesignData.SetItem(slot, ItemManager.NothingItem(slot));
DesignData.SetStain(slot, 0);
}
DesignData.SetItem(EquipSlot.MainHand, items.DefaultSword);
DesignData.SetStain(EquipSlot.MainHand, 0);
DesignData.SetItem(EquipSlot.OffHand, ItemManager.NothingItem(FullEquipType.Shield));
DesignData.SetStain(EquipSlot.OffHand, 0);
}
#endregion
#region Application Data
@ -272,7 +258,7 @@ public class Design : ISavable
{
if (equip == null)
{
design.SetDefaultEquipment(items);
design.DesignData.SetDefaultEquipment(items);
Glamourer.Chat.NotificationMessage("The loaded design does not contain any equipment data, reset to default.", "Warning",
NotificationType.Warning);
return;

View file

@ -1,8 +1,13 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Client.Game.Gauge;
using Glamourer.Customization;
using Glamourer.Services;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String.Functions;
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
namespace Glamourer.Designs;
@ -32,7 +37,7 @@ public unsafe struct DesignData
private byte _states;
public DesignData()
{}
{ }
public readonly StainId Stain(EquipSlot slot)
{
@ -167,6 +172,53 @@ public unsafe struct DesignData
return true;
}
public void SetDefaultEquipment(ItemManager items)
{
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
SetItem(slot, ItemManager.NothingItem(slot));
SetStain(slot, 0);
}
SetItem(EquipSlot.MainHand, items.DefaultSword);
SetStain(EquipSlot.MainHand, 0);
SetItem(EquipSlot.OffHand, ItemManager.NothingItem(FullEquipType.Shield));
SetStain(EquipSlot.OffHand, 0);
}
public void LoadNonHuman(uint modelId, Customize customize, byte* equipData)
{
ModelId = modelId;
Customize.Load(customize);
fixed (byte* ptr = _equipmentBytes)
{
MemoryUtility.MemCpyUnchecked(ptr, equipData, 40);
}
}
public readonly byte[] GetCustomizeBytes()
{
var ret = new byte[CustomizeData.Size];
fixed (byte* retPtr = ret, inPtr = Customize.Data.Data)
{
MemoryUtility.MemCpyUnchecked(retPtr, inPtr, ret.Length);
}
return ret;
}
public readonly byte[] GetEquipmentBytes()
{
var ret = new byte[40];
fixed (byte* retPtr = ret, inPtr = _equipmentBytes)
{
MemoryUtility.MemCpyUnchecked(retPtr, inPtr, ret.Length);
}
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static bool SetIfDifferent<T>(ref T old, T value) where T : IEquatable<T>
{
@ -176,4 +228,4 @@ public unsafe struct DesignData
old = value;
return true;
}
}
}

View file

@ -11,6 +11,11 @@ public enum ColorId
public static class Colors
{
public const uint DiscordColor = 0xFFDA8972;
public const uint ReniColorButton = 0xFFCC648D;
public const uint ReniColorHovered = 0xFFB070B0;
public const uint ReniColorActive = 0xFF9070E0;
public static (uint DefaultColor, string Name, string Description) Data(this ColorId color)
=> color switch
{

View file

@ -4,15 +4,29 @@ using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Glamourer.Gui.Tabs;
using ImGuiNET;
using OtterGui.Custom;
using OtterGui.Widgets;
namespace Glamourer.Gui;
public class MainWindow : Window
{
private readonly ITab[] _tabs;
public enum TabType
{
None = -1,
Settings = 0,
Debug = 1,
}
public MainWindow(DalamudPluginInterface pi, DebugTab debugTab)
private readonly Configuration _config;
private readonly ITab[] _tabs;
public readonly SettingsTab Settings;
public readonly DebugTab Debug;
public TabType SelectTab = TabType.None;
public MainWindow(DalamudPluginInterface pi, Configuration config, SettingsTab settings, DebugTab debugTab)
: base(GetLabel())
{
pi.UiBuilder.DisableGposeUiHide = true;
@ -21,19 +35,64 @@ public class MainWindow : Window
MinimumSize = new Vector2(675, 675),
MaximumSize = ImGui.GetIO().DisplaySize,
};
Settings = settings;
Debug = debugTab;
_config = config;
_tabs = new ITab[]
{
settings,
debugTab,
};
IsOpen = _config.DebugMode;
}
public override void Draw()
{
TabBar.Draw("##tabs", ImGuiTabBarFlags.None, ReadOnlySpan<byte>.Empty, out var currentTab, () => { }, _tabs);
if (!TabBar.Draw("##tabs", ImGuiTabBarFlags.None, ToLabel(SelectTab), out var currentTab, () => { }, _tabs))
return;
SelectTab = TabType.None;
_config.SelectedTab = FromLabel(currentTab);
_config.Save();
}
private ReadOnlySpan<byte> ToLabel(TabType type)
=> type switch
{
TabType.Settings => Settings.Label,
TabType.Debug => Debug.Label,
_ => ReadOnlySpan<byte>.Empty,
};
private TabType FromLabel(ReadOnlySpan<byte> label)
{
// @formatter:off
if (label == Settings.Label) return TabType.Settings;
if (label == Debug.Label) return TabType.Debug;
// @formatter:on
return TabType.None;
}
private static string GetLabel()
=> Glamourer.Version.Length == 0
? "Glamourer###GlamourerMainWindow"
: $"Glamourer v{Glamourer.Version}###GlamourerMainWindow";
/// <summary> Draw the support button group on the right-hand side of the window. </summary>
public static void DrawSupportButtons()
{
var width = ImGui.CalcTextSize("Join Discord for Support").X + ImGui.GetStyle().FramePadding.X * 2;
var xPos = ImGui.GetWindowWidth() - width;
// Respect the scroll bar width.
if (ImGui.GetScrollMaxY() > 0)
xPos -= ImGui.GetStyle().ScrollbarSize + ImGui.GetStyle().FramePadding.X;
ImGui.SetCursorPos(new Vector2(xPos, 0));
CustomGui.DrawDiscordButton(Glamourer.Chat, width);
ImGui.SetCursorPos(new Vector2(xPos, ImGui.GetFrameHeightWithSpacing()));
CustomGui.DrawGuideButton(Glamourer.Chat, width);
}
}

View file

@ -12,6 +12,7 @@ using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.Interop.Structs;
using Glamourer.Services;
using Glamourer.State;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
@ -24,6 +25,7 @@ namespace Glamourer.Gui.Tabs;
public unsafe class DebugTab : ITab
{
private readonly Configuration _config;
private readonly VisorService _visorService;
private readonly ChangeCustomizeService _changeCustomizeService;
private readonly UpdateSlotService _updateSlotService;
@ -39,12 +41,17 @@ public unsafe class DebugTab : ITab
private readonly DesignManager _designManager;
private readonly DesignFileSystem _designFileSystem;
private readonly StateManager _state;
private int _gameObjectIndex;
public bool IsVisible
=> _config.DebugMode;
public DebugTab(ChangeCustomizeService changeCustomizeService, VisorService visorService, ObjectTable objects,
UpdateSlotService updateSlotService, WeaponService weaponService, PenumbraService penumbra,
ActorService actors, ItemManager items, CustomizationService customization, ObjectManager objectManager,
DesignFileSystem designFileSystem, DesignManager designManager)
DesignFileSystem designFileSystem, DesignManager designManager, StateManager state, Configuration config)
{
_changeCustomizeService = changeCustomizeService;
_visorService = visorService;
@ -58,6 +65,8 @@ public unsafe class DebugTab : ITab
_objectManager = objectManager;
_designFileSystem = designFileSystem;
_designManager = designManager;
_state = state;
_config = config;
}
public ReadOnlySpan<byte> Label
@ -69,6 +78,7 @@ public unsafe class DebugTab : ITab
DrawGameDataHeader();
DrawPenumbraHeader();
DrawDesigns();
DrawState();
}
#region Interop
@ -724,7 +734,8 @@ public unsafe class DebugTab : ITab
continue;
DrawDesign(design);
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.ApplyEquip, design.ApplyCustomize, design.DoApplyHatVisible(),
var base64 = DesignBase64Migration.CreateOldBase64(design.DesignData, design.ApplyEquip, design.ApplyCustomize,
design.DoApplyHatVisible(),
design.DoApplyVisorToggle(), design.DoApplyWeaponVisible(), design.WriteProtected());
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGuiUtil.TextWrapped(base64);
@ -773,7 +784,7 @@ public unsafe class DebugTab : ITab
}
else if (_restore.Length > 0)
{
DrawDesignData(_parse64, true);
DrawDesignData(_parse64);
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGui.TextUnformatted(_base64);
using (var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing with { X = 0 }))
@ -818,44 +829,60 @@ public unsafe class DebugTab : ITab
}
}
private static void DrawDesignData(in DesignData data, bool createTable)
private static void DrawDesignData(in DesignData data)
{
using var table = createTable ? ImRaii.Table("##equip", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit) : null;
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
if (data.ModelId == 0)
{
var item = data.Item(slot);
var stain = data.Stain(slot);
ImGuiUtil.DrawTableColumn(slot.ToName());
ImGuiUtil.DrawTableColumn(item.Name);
ImGuiUtil.DrawTableColumn(item.Id.ToString());
ImGuiUtil.DrawTableColumn(stain.ToString());
}
using var table = ImRaii.Table("##equip", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
{
var item = data.Item(slot);
var stain = data.Stain(slot);
ImGuiUtil.DrawTableColumn(slot.ToName());
ImGuiUtil.DrawTableColumn(item.Name);
ImGuiUtil.DrawTableColumn(item.Id.ToString());
ImGuiUtil.DrawTableColumn(stain.ToString());
}
ImGuiUtil.DrawTableColumn("Hat Visible");
ImGuiUtil.DrawTableColumn(data.IsHatVisible().ToString());
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Visor Toggled");
ImGuiUtil.DrawTableColumn(data.IsVisorToggled().ToString());
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Weapon Visible");
ImGuiUtil.DrawTableColumn(data.IsWeaponVisible().ToString());
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Hat Visible");
ImGuiUtil.DrawTableColumn(data.IsHatVisible().ToString());
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Visor Toggled");
ImGuiUtil.DrawTableColumn(data.IsVisorToggled().ToString());
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Weapon Visible");
ImGuiUtil.DrawTableColumn(data.IsWeaponVisible().ToString());
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Model ID");
ImGuiUtil.DrawTableColumn(data.ModelId.ToString());
ImGui.TableNextRow();
ImGuiUtil.DrawTableColumn("Model ID");
ImGuiUtil.DrawTableColumn(data.ModelId.ToString());
ImGui.TableNextRow();
foreach (var index in Enum.GetValues<CustomizeIndex>())
{
var value = data.Customize[index];
ImGuiUtil.DrawTableColumn(index.ToDefaultName());
ImGuiUtil.DrawTableColumn(value.Value.ToString());
foreach (var index in Enum.GetValues<CustomizeIndex>())
{
var value = data.Customize[index];
ImGuiUtil.DrawTableColumn(index.ToDefaultName());
ImGuiUtil.DrawTableColumn(value.Value.ToString());
ImGui.TableNextRow();
}
ImGuiUtil.DrawTableColumn("Is Wet");
ImGuiUtil.DrawTableColumn(data.IsWet().ToString());
ImGui.TableNextRow();
}
else
{
ImGui.TextUnformatted($"Model ID {data.ModelId}");
ImGui.Separator();
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImGui.TextUnformatted("Customize Array");
ImGui.Separator();
ImGuiUtil.TextWrapped(string.Join(" ", data.GetCustomizeBytes().Select(b => b.ToString("X2"))));
ImGuiUtil.DrawTableColumn("Is Wet");
ImGuiUtil.DrawTableColumn(data.IsWet().ToString());
ImGui.TableNextRow();
ImGui.TextUnformatted("Equipment Array");
ImGui.Separator();
ImGuiUtil.TextWrapped(string.Join(" ", data.GetEquipmentBytes().Select(b => b.ToString("X2"))));
}
}
private void DrawDesign(Design design)
@ -933,4 +960,51 @@ public unsafe class DebugTab : ITab
}
#endregion
#region State
private void DrawState()
{
if (!ImGui.CollapsingHeader($"State ({_state.Count})###State"))
return;
DrawActorTrees();
DrawRetainedStates();
}
private void DrawActorTrees()
{
using var tree = ImRaii.TreeNode("Active Actors");
if (!tree)
return;
_objectManager.Update();
foreach (var (identifier, actors) in _objectManager)
{
using var t = ImRaii.TreeNode(actors.Label);
if (!t)
continue;
if (_state.GetOrCreate(identifier, actors.Objects[0], out var state))
DrawDesignData(state.Data);
else
ImGui.TextUnformatted("Invalid actor.");
}
}
private void DrawRetainedStates()
{
using var tree = ImRaii.TreeNode("Retained States (Inactive Actors)");
if (!tree)
return;
foreach (var (identifier, state) in _state.Where(kvp => !_objectManager.ContainsKey(kvp.Key)))
{
using var t = ImRaii.TreeNode(identifier.ToString());
if (t)
DrawDesignData(state.Data);
}
}
#endregion
}

View file

@ -0,0 +1,68 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
namespace Glamourer.Gui.Tabs;
public class SettingsTab : ITab
{
private readonly Configuration _config;
public SettingsTab(Configuration config)
=> _config = config;
public ReadOnlySpan<byte> Label
=> "Settings"u8;
public void DrawContent()
{
using var child = ImRaii.Child("##SettingsTab", -Vector2.One, false);
if (!child)
return;
Checkbox("Restricted Gear Protection",
"Use gender- and race-appropriate models when detecting certain items not available for a characters current gender and race.",
_config.UseRestrictedGearProtection, v => _config.UseRestrictedGearProtection = v);
Checkbox("Debug Mode", "Show the debug tab. Only useful for debugging or advanced use.", _config.DebugMode, v => _config.DebugMode = v);
DrawColorSettings();
MainWindow.DrawSupportButtons();
}
/// <summary> Draw the entire Color subsection. </summary>
private void DrawColorSettings()
{
if (!ImGui.CollapsingHeader("Colors"))
return;
foreach (var color in Enum.GetValues<ColorId>())
{
var (defaultColor, name, description) = color.Data();
var currentColor = _config.Colors.TryGetValue(color, out var current) ? current : defaultColor;
if (Widget.ColorPicker(name, description, currentColor, c => _config.Colors[color] = c, defaultColor))
_config.Save();
}
ImGui.NewLine();
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void Checkbox(string label, string tooltip, bool current, Action<bool> setter)
{
using var id = ImRaii.PushId(label);
var tmp = current;
if (ImGui.Checkbox(string.Empty, ref tmp) && tmp != current)
{
setter(tmp);
_config.Save();
}
ImGui.SameLine();
ImGuiUtil.LabeledHelpMarker(label, tooltip);
}
}

View file

@ -3,6 +3,7 @@ using System;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.System.String;
using Glamourer.Customization;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String;
@ -103,6 +104,9 @@ public readonly unsafe struct Actor : IEquatable<Actor>
public CharacterWeapon GetOffhand()
=> *(CharacterWeapon*)&AsCharacter->DrawData.OffHandModel;
public Customize GetCustomize()
=> *(Customize*)&AsCharacter->DrawData.CustomizeData;
public override string ToString()
=> $"0x{Address:X}";
}

View file

@ -1,5 +1,6 @@
using System;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Customization;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object;
@ -89,6 +90,9 @@ public readonly unsafe struct Model : IEquatable<Model>
public CharacterArmor GetArmor(EquipSlot slot)
=> ((CharacterArmor*)AsHuman->EquipSlotData)[slot.ToIndex()];
public Customize GetCustomize()
=> *(Customize*)&AsHuman->Customize;
public (Model Address, CharacterWeapon Data) GetMainhand()
{
Model weapon = AsDrawObject->Object.ChildObject;

View file

@ -5,6 +5,7 @@ using Glamourer.Gui;
using Glamourer.Gui.Tabs;
using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.State;
using Microsoft.Extensions.DependencyInjection;
using OtterGui.Classes;
using OtterGui.Log;
@ -23,6 +24,7 @@ public static class ServiceManager
.AddEvents()
.AddData()
.AddDesigns()
.AddState()
.AddUi()
.AddApi();
@ -68,8 +70,12 @@ public static class ServiceManager
=> services.AddSingleton<DesignManager>()
.AddSingleton<DesignFileSystem>();
private static IServiceCollection AddState(this IServiceCollection services)
=> services.AddSingleton<StateManager>();
private static IServiceCollection AddUi(this IServiceCollection services)
=> services.AddSingleton<DebugTab>()
.AddSingleton<SettingsTab>()
.AddSingleton<MainWindow>()
.AddSingleton<GlamourerWindowSystem>();

View file

@ -0,0 +1,13 @@
using Glamourer.Designs;
using Penumbra.GameData.Actors;
namespace Glamourer.State;
public class ActorState
{
public ActorIdentifier Identifier { get; internal init; }
public DesignData Data { get; internal set; }
internal ActorState(ActorIdentifier identifier)
=> Identifier = identifier;
}

View file

@ -0,0 +1,146 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Glamourer.Customization;
using Glamourer.Designs;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using Glamourer.Services;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.State;
public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
{
private readonly ActorService _actors;
private readonly ItemManager _items;
private readonly CustomizationService _customizations;
private readonly VisorService _visor;
private readonly Dictionary<ActorIdentifier, ActorState> _states = new();
public StateManager(ActorService actors, ItemManager items, CustomizationService customizations, VisorService visor)
{
_actors = actors;
_items = items;
_customizations = customizations;
_visor = visor;
}
public bool GetOrCreate(Actor actor, [NotNullWhen(true)] out ActorState? state)
=> GetOrCreate(actor.GetIdentifier(_actors.AwaitedService), actor, out state);
public bool GetOrCreate(ActorIdentifier identifier, Actor actor, [NotNullWhen(true)] out ActorState? state)
{
if (TryGetValue(identifier, out state))
return true;
try
{
var designData = FromActor(actor);
_states.Add(identifier, new ActorState(identifier) { Data = designData });
return true;
}
catch (Exception ex)
{
Glamourer.Log.Error($"Could not create new actor data for {identifier}:\n{ex}");
return false;
}
}
public IEnumerator<KeyValuePair<ActorIdentifier, ActorState>> GetEnumerator()
=> _states.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
public int Count
=> _states.Count;
public bool ContainsKey(ActorIdentifier key)
=> _states.ContainsKey(key);
public bool TryGetValue(ActorIdentifier key, out ActorState value)
=> _states.TryGetValue(key, out value!);
public ActorState this[ActorIdentifier key]
=> _states[key];
public IEnumerable<ActorIdentifier> Keys
=> _states.Keys;
public IEnumerable<ActorState> Values
=> _states.Values;
public unsafe DesignData FromActor(Actor actor)
{
var ret = new DesignData();
if (!actor.IsCharacter)
{
ret.SetDefaultEquipment(_items);
return ret;
}
if (actor.AsCharacter->ModelCharaId != 0)
{
ret.LoadNonHuman((uint)actor.AsCharacter->ModelCharaId, *(Customize*)&actor.AsCharacter->DrawData.CustomizeData,
(byte*)&actor.AsCharacter->DrawData.Head);
return ret;
}
var model = actor.Model;
CharacterWeapon main;
CharacterWeapon off;
ret.SetHatVisible(!actor.AsCharacter->DrawData.IsHatHidden);
if (model.IsHuman)
{
var head = ret.IsHatVisible() ? model.GetArmor(EquipSlot.Head) : actor.GetArmor(EquipSlot.Head);
var headItem = _items.Identify(EquipSlot.Head, head.Set, head.Variant);
ret.SetItem(EquipSlot.Head, headItem);
ret.SetStain(EquipSlot.Head, head.Stain);
foreach (var slot in EquipSlotExtensions.EqdpSlots.Skip(1))
{
var armor = model.GetArmor(slot);
var item = _items.Identify(slot, armor.Set, armor.Variant);
ret.SetItem(slot, item);
ret.SetStain(slot, armor.Stain);
}
ret.Customize = model.GetCustomize();
(_, _, main, off) = model.GetWeapons(actor);
ret.SetVisor(_visor.GetVisorState(model));
}
else
{
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
var armor = actor.GetArmor(slot);
var item = _items.Identify(slot, armor.Set, armor.Variant);
ret.SetItem(slot, item);
ret.SetStain(slot, armor.Stain);
}
ret.Customize = actor.GetCustomize();
main = actor.GetMainhand();
off = actor.GetOffhand();
ret.SetVisor(actor.AsCharacter->DrawData.IsVisorToggled);
}
var mainItem = _items.Identify(EquipSlot.MainHand, main.Set, main.Type, (byte)main.Variant);
var offItem = _items.Identify(EquipSlot.OffHand, off.Set, off.Type, (byte)off.Variant, mainItem.Type);
ret.SetItem(EquipSlot.MainHand, mainItem);
ret.SetStain(EquipSlot.MainHand, main.Stain);
ret.SetItem(EquipSlot.OffHand, offItem);
ret.SetStain(EquipSlot.OffHand, off.Stain);
ret.SetIsWet(actor.AsCharacter->IsGPoseWet);
ret.SetWeaponVisible(!actor.AsCharacter->DrawData.IsWeaponHidden);
return ret;
}
}

View file

@ -117,16 +117,16 @@ public partial class CustomizationDrawer : IDisposable
foreach (var id in _set.Order[CharaMakeParams.MenuType.Percentage])
PercentageSelector(id);
Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.IconSelector], DrawIconSelector, ImGui.SameLine);
CustomGui.IteratePairwise(_set.Order[CharaMakeParams.MenuType.IconSelector], DrawIconSelector, ImGui.SameLine);
DrawMultiIconSelector();
foreach (var id in _set.Order[CharaMakeParams.MenuType.ListSelector])
DrawListSelector(id);
Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.ColorPicker], DrawColorPicker, ImGui.SameLine);
CustomGui.IteratePairwise(_set.Order[CharaMakeParams.MenuType.ColorPicker], DrawColorPicker, ImGui.SameLine);
Functions.IteratePairwise(_set.Order[CharaMakeParams.MenuType.Checkmark], DrawCheckbox,
CustomGui.IteratePairwise(_set.Order[CharaMakeParams.MenuType.Checkmark], DrawCheckbox,
() => ImGui.SameLine(_inputIntSize + _framedIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X));
return Changed != 0;
}