mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-16 05:34:25 +01:00
Add NPC appearance tab.
This commit is contained in:
parent
4b242bb3cf
commit
0c1dd50890
18 changed files with 684 additions and 35 deletions
|
|
@ -26,9 +26,9 @@ public class DesignColorUi
|
|||
|
||||
public DesignColorUi(DesignColors colors, DesignManager designs, Configuration config)
|
||||
{
|
||||
_colors = colors;
|
||||
_designs = designs;
|
||||
_config = config;
|
||||
_colors = colors;
|
||||
_designs = designs;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
|
|
@ -78,7 +78,7 @@ public class DesignColorUi
|
|||
{
|
||||
using var id = ImRaii.PushId(idx);
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), buttonSize, tt, disabled, true))
|
||||
{
|
||||
changeString = name;
|
||||
|
|
@ -119,7 +119,7 @@ public class DesignColorUi
|
|||
changeString = _newName;
|
||||
changeValue = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (changeString.Length > 0)
|
||||
{
|
||||
|
|
@ -139,6 +139,7 @@ public class DesignColorUi
|
|||
newColor = color;
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip(tooltip);
|
||||
|
||||
newColor = ImGui.ColorConvertFloat4ToU32(vec);
|
||||
|
|
@ -148,12 +149,12 @@ public class DesignColorUi
|
|||
|
||||
public class DesignColors : ISavable, IReadOnlyDictionary<string, uint>
|
||||
{
|
||||
public const string AutomaticName = "Automatic";
|
||||
public const string AutomaticName = "Automatic";
|
||||
public const string MissingColorName = "Missing Color";
|
||||
public const uint MissingColorDefault = 0xFF0000D0;
|
||||
|
||||
private readonly SaveService _saveService;
|
||||
private readonly Dictionary<string, uint> _colors = new();
|
||||
private readonly Dictionary<string, uint> _colors = [];
|
||||
public uint MissingColor { get; private set; } = MissingColorDefault;
|
||||
|
||||
public event Action? ColorChanged;
|
||||
|
|
|
|||
|
|
@ -39,12 +39,18 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
=> ShareBase64(state, EquipFlagExtensions.All, CustomizeFlagExtensions.All, CrestExtensions.All);
|
||||
|
||||
public string ShareBase64(ActorState state, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
=> ShareBase64(state.ModelData, equipFlags, customizeFlags, crestFlags);
|
||||
|
||||
public string ShareBase64(in DesignData data, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
{
|
||||
var design = Convert(state, equipFlags, customizeFlags, crestFlags);
|
||||
var design = Convert(data, equipFlags, customizeFlags, crestFlags);
|
||||
return ShareBase64(ShareJObject(design));
|
||||
}
|
||||
|
||||
public DesignBase Convert(ActorState state, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
=> Convert(state.ModelData, equipFlags, customizeFlags, crestFlags);
|
||||
|
||||
public DesignBase Convert(in DesignData data, EquipFlag equipFlags, CustomizeFlag customizeFlags, CrestFlag crestFlags)
|
||||
{
|
||||
var design = _designs.CreateTemporary();
|
||||
design.ApplyEquip = equipFlags & EquipFlagExtensions.All;
|
||||
|
|
@ -54,7 +60,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
design.SetApplyVisorToggle(design.DoApplyEquip(EquipSlot.Head));
|
||||
design.SetApplyWeaponVisible(design.DoApplyEquip(EquipSlot.MainHand) || design.DoApplyEquip(EquipSlot.OffHand));
|
||||
design.SetApplyWetness(true);
|
||||
design.SetDesignData(_customize, state.ModelData);
|
||||
design.SetDesignData(_customize, data);
|
||||
return design;
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +150,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
}
|
||||
|
||||
public IEnumerable<(EquipSlot Slot, EquipItem Item, StainId Stain)> FromDrawData(IReadOnlyList<CharacterArmor> armors,
|
||||
CharacterWeapon mainhand, CharacterWeapon offhand)
|
||||
CharacterWeapon mainhand, CharacterWeapon offhand, bool skipWarnings)
|
||||
{
|
||||
if (armors.Count != 10)
|
||||
throw new ArgumentException("Invalid length of armor array.");
|
||||
|
|
@ -156,7 +162,8 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
var item = _items.Identify(slot, armor.Set, armor.Variant);
|
||||
if (!item.Valid)
|
||||
{
|
||||
Glamourer.Log.Warning($"Appearance data {armor} for slot {slot} invalid, item could not be identified.");
|
||||
if (!skipWarnings)
|
||||
Glamourer.Log.Warning($"Appearance data {armor} for slot {slot} invalid, item could not be identified.");
|
||||
item = ItemManager.NothingItem(slot);
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +171,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
}
|
||||
|
||||
var mh = _items.Identify(EquipSlot.MainHand, mainhand.Skeleton, mainhand.Weapon, mainhand.Variant);
|
||||
if (!mh.Valid)
|
||||
if (!skipWarnings && !mh.Valid)
|
||||
{
|
||||
Glamourer.Log.Warning($"Appearance data {mainhand} for mainhand weapon invalid, item could not be identified.");
|
||||
mh = _items.DefaultSword;
|
||||
|
|
@ -173,7 +180,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
|
|||
yield return (EquipSlot.MainHand, mh, mainhand.Stain);
|
||||
|
||||
var oh = _items.Identify(EquipSlot.OffHand, offhand.Skeleton, offhand.Weapon, offhand.Variant, mh.Type);
|
||||
if (!oh.Valid)
|
||||
if (!skipWarnings && !oh.Valid)
|
||||
{
|
||||
Glamourer.Log.Warning($"Appearance data {offhand} for offhand weapon invalid, item could not be identified.");
|
||||
oh = _items.GetDefaultOffhand(mh);
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList<NpcData>
|
|||
// Convert the NPCs to a dictionary of lists grouped by name.
|
||||
var groups = eNpcEquip.Concat(bNpcEquip).GroupBy(d => d.Name).ToDictionary(g => g.Key, g => g.ToList());
|
||||
// Iterate through the sorted list.
|
||||
foreach (var (name, duplicates) in groups.OrderBy(kvp => kvp.Key))
|
||||
foreach (var (_, duplicates) in groups.OrderBy(kvp => kvp.Key))
|
||||
{
|
||||
// Remove any duplicate entries for a name with identical data.
|
||||
for (var i = 0; i < duplicates.Count; ++i)
|
||||
|
|
@ -177,7 +177,7 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList<NpcData>
|
|||
}
|
||||
}
|
||||
|
||||
// If there is only a single entry, add that. This does not take additional string memory through interning.
|
||||
// If there is only a single entry, add that.
|
||||
if (duplicates.Count == 1)
|
||||
{
|
||||
_data.Add(duplicates[0]);
|
||||
|
|
@ -185,13 +185,8 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList<NpcData>
|
|||
}
|
||||
else
|
||||
{
|
||||
// Add all distinct duplicates with their ID specified in the name.
|
||||
_data.AddRange(duplicates
|
||||
.Select(duplicate => duplicate with
|
||||
{
|
||||
Name = $"{name} ({(duplicate.Kind is ObjectKind.BattleNpc ? 'B' : 'E')}{duplicate.Id})",
|
||||
}));
|
||||
Memory += 96 * duplicates.Count + duplicates.Sum(d => d.Name.Length * 2);
|
||||
_data.AddRange(duplicates);
|
||||
Memory += 96 * duplicates.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ public enum ColorId
|
|||
TriStateCheck,
|
||||
TriStateCross,
|
||||
TriStateNeutral,
|
||||
BattleNpc,
|
||||
EventNpc,
|
||||
}
|
||||
|
||||
public static class Colors
|
||||
|
|
@ -60,7 +62,9 @@ public static class Colors
|
|||
ColorId.QuickDesignBg => (0x00F0F0F0, "Quick Design Bar Window Background", "The color of the window background in the quick design bar." ),
|
||||
ColorId.TriStateCheck => (0xFF00D000, "Checkmark in Tri-State Checkboxes", "The color of the checkmark indicating positive change in tri-state checkboxes." ),
|
||||
ColorId.TriStateCross => (0xFF0000D0, "Cross in Tri-State Checkboxes", "The color of the cross indicating negative change in tri-state checkboxes." ),
|
||||
ColorId.TriStateNeutral => (0xFFD0D0D0, "Dot in Tri-State Checkboxes", "The color of the dot indicating no change in tri-state checkboxes" ),
|
||||
ColorId.TriStateNeutral => (0xFFD0D0D0, "Dot in Tri-State Checkboxes", "The color of the dot indicating no change in tri-state checkboxes." ),
|
||||
ColorId.BattleNpc => (0xFFFFFFFF, "Battle NPC in NPC Tab", "The color of the names of battle NPCs in the NPC tab that do not have a more specific color assigned." ),
|
||||
ColorId.EventNpc => (0xFFFFFFFF, "Event NPC in NPC Tab", "The color of the names of event NPCs in the NPC tab that do not have a more specific color assigned." ),
|
||||
_ => (0x00000000, string.Empty, string.Empty ),
|
||||
// @formatter:on
|
||||
};
|
||||
|
|
|
|||
|
|
@ -82,8 +82,11 @@ public partial class CustomizationDrawer
|
|||
if (_customize.BodyType.Value == 1)
|
||||
return;
|
||||
|
||||
if (!ImGuiUtil.DrawDisabledButton($"Reset Body Type {_customize.BodyType.Value} to Default",
|
||||
new Vector2(_raceSelectorWidth + _framedIconSize.X + ImGui.GetStyle().ItemSpacing.X, 0), string.Empty, _lockedRedraw))
|
||||
var label = _lockedRedraw
|
||||
? $"Body Type {_customize.BodyType.Value}"
|
||||
: $"Reset Body Type {_customize.BodyType.Value} to Default";
|
||||
if (!ImGuiUtil.DrawDisabledButton(label, new Vector2(_raceSelectorWidth + _framedIconSize.X + ImGui.GetStyle().ItemSpacing.X, 0),
|
||||
string.Empty, _lockedRedraw))
|
||||
return;
|
||||
|
||||
Changed |= CustomizeFlag.BodyType;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Glamourer.Gui.Tabs.ActorTab;
|
|||
using Glamourer.Gui.Tabs.AutomationTab;
|
||||
using Glamourer.Gui.Tabs.DebugTab;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
using Glamourer.Gui.Tabs.NpcTab;
|
||||
using Glamourer.Gui.Tabs.UnlocksTab;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Custom;
|
||||
|
|
@ -29,6 +30,7 @@ public class MainWindow : Window, IDisposable
|
|||
Automation = 4,
|
||||
Unlocks = 5,
|
||||
Messages = 6,
|
||||
Npcs = 7,
|
||||
}
|
||||
|
||||
private readonly Configuration _config;
|
||||
|
|
@ -42,12 +44,14 @@ public class MainWindow : Window, IDisposable
|
|||
public readonly DesignTab Designs;
|
||||
public readonly AutomationTab Automation;
|
||||
public readonly UnlocksTab Unlocks;
|
||||
public readonly NpcTab Npcs;
|
||||
public readonly MessagesTab Messages;
|
||||
|
||||
public TabType SelectTab = TabType.None;
|
||||
|
||||
public MainWindow(DalamudPluginInterface pi, Configuration config, SettingsTab settings, ActorTab actors, DesignTab designs,
|
||||
DebugTab debugTab, AutomationTab automation, UnlocksTab unlocks, TabSelected @event, MessagesTab messages, DesignQuickBar quickBar)
|
||||
DebugTab debugTab, AutomationTab automation, UnlocksTab unlocks, TabSelected @event, MessagesTab messages, DesignQuickBar quickBar,
|
||||
NpcTab npcs)
|
||||
: base(GetLabel())
|
||||
{
|
||||
pi.UiBuilder.DisableGposeUiHide = true;
|
||||
|
|
@ -65,6 +69,7 @@ public class MainWindow : Window, IDisposable
|
|||
_event = @event;
|
||||
Messages = messages;
|
||||
_quickBar = quickBar;
|
||||
Npcs = npcs;
|
||||
_config = config;
|
||||
_tabs =
|
||||
[
|
||||
|
|
@ -73,6 +78,7 @@ public class MainWindow : Window, IDisposable
|
|||
designs,
|
||||
automation,
|
||||
unlocks,
|
||||
npcs,
|
||||
messages,
|
||||
debugTab,
|
||||
];
|
||||
|
|
@ -117,6 +123,7 @@ public class MainWindow : Window, IDisposable
|
|||
TabType.Automation => Automation.Label,
|
||||
TabType.Unlocks => Unlocks.Label,
|
||||
TabType.Messages => Messages.Label,
|
||||
TabType.Npcs => Npcs.Label,
|
||||
_ => ReadOnlySpan<byte>.Empty,
|
||||
};
|
||||
|
||||
|
|
@ -128,6 +135,7 @@ public class MainWindow : Window, IDisposable
|
|||
if (label == Settings.Label) return TabType.Settings;
|
||||
if (label == Automation.Label) return TabType.Automation;
|
||||
if (label == Unlocks.Label) return TabType.Unlocks;
|
||||
if (label == Npcs.Label) return TabType.Npcs;
|
||||
if (label == Messages.Label) return TabType.Messages;
|
||||
if (label == Debug.Label) return TabType.Debug;
|
||||
// @formatter:on
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
|
|||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
var resetScroll = ImGui.InputTextWithHint("##npcFilter", "Filter...", ref _npcFilter, 64);
|
||||
|
||||
using var table = ImRaii.Table("npcs", 5, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit,
|
||||
using var table = ImRaii.Table("npcs", 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit,
|
||||
new Vector2(-1, 400 * ImGuiHelpers.GlobalScale));
|
||||
if (!table)
|
||||
return;
|
||||
|
|
@ -46,6 +46,7 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
|
|||
ImGui.TableSetupColumn("Button", ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthFixed, ImGuiHelpers.GlobalScale * 300);
|
||||
ImGui.TableSetupColumn("Kind", ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Id", ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Visor", ImGuiTableColumnFlags.WidthFixed);
|
||||
ImGui.TableSetupColumn("Compare", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
|
|||
ImGui.TableNextColumn();
|
||||
if (ImGuiUtil.DrawDisabledButton("Apply", Vector2.Zero, string.Empty, disabled))
|
||||
{
|
||||
foreach (var (slot, item, stain) in _designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand))
|
||||
foreach (var (slot, item, stain) in _designConverter.FromDrawData(data.Equip.ToArray(), data.Mainhand, data.Offhand, true))
|
||||
_state.ChangeEquip(state!, slot, item, stain, StateChanged.Source.Manual);
|
||||
_state.ChangeVisorState(state!, data.VisorToggled, StateChanged.Source.Manual);
|
||||
_state.ChangeCustomize(state!, data.Customize, CustomizeFlagExtensions.All, StateChanged.Source.Manual);
|
||||
|
|
@ -80,6 +81,10 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
|
|||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(data.Kind is ObjectKind.BattleNpc ? "B" : "E");
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(data.Id.Id.ToString());
|
||||
|
||||
using (_ = ImRaii.PushFont(UiBuilder.IconFont))
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
|
|
|
|||
141
Glamourer/Gui/Tabs/NpcTab/LocalNpcAppearanceData.cs
Normal file
141
Glamourer/Gui/Tabs/NpcTab/LocalNpcAppearanceData.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Services;
|
||||
using ImGuiNET;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.NpcTab;
|
||||
|
||||
public class LocalNpcAppearanceData : ISavable
|
||||
{
|
||||
private readonly DesignColors _colors;
|
||||
|
||||
public record struct Data(string Color = "", bool Favorite = false);
|
||||
|
||||
private readonly Dictionary<ulong, Data> _data = [];
|
||||
|
||||
public LocalNpcAppearanceData(DesignColors colors, SaveService saveService)
|
||||
{
|
||||
_colors = colors;
|
||||
Load(saveService);
|
||||
DataChanged += () => saveService.QueueSave(this);
|
||||
}
|
||||
|
||||
public bool IsFavorite(in NpcData data)
|
||||
=> _data.TryGetValue(ToKey(data), out var tuple) && tuple.Favorite;
|
||||
|
||||
public (uint Color, bool Favorite) GetData(in NpcData data)
|
||||
=> _data.TryGetValue(ToKey(data), out var t)
|
||||
? (GetColor(t.Color, t.Favorite, data.Kind), t.Favorite)
|
||||
: (GetColor(string.Empty, false, data.Kind), false);
|
||||
|
||||
public string GetColor(in NpcData data)
|
||||
=> _data.TryGetValue(ToKey(data), out var t) ? t.Color : string.Empty;
|
||||
|
||||
private uint GetColor(string color, bool favorite, ObjectKind kind)
|
||||
{
|
||||
if (color.Length == 0)
|
||||
{
|
||||
if (favorite)
|
||||
return ColorId.FavoriteStarOn.Value();
|
||||
|
||||
return kind is ObjectKind.BattleNpc
|
||||
? ColorId.BattleNpc.Value()
|
||||
: ColorId.EventNpc.Value();
|
||||
}
|
||||
|
||||
if (_colors.TryGetValue(color, out var value))
|
||||
return value == 0 ? ImGui.GetColorU32(ImGuiCol.Text) : value;
|
||||
|
||||
return _colors.MissingColor;
|
||||
}
|
||||
|
||||
public void ToggleFavorite(in NpcData data)
|
||||
{
|
||||
var key = ToKey(data);
|
||||
if (_data.TryGetValue(key, out var t))
|
||||
{
|
||||
if (t is { Color: "", Favorite: true })
|
||||
_data.Remove(key);
|
||||
else
|
||||
_data[key] = t with { Favorite = !t.Favorite };
|
||||
}
|
||||
else
|
||||
{
|
||||
_data[key] = new Data(string.Empty, true);
|
||||
}
|
||||
|
||||
DataChanged.Invoke();
|
||||
}
|
||||
|
||||
public void SetColor(in NpcData data, string color)
|
||||
{
|
||||
var key = ToKey(data);
|
||||
if (_data.TryGetValue(key, out var t))
|
||||
{
|
||||
if (!t.Favorite && color.Length == 0)
|
||||
_data.Remove(key);
|
||||
else
|
||||
_data[key] = t with { Color = color };
|
||||
}
|
||||
else if (color.Length != 0)
|
||||
{
|
||||
_data[key] = new Data(color);
|
||||
}
|
||||
|
||||
DataChanged.Invoke();
|
||||
}
|
||||
|
||||
private static ulong ToKey(in NpcData data)
|
||||
=> (byte)data.Kind | ((ulong)data.Id.Id << 8);
|
||||
|
||||
public event Action DataChanged = null!;
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.NpcAppearanceFile;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
var jObj = new JObject()
|
||||
{
|
||||
["Version"] = 1,
|
||||
["Data"] = JToken.FromObject(_data),
|
||||
};
|
||||
using var j = new JsonTextWriter(writer);
|
||||
j.Formatting = Formatting.Indented;
|
||||
jObj.WriteTo(j);
|
||||
}
|
||||
|
||||
private void Load(SaveService save)
|
||||
{
|
||||
var file = save.FileNames.NpcAppearanceFile;
|
||||
if (!File.Exists(file))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(file);
|
||||
var jObj = JObject.Parse(text);
|
||||
var version = jObj["Version"]?.ToObject<int>() ?? 0;
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
var data = jObj["Data"]?.ToObject<Dictionary<ulong, Data>>() ?? [];
|
||||
_data.EnsureCapacity(data.Count);
|
||||
foreach (var kvp in data)
|
||||
_data.Add(kvp.Key, kvp.Value);
|
||||
return;
|
||||
default: throw new Exception("Invalid version {version}.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not read local NPC appearance data:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Glamourer/Gui/Tabs/NpcTab/NpcFilter.cs
Normal file
45
Glamourer/Gui/Tabs/NpcTab/NpcFilter.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.GameData;
|
||||
using OtterGui.Classes;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.NpcTab;
|
||||
|
||||
public sealed class NpcFilter(LocalNpcAppearanceData _favorites) : FilterUtility<NpcData>
|
||||
{
|
||||
protected override string Tooltip
|
||||
=> "Filter NPC appearances for those where their names contain the given substring.\n"
|
||||
+ "Enter i:[number] to filter for NPCs of certain IDs.\n"
|
||||
+ "Enter c:[string] to filter for NPC appearances set to specific colors.";
|
||||
|
||||
protected override (LowerString, long, int) FilterChange(string input)
|
||||
=> input.Length switch
|
||||
{
|
||||
0 => (LowerString.Empty, 0, -1),
|
||||
> 1 when input[1] == ':' =>
|
||||
input[0] switch
|
||||
{
|
||||
'i' or 'I' => input.Length == 2 ? (LowerString.Empty, 0, -1) :
|
||||
long.TryParse(input.AsSpan(2), out var r) ? (LowerString.Empty, r, 1) : (LowerString.Empty, 0, -1),
|
||||
'c' or 'C' => input.Length == 2 ? (LowerString.Empty, 0, -1) : (new LowerString(input[2..]), 0, 2),
|
||||
_ => (new LowerString(input), 0, 0),
|
||||
},
|
||||
_ => (new LowerString(input), 0, 0),
|
||||
};
|
||||
|
||||
public override bool ApplyFilter(in NpcData value)
|
||||
=> FilterMode switch
|
||||
{
|
||||
-1 => false,
|
||||
0 => Filter.IsContained(value.Name),
|
||||
1 => value.Id.Id == NumericalFilter,
|
||||
2 => Filter.IsContained(GetColor(value)),
|
||||
_ => false, // Should never happen
|
||||
};
|
||||
|
||||
private string GetColor(in NpcData value)
|
||||
{
|
||||
var color = _favorites.GetColor(value);
|
||||
return color.Length == 0 ? DesignColors.AutomaticName : color;
|
||||
}
|
||||
}
|
||||
294
Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs
Normal file
294
Glamourer/Gui/Tabs/NpcTab/NpcPanel.cs
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.State;
|
||||
using ImGuiNET;
|
||||
using Lumina.Data.Parsing.Scd;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.NpcTab;
|
||||
|
||||
public class NpcPanel(
|
||||
NpcSelector _selector,
|
||||
LocalNpcAppearanceData _favorites,
|
||||
CustomizationDrawer _customizeDrawer,
|
||||
EquipmentDrawer _equipDrawer,
|
||||
DesignConverter _converter,
|
||||
DesignManager _designManager,
|
||||
StateManager _state,
|
||||
ObjectManager _objects,
|
||||
DesignColors _colors)
|
||||
{
|
||||
private readonly DesignColorCombo _colorCombo = new(_colors, true);
|
||||
private string _newName = string.Empty;
|
||||
private DesignBase? _newDesign;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var group = ImRaii.Group();
|
||||
|
||||
DrawHeader();
|
||||
DrawPanel();
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
{
|
||||
HeaderDrawer.Draw(_selector.HasSelection ? _selector.Selection.Name : "No Selection", ColorId.NormalDesign.Value(),
|
||||
ImGui.GetColorU32(ImGuiCol.FrameBg), 2, ExportToClipboardButton(), SaveAsDesignButton(), FavoriteButton());
|
||||
SaveDesignDrawPopup();
|
||||
}
|
||||
|
||||
private HeaderDrawer.Button FavoriteButton()
|
||||
{
|
||||
var (desc, color) = _favorites.IsFavorite(_selector.Selection)
|
||||
? ("Remove this NPC appearance from your favorites.", ColorId.FavoriteStarOn.Value())
|
||||
: ("Add this NPC Appearance to your favorites.", 0x80000000);
|
||||
return new HeaderDrawer.Button
|
||||
{
|
||||
Icon = FontAwesomeIcon.Star,
|
||||
OnClick = () => _favorites.ToggleFavorite(_selector.Selection),
|
||||
Visible = _selector.HasSelection,
|
||||
Description = desc,
|
||||
TextColor = color,
|
||||
};
|
||||
}
|
||||
|
||||
private HeaderDrawer.Button ExportToClipboardButton()
|
||||
=> new()
|
||||
{
|
||||
Description =
|
||||
"Copy the current NPCs appearance to your clipboard.\nHold Control to disable applying of customizations for the copied design.\nHold Shift to disable applying of gear for the copied design.",
|
||||
Icon = FontAwesomeIcon.Copy,
|
||||
OnClick = ExportToClipboard,
|
||||
Visible = _selector.HasSelection,
|
||||
};
|
||||
|
||||
private HeaderDrawer.Button SaveAsDesignButton()
|
||||
=> new()
|
||||
{
|
||||
Description =
|
||||
"Save this NPCs appearance as a design.\nHold Control to disable applying of customizations for the saved design.\nHold Shift to disable applying of gear for the saved design.",
|
||||
Icon = FontAwesomeIcon.Save,
|
||||
OnClick = SaveDesignOpen,
|
||||
Visible = _selector.HasSelection,
|
||||
};
|
||||
|
||||
private void ExportToClipboard()
|
||||
{
|
||||
try
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var data = ToDesignData();
|
||||
var text = _converter.ShareBase64(data, applyGear, applyCustomize, applyCrest);
|
||||
ImGui.SetClipboardText(text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Messager.NotificationMessage(ex, $"Could not copy {_selector.Selection.Name}'s data to clipboard.",
|
||||
$"Could not copy data from NPC appearance {_selector.Selection.Kind} {_selector.Selection.Id.Id} to clipboard",
|
||||
NotificationType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveDesignOpen()
|
||||
{
|
||||
ImGui.OpenPopup("Save as Design");
|
||||
_newName = _selector.Selection.Name;
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
|
||||
var data = ToDesignData();
|
||||
_newDesign = _converter.Convert(data, applyGear, applyCustomize, applyCrest);
|
||||
}
|
||||
|
||||
private void SaveDesignDrawPopup()
|
||||
{
|
||||
if (!ImGuiUtil.OpenNameField("Save as Design", ref _newName))
|
||||
return;
|
||||
|
||||
if (_newDesign != null && _newName.Length > 0)
|
||||
_designManager.CreateClone(_newDesign, _newName, true);
|
||||
_newDesign = null;
|
||||
_newName = string.Empty;
|
||||
}
|
||||
|
||||
private void DrawPanel()
|
||||
{
|
||||
using var child = ImRaii.Child("##Panel", -Vector2.One, true);
|
||||
if (!child || !_selector.HasSelection)
|
||||
return;
|
||||
|
||||
DrawButtonRow();
|
||||
DrawCustomization();
|
||||
DrawEquipment();
|
||||
DrawAppearanceInfo();
|
||||
}
|
||||
|
||||
private void DrawButtonRow()
|
||||
{
|
||||
DrawApplyToSelf();
|
||||
ImGui.SameLine();
|
||||
DrawApplyToTarget();
|
||||
}
|
||||
|
||||
private void DrawCustomization()
|
||||
{
|
||||
if (!ImGui.CollapsingHeader("Customization"))
|
||||
return;
|
||||
|
||||
_customizeDrawer.Draw(_selector.Selection.Customize, true, true);
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
}
|
||||
|
||||
private void DrawEquipment()
|
||||
{
|
||||
if (!ImGui.CollapsingHeader("Equipment"))
|
||||
return;
|
||||
|
||||
_equipDrawer.Prepare();
|
||||
var designData = ToDesignData();
|
||||
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var data = new EquipDrawData(slot, designData) { Locked = true };
|
||||
_equipDrawer.DrawEquip(data);
|
||||
}
|
||||
|
||||
var mainhandData = new EquipDrawData(EquipSlot.MainHand, designData) { Locked = true };
|
||||
var offhandData = new EquipDrawData(EquipSlot.OffHand, designData) { Locked = true };
|
||||
_equipDrawer.DrawWeapons(mainhandData, offhandData, false);
|
||||
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
EquipmentDrawer.DrawMetaToggle(ToggleDrawData.FromValue(ActorState.MetaIndex.VisorState, _selector.Selection.VisorToggled));
|
||||
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
|
||||
}
|
||||
|
||||
private DesignData ToDesignData()
|
||||
{
|
||||
var selection = _selector.Selection;
|
||||
var items = _converter.FromDrawData(selection.Equip.ToArray(), selection.Mainhand, selection.Offhand, true).ToArray();
|
||||
var designData = new DesignData { Customize = selection.Customize };
|
||||
foreach (var (slot, item, stain) in items)
|
||||
{
|
||||
designData.SetItem(slot, item);
|
||||
designData.SetStain(slot, stain);
|
||||
}
|
||||
|
||||
return designData;
|
||||
}
|
||||
|
||||
private void DrawApplyToSelf()
|
||||
{
|
||||
var (id, data) = _objects.PlayerData;
|
||||
if (!ImGuiUtil.DrawDisabledButton("Apply to Yourself", Vector2.Zero,
|
||||
"Apply the current NPC appearance to your character.\nHold Control to only apply gear.\nHold Shift to only apply customizations.",
|
||||
!data.Valid))
|
||||
return;
|
||||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, applyCrest);
|
||||
_state.ApplyDesign(design, state, StateChanged.Source.Manual);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawApplyToTarget()
|
||||
{
|
||||
var (id, data) = _objects.TargetData;
|
||||
var tt = id.IsValid
|
||||
? data.Valid
|
||||
? "Apply the current NPC appearance to your current target.\nHold Control to only apply gear.\nHold Shift to only apply customizations."
|
||||
: "The current target can not be manipulated."
|
||||
: "No valid target selected.";
|
||||
if (!ImGuiUtil.DrawDisabledButton("Apply to Target", Vector2.Zero, tt, !data.Valid))
|
||||
return;
|
||||
|
||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||
{
|
||||
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
|
||||
var design = _converter.Convert(ToDesignData(), applyGear, applyCustomize, applyCrest);
|
||||
_state.ApplyDesign(design, state, StateChanged.Source.Manual);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DrawAppearanceInfo()
|
||||
{
|
||||
if (!ImGui.CollapsingHeader("Appearance Details"))
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table("Details", 2);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f));
|
||||
ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize("Last Update Datem").X);
|
||||
ImGui.TableSetupColumn("Data", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
var selection = _selector.Selection;
|
||||
CopyButton("NPC Name", selection.Name);
|
||||
CopyButton("NPC ID", selection.Id.Id.ToString());
|
||||
ImGuiUtil.DrawFrameColumn("NPC Type");
|
||||
ImGui.TableNextColumn();
|
||||
var width = ImGui.GetContentRegionAvail().X;
|
||||
ImGuiUtil.DrawTextButton(selection.Kind is ObjectKind.BattleNpc ? "Battle NPC" : "Event NPC", new Vector2(width, 0),
|
||||
ImGui.GetColorU32(ImGuiCol.FrameBg));
|
||||
|
||||
ImGuiUtil.DrawFrameColumn("Color");
|
||||
var color = _favorites.GetColor(selection);
|
||||
var colorName = color.Length == 0 ? DesignColors.AutomaticName : color;
|
||||
ImGui.TableNextColumn();
|
||||
if (_colorCombo.Draw("##colorCombo", colorName,
|
||||
"Associate a color with this NPC appearance. Right-Click to revert to automatic coloring.",
|
||||
width - ImGui.GetStyle().ItemSpacing.X - ImGui.GetFrameHeight(), ImGui.GetTextLineHeight())
|
||||
&& _colorCombo.CurrentSelection != null)
|
||||
{
|
||||
color = _colorCombo.CurrentSelection is DesignColors.AutomaticName ? string.Empty : _colorCombo.CurrentSelection;
|
||||
_favorites.SetColor(selection, color);
|
||||
}
|
||||
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
{
|
||||
_favorites.SetColor(selection, string.Empty);
|
||||
color = string.Empty;
|
||||
}
|
||||
|
||||
if (_colors.TryGetValue(color, out var currentColor))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
if (DesignColorUi.DrawColorButton($"Color associated with {color}", currentColor, out var newColor))
|
||||
_colors.SetColor(color, newColor);
|
||||
}
|
||||
else if (color.Length != 0)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
var size = new Vector2(ImGui.GetFrameHeight());
|
||||
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||
ImGuiUtil.DrawTextButton(FontAwesomeIcon.ExclamationCircle.ToIconString(), size, 0, _colors.MissingColor);
|
||||
ImGuiUtil.HoverTooltip("The color associated with this design does not exist.");
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
static void CopyButton(string label, string text)
|
||||
{
|
||||
ImGuiUtil.DrawFrameColumn(label);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button(text, new Vector2(ImGui.GetContentRegionAvail().X, 0)))
|
||||
ImGui.SetClipboardText(text);
|
||||
ImGuiUtil.HoverTooltip("Click to copy to clipboard.");
|
||||
}
|
||||
}
|
||||
}
|
||||
95
Glamourer/Gui/Tabs/NpcTab/NpcSelector.cs
Normal file
95
Glamourer/Gui/Tabs/NpcTab/NpcSelector.cs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Glamourer.GameData;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using ImGuiClip = OtterGui.ImGuiClip;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.NpcTab;
|
||||
|
||||
public class NpcSelector : IDisposable
|
||||
{
|
||||
private readonly NpcCustomizeSet _npcs;
|
||||
private readonly LocalNpcAppearanceData _favorites;
|
||||
|
||||
private NpcFilter _filter;
|
||||
private readonly List<int> _visibleOrdered = [];
|
||||
private int _selectedGlobalIndex;
|
||||
private bool _listDirty = true;
|
||||
private Vector2 _defaultItemSpacing;
|
||||
private float _width;
|
||||
|
||||
|
||||
public NpcSelector(NpcCustomizeSet npcs, LocalNpcAppearanceData favorites)
|
||||
{
|
||||
_npcs = npcs;
|
||||
_favorites = favorites;
|
||||
_filter = new NpcFilter(_favorites);
|
||||
_favorites.DataChanged += OnFavoriteChange;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_favorites.DataChanged -= OnFavoriteChange;
|
||||
}
|
||||
|
||||
private void OnFavoriteChange()
|
||||
=> _listDirty = true;
|
||||
|
||||
public void UpdateList()
|
||||
{
|
||||
if (!_listDirty)
|
||||
return;
|
||||
|
||||
_listDirty = false;
|
||||
_visibleOrdered.Clear();
|
||||
var enumerable = _npcs.WithIndex();
|
||||
if (!_filter.IsEmpty)
|
||||
enumerable = enumerable.Where(d => _filter.ApplyFilter(d.Value));
|
||||
var range = enumerable.OrderByDescending(d => _favorites.IsFavorite(d.Value))
|
||||
.ThenBy(d => d.Index)
|
||||
.Select(d => d.Index);
|
||||
_visibleOrdered.AddRange(range);
|
||||
}
|
||||
|
||||
public bool HasSelection
|
||||
=> _selectedGlobalIndex >= 0 && _selectedGlobalIndex < _npcs.Count;
|
||||
|
||||
public NpcData Selection
|
||||
=> HasSelection ? _npcs[_selectedGlobalIndex] : default;
|
||||
|
||||
public void Draw(float width)
|
||||
{
|
||||
_width = width;
|
||||
using var group = ImRaii.Group();
|
||||
_defaultItemSpacing = ImGui.GetStyle().ItemSpacing;
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FrameRounding, 0);
|
||||
|
||||
if (_filter.Draw(width))
|
||||
_listDirty = true;
|
||||
UpdateList();
|
||||
DrawSelector();
|
||||
}
|
||||
|
||||
private void DrawSelector()
|
||||
{
|
||||
using var child = ImRaii.Child("##Selector", new Vector2(_width, ImGui.GetContentRegionAvail().Y), true);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing);
|
||||
ImGuiClip.ClippedDraw(_visibleOrdered, DrawSelectable, ImGui.GetTextLineHeight());
|
||||
}
|
||||
|
||||
private void DrawSelectable(int globalIndex)
|
||||
{
|
||||
using var id = ImRaii.PushId(globalIndex);
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, _favorites.GetData(_npcs[globalIndex]).Color);
|
||||
if (ImGui.Selectable(_npcs[globalIndex].Name, _selectedGlobalIndex == globalIndex, ImGuiSelectableFlags.AllowItemOverlap))
|
||||
_selectedGlobalIndex = globalIndex;
|
||||
}
|
||||
}
|
||||
19
Glamourer/Gui/Tabs/NpcTab/NpcTab.cs
Normal file
19
Glamourer/Gui/Tabs/NpcTab/NpcTab.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using Dalamud.Interface.Utility;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Widgets;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.NpcTab;
|
||||
|
||||
public class NpcTab(NpcSelector _selector, NpcPanel _panel) : ITab
|
||||
{
|
||||
public ReadOnlySpan<byte> Label
|
||||
=> "NPCs"u8;
|
||||
|
||||
public void DrawContent()
|
||||
{
|
||||
_selector.Draw(200 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.SameLine();
|
||||
_panel.Draw();
|
||||
}
|
||||
}
|
||||
|
|
@ -100,4 +100,23 @@ public ref struct ToggleDrawData
|
|||
SetValue = setValue,
|
||||
};
|
||||
}
|
||||
|
||||
public static ToggleDrawData FromValue(ActorState.MetaIndex index, bool value)
|
||||
{
|
||||
var (label, tooltip) = index switch
|
||||
{
|
||||
ActorState.MetaIndex.HatState => ("Hat Visible", "Hide or show the characters head gear."),
|
||||
ActorState.MetaIndex.VisorState => ("Visor Toggled", "Toggle the visor state of the characters head gear."),
|
||||
ActorState.MetaIndex.WeaponState => ("Weapon Visible", "Hide or show the characters weapons when not drawn."),
|
||||
ActorState.MetaIndex.Wetness => ("Force Wetness", "Force the character to be wet or not."),
|
||||
_ => throw new Exception("Unsupported meta index."),
|
||||
};
|
||||
return new ToggleDrawData
|
||||
{
|
||||
Label = label,
|
||||
Tooltip = tooltip,
|
||||
Locked = true,
|
||||
CurrentValue = value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,9 +47,15 @@ public static class UiHelpers
|
|||
|
||||
public static bool DrawCheckbox(string label, string tooltip, bool value, out bool on, bool locked)
|
||||
{
|
||||
using var disabled = ImRaii.Disabled(locked);
|
||||
var ret = ImGuiUtil.Checkbox(label, string.Empty, value, v => value = v);
|
||||
ImGuiUtil.HoverTooltip(tooltip);
|
||||
bool ret;
|
||||
using (var disabled = ImRaii.Disabled(locked))
|
||||
{
|
||||
ret = ImGuiUtil.Checkbox("##" + label, string.Empty, value, v => value = v);
|
||||
}
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(label);
|
||||
ImGuiUtil.HoverTooltip(tooltip, ImGuiHoveredFlags.AllowWhenDisabled);
|
||||
on = value;
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -58,7 +64,6 @@ public static class UiHelpers
|
|||
out bool newApply, bool locked)
|
||||
{
|
||||
var flags = (sbyte)(currentApply ? currentValue ? 1 : -1 : 0);
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemInnerSpacing);
|
||||
using (_ = ImRaii.Disabled(locked))
|
||||
{
|
||||
if (new TristateCheckbox(ColorId.TriStateCross.Value(), ColorId.TriStateCheck.Value(), ColorId.TriStateNeutral.Value()).Draw(
|
||||
|
|
@ -80,7 +85,8 @@ public static class UiHelpers
|
|||
|
||||
ImGuiUtil.HoverTooltip($"This attribute will be {(currentApply ? currentValue ? "enabled." : "disabled." : "kept as is.")}");
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(label);
|
||||
|
||||
return (currentValue != newValue, currentApply != newApply);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ public class FilenameService
|
|||
public readonly string FavoriteFile;
|
||||
public readonly string DesignColorFile;
|
||||
public readonly string EphemeralConfigFile;
|
||||
public readonly string NpcAppearanceFile;
|
||||
|
||||
public FilenameService(DalamudPluginInterface pi)
|
||||
{
|
||||
|
|
@ -32,9 +33,9 @@ public class FilenameService
|
|||
FavoriteFile = Path.Combine(ConfigDirectory, "favorites.json");
|
||||
DesignColorFile = Path.Combine(ConfigDirectory, "design_colors.json");
|
||||
EphemeralConfigFile = Path.Combine(ConfigDirectory, "ephemeral_config.json");
|
||||
NpcAppearanceFile = Path.Combine(ConfigDirectory, "npc_appearance_data.json");
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<FileInfo> Designs()
|
||||
{
|
||||
if (!Directory.Exists(DesignDirectory))
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Glamourer.Gui.Tabs.ActorTab;
|
|||
using Glamourer.Gui.Tabs.AutomationTab;
|
||||
using Glamourer.Gui.Tabs.DebugTab;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
using Glamourer.Gui.Tabs.NpcTab;
|
||||
using Glamourer.Gui.Tabs.UnlocksTab;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
|
|
@ -131,6 +132,10 @@ public static class ServiceManagerA
|
|||
.AddSingleton<ActorTab>()
|
||||
.AddSingleton<ActorSelector>()
|
||||
.AddSingleton<ActorPanel>()
|
||||
.AddSingleton<NpcPanel>()
|
||||
.AddSingleton<NpcSelector>()
|
||||
.AddSingleton<LocalNpcAppearanceData>()
|
||||
.AddSingleton<NpcTab>()
|
||||
.AddSingleton<MainWindow>()
|
||||
.AddSingleton<GenericPopupWindow>()
|
||||
.AddSingleton<GlamourerWindowSystem>()
|
||||
|
|
|
|||
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit 15203edf1dba72713f508b798048c56ad969fb95
|
||||
Subproject commit e58c3c1240cda9d2d2b54f5ab7b8c729c1251fd4
|
||||
Loading…
Add table
Add a link
Reference in a new issue