mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
Fix equip changes not working on designs, rudimentary revert button for players, add beta options to NPC and Monsters
This commit is contained in:
parent
fb909aaf87
commit
31cf5dffcc
11 changed files with 486 additions and 59 deletions
|
|
@ -1,8 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Glamourer.Customization;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer
|
||||
|
|
@ -129,6 +132,99 @@ namespace Glamourer
|
|||
}
|
||||
}
|
||||
|
||||
private static Dictionary<EquipSlot, (int, int, bool)> Offsets()
|
||||
{
|
||||
var stainOffsetWeapon = (int) Marshal.OffsetOf<CharacterWeapon>("Stain");
|
||||
var stainOffsetEquip = (int) Marshal.OffsetOf<CharacterArmor>("Stain");
|
||||
|
||||
(int, int, bool) ToOffsets(IntPtr offset, bool weapon)
|
||||
{
|
||||
var off = 4 + CharacterCustomization.CustomizationBytes + (int) offset;
|
||||
return weapon ? (off, off + stainOffsetWeapon, weapon) : (off, off + stainOffsetEquip, weapon);
|
||||
}
|
||||
|
||||
return new Dictionary<EquipSlot, (int, int, bool)>(12)
|
||||
{
|
||||
[EquipSlot.MainHand] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("MainHand"), true),
|
||||
[EquipSlot.OffHand] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("OffHand"), true),
|
||||
[EquipSlot.Head] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Head"), false),
|
||||
[EquipSlot.Body] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Body"), false),
|
||||
[EquipSlot.Hands] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Hands"), false),
|
||||
[EquipSlot.Legs] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Legs"), false),
|
||||
[EquipSlot.Feet] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Feet"), false),
|
||||
[EquipSlot.Ears] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Ears"), false),
|
||||
[EquipSlot.Neck] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Neck"), false),
|
||||
[EquipSlot.Wrists] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("Wrists"), false),
|
||||
[EquipSlot.RFinger] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("RFinger"), false),
|
||||
[EquipSlot.LFinger] = ToOffsets(Marshal.OffsetOf<CharacterEquipment>("LFinger"), false),
|
||||
};
|
||||
}
|
||||
|
||||
private static readonly IReadOnlyDictionary<EquipSlot, (int, int, bool)> FieldOffsets = Offsets();
|
||||
|
||||
public bool WriteStain(EquipSlot slot, StainId stainId)
|
||||
{
|
||||
if (WriteProtected)
|
||||
return false;
|
||||
|
||||
var (_, stainOffset, _) = FieldOffsets[slot];
|
||||
if (_bytes[stainOffset] == (byte) stainId)
|
||||
return false;
|
||||
|
||||
_bytes[stainOffset] = stainId.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool WriteItem(int offset, SetId id, WeaponType type, ushort variant, bool weapon)
|
||||
{
|
||||
var idBytes = BitConverter.GetBytes(id.Value);
|
||||
|
||||
static bool WriteIfDifferent(ref byte x, byte y)
|
||||
{
|
||||
if (x == y)
|
||||
return false;
|
||||
|
||||
x = y;
|
||||
return true;
|
||||
}
|
||||
|
||||
var ret = WriteIfDifferent(ref _bytes[offset], idBytes[0]);
|
||||
ret |= WriteIfDifferent(ref _bytes[offset + 1], idBytes[1]);
|
||||
if (weapon)
|
||||
{
|
||||
var typeBytes = BitConverter.GetBytes(type.Value);
|
||||
var variantBytes = BitConverter.GetBytes(variant);
|
||||
ret |= WriteIfDifferent(ref _bytes[offset + 2], typeBytes[0]);
|
||||
ret |= WriteIfDifferent(ref _bytes[offset + 3], typeBytes[1]);
|
||||
ret |= WriteIfDifferent(ref _bytes[offset + 4], variantBytes[0]);
|
||||
ret |= WriteIfDifferent(ref _bytes[offset + 5], variantBytes[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret |= WriteIfDifferent(ref _bytes[offset + 2], (byte) variant);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool WriteItem(Item item)
|
||||
{
|
||||
if (WriteProtected)
|
||||
return false;
|
||||
|
||||
var (itemOffset, _, isWeapon) = FieldOffsets[item.EquippableTo];
|
||||
var (id, type, variant) = item.MainModel;
|
||||
var ret = WriteItem(itemOffset, id, type, variant, isWeapon);
|
||||
if (item.EquippableTo == EquipSlot.MainHand && item.HasSubModel)
|
||||
{
|
||||
var (subOffset, _, _) = FieldOffsets[EquipSlot.OffHand];
|
||||
var (subId, subType, subVariant) = item.SubModel;
|
||||
ret |= WriteItem(subOffset, subId, subType, subVariant, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public unsafe float Alpha
|
||||
{
|
||||
get
|
||||
|
|
@ -204,6 +300,8 @@ namespace Glamourer
|
|||
|
||||
public void Apply(Character a)
|
||||
{
|
||||
Glamourer.RevertableDesigns.Add(a);
|
||||
|
||||
if (WriteCustomizations)
|
||||
Customizations.Write(a.Address);
|
||||
if (WriteEquipment != CharacterEquipMask.None)
|
||||
|
|
|
|||
31
Glamourer/Designs/RevertableDesigns.cs
Normal file
31
Glamourer/Designs/RevertableDesigns.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System.Collections.Generic;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
|
||||
namespace Glamourer.Designs
|
||||
{
|
||||
public class RevertableDesigns
|
||||
{
|
||||
public readonly Dictionary<string, CharacterSave> Saves = new();
|
||||
|
||||
public bool Add(Character actor)
|
||||
{
|
||||
var name = actor.Name.ToString();
|
||||
if (Saves.TryGetValue(name, out var save))
|
||||
return false;
|
||||
|
||||
save = new CharacterSave();
|
||||
save.LoadCharacter(actor);
|
||||
Saves[name] = save;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Revert(Character actor)
|
||||
{
|
||||
if (!Saves.TryGetValue(actor.Name.ToString(), out var save))
|
||||
return false;
|
||||
|
||||
save.Apply(actor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ namespace Glamourer
|
|||
private readonly Interface _interface;
|
||||
public readonly DesignManager Designs;
|
||||
public readonly FixedDesigns FixedDesigns;
|
||||
public static RevertableDesigns RevertableDesigns = new();
|
||||
|
||||
|
||||
public static string Version = string.Empty;
|
||||
|
|
@ -41,7 +42,7 @@ namespace Glamourer
|
|||
Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
|
||||
PlayerWatcher = PlayerWatchFactory.Create(Dalamud.Framework, Dalamud.ClientState, Dalamud.Objects);
|
||||
FixedDesigns = new FixedDesigns(Designs);
|
||||
|
||||
|
||||
if (Config.ApplyFixedDesigns)
|
||||
PlayerWatcher.Enable();
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Glamourer.Designs;
|
||||
using ImGuiNET;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
|
|
@ -13,11 +16,12 @@ namespace Glamourer.Gui
|
|||
{
|
||||
public const float SelectorWidth = 200;
|
||||
public const float MinWindowWidth = 675;
|
||||
public const int GPoseObjectId = 201;
|
||||
public const int GPoseObjectId = 201;
|
||||
private const string PluginName = "Glamourer";
|
||||
private readonly string _glamourerHeader;
|
||||
|
||||
private readonly IReadOnlyDictionary<byte, Stain> _stains;
|
||||
private readonly IReadOnlyDictionary<uint, ModelChara> _models;
|
||||
private readonly IObjectIdentifier _identifier;
|
||||
private readonly Dictionary<EquipSlot, (ComboWithFilter<Item>, ComboWithFilter<Stain>)> _combos;
|
||||
private readonly ImGuiScene.TextureWrap? _legacyTattooIcon;
|
||||
|
|
@ -39,11 +43,18 @@ namespace Glamourer.Gui
|
|||
Dalamud.PluginInterface.UiBuilder.Draw += Draw;
|
||||
Dalamud.PluginInterface.UiBuilder.OpenConfigUi += ToggleVisibility;
|
||||
|
||||
_characterConstructor = typeof(Character).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[]
|
||||
{
|
||||
typeof(IntPtr),
|
||||
}, null)!;
|
||||
|
||||
_equipSlotNames = GetEquipSlotNames();
|
||||
|
||||
_stains = GameData.Stains(Dalamud.GameData);
|
||||
_models = GameData.Models(Dalamud.GameData);
|
||||
_identifier = Penumbra.GameData.GameData.GetIdentifier(Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
|
||||
|
||||
|
||||
var stainCombo = CreateDefaultStainCombo(_stains.Values.ToArray());
|
||||
|
||||
var equip = GameData.ItemsBySlot(Dalamud.GameData);
|
||||
|
|
@ -93,6 +104,7 @@ namespace Glamourer.Gui
|
|||
DrawSaves();
|
||||
DrawFixedDesignsTab();
|
||||
DrawConfigTab();
|
||||
DrawRevertablesTab();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Logging;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.FileSystem;
|
||||
using ImGuiNET;
|
||||
|
|
@ -12,12 +16,13 @@ namespace Glamourer.Gui
|
|||
{
|
||||
internal partial class Interface
|
||||
{
|
||||
private readonly CharacterSave _currentSave = new();
|
||||
private string _newDesignName = string.Empty;
|
||||
private bool _keyboardFocus;
|
||||
private const string DesignNamePopupLabel = "Save Design As...";
|
||||
private const uint RedHeaderColor = 0xFF1818C0;
|
||||
private const uint GreenHeaderColor = 0xFF18C018;
|
||||
private readonly CharacterSave _currentSave = new();
|
||||
private string _newDesignName = string.Empty;
|
||||
private bool _keyboardFocus;
|
||||
private const string DesignNamePopupLabel = "Save Design As...";
|
||||
private const uint RedHeaderColor = 0xFF1818C0;
|
||||
private const uint GreenHeaderColor = 0xFF18C018;
|
||||
private readonly ConstructorInfo _characterConstructor;
|
||||
|
||||
private void DrawPlayerHeader()
|
||||
{
|
||||
|
|
@ -30,7 +35,7 @@ namespace Glamourer.Gui
|
|||
.PushColor(ImGuiCol.ButtonActive, buttonColor)
|
||||
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
|
||||
ImGui.Button($"{_currentPlayerName}##playerHeader", -Vector2.UnitX * 0.0001f);
|
||||
ImGui.Button($"{_currentLabel}##playerHeader", -Vector2.UnitX * 0.0001f);
|
||||
}
|
||||
|
||||
private static void DrawCopyClipboardButton(CharacterSave save)
|
||||
|
|
@ -109,22 +114,81 @@ namespace Glamourer.Gui
|
|||
Glamourer.Penumbra.UpdateCharacters(player, fallback);
|
||||
}
|
||||
|
||||
private const int ModelTypeOffset = 0x01B4;
|
||||
|
||||
private static unsafe int ModelType(GameObject actor)
|
||||
=> *(int*) (actor.Address + ModelTypeOffset);
|
||||
|
||||
private static unsafe void SetModelType(GameObject actor, int value)
|
||||
=> *(int*) (actor.Address + ModelTypeOffset) = value;
|
||||
|
||||
private Character Character(IntPtr address)
|
||||
=> (Character) _characterConstructor.Invoke(new object[]
|
||||
{
|
||||
address,
|
||||
});
|
||||
|
||||
private Character? CreateCharacter(GameObject? actor)
|
||||
{
|
||||
if (actor == null)
|
||||
return null;
|
||||
|
||||
return actor switch
|
||||
{
|
||||
PlayerCharacter p => p,
|
||||
BattleChara b => b,
|
||||
_ => actor.ObjectKind switch
|
||||
{
|
||||
ObjectKind.BattleNpc => Character(actor.Address),
|
||||
ObjectKind.Companion => Character(actor.Address),
|
||||
ObjectKind.EventNpc => Character(actor.Address),
|
||||
_ => null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private static Character? TransformToCustomizable(Character? actor)
|
||||
{
|
||||
if (actor == null)
|
||||
return null;
|
||||
|
||||
if (ModelType(actor) == 0)
|
||||
return actor;
|
||||
|
||||
SetModelType(actor, 0);
|
||||
CharacterCustomization.Default.Write(actor.Address);
|
||||
return actor;
|
||||
}
|
||||
|
||||
private void DrawApplyToTargetButton(CharacterSave save)
|
||||
{
|
||||
if (!ImGui.Button("Apply to Target"))
|
||||
return;
|
||||
|
||||
var player = Dalamud.Targets.Target as Character;
|
||||
var player = TransformToCustomizable(CreateCharacter(Dalamud.Targets.Target));
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
var fallBackCharacter = _playerNames[player.Name.ToString()];
|
||||
var fallBackCharacter = _gPoseActors.TryGetValue(player.Name.ToString(), out var f) ? f : null;
|
||||
save.Apply(player);
|
||||
if (fallBackCharacter != null)
|
||||
save.Apply(fallBackCharacter);
|
||||
Glamourer.Penumbra.UpdateCharacters(player, fallBackCharacter);
|
||||
}
|
||||
|
||||
private void DrawRevertButton()
|
||||
{
|
||||
if (!DrawDisableButton("Revert", _player == null))
|
||||
return;
|
||||
|
||||
Glamourer.RevertableDesigns.Revert(_player!);
|
||||
var fallBackCharacter = _gPoseActors.TryGetValue(_player!.Name.ToString(), out var f) ? f : null;
|
||||
if (fallBackCharacter != null)
|
||||
Glamourer.RevertableDesigns.Revert(fallBackCharacter);
|
||||
Glamourer.Penumbra.UpdateCharacters(_player, fallBackCharacter);
|
||||
}
|
||||
|
||||
private void SaveNewDesign(CharacterSave save)
|
||||
{
|
||||
try
|
||||
|
|
@ -144,13 +208,42 @@ namespace Glamourer.Gui
|
|||
}
|
||||
}
|
||||
|
||||
private void DrawPlayerPanel()
|
||||
private void DrawMonsterPanel()
|
||||
{
|
||||
ImGui.BeginGroup();
|
||||
DrawPlayerHeader();
|
||||
if (!ImGui.BeginChild("##playerData", -Vector2.One, true))
|
||||
if (DrawApplyClipboardButton())
|
||||
Glamourer.Penumbra.UpdateCharacters(_player!);
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Convert to Character"))
|
||||
{
|
||||
TransformToCustomizable(_player);
|
||||
_currentLabel = _currentLabel.Replace("(Monster)", "(NPC)");
|
||||
Glamourer.Penumbra.UpdateCharacters(_player!);
|
||||
}
|
||||
|
||||
if (!_inGPose)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
DrawTargetPlayerButton();
|
||||
}
|
||||
|
||||
var currentModel = ModelType(_player!);
|
||||
using var raii = new ImGuiRaii();
|
||||
if (!raii.Begin(() => ImGui.BeginCombo("Model Id", currentModel.ToString()), ImGui.EndCombo))
|
||||
return;
|
||||
|
||||
foreach (var (id, _) in _models.Skip(1))
|
||||
{
|
||||
if (!ImGui.Selectable($"{id:D6}##models", id == currentModel) || id == currentModel)
|
||||
continue;
|
||||
|
||||
SetModelType(_player!, (int) id);
|
||||
Glamourer.Penumbra.UpdateCharacters(_player!);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPlayerPanel()
|
||||
{
|
||||
DrawCopyClipboardButton(_currentSave);
|
||||
ImGui.SameLine();
|
||||
var changes = DrawApplyClipboardButton();
|
||||
|
|
@ -169,9 +262,12 @@ namespace Glamourer.Gui
|
|||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
DrawRevertButton();
|
||||
|
||||
if (DrawCustomization(ref _currentSave.Customizations) && _player != null)
|
||||
{
|
||||
Glamourer.RevertableDesigns.Add(_player);
|
||||
_currentSave.Customizations.Write(_player.Address);
|
||||
changes = true;
|
||||
}
|
||||
|
|
@ -181,8 +277,24 @@ namespace Glamourer.Gui
|
|||
|
||||
if (_player != null && changes)
|
||||
Glamourer.Penumbra.UpdateCharacters(_player);
|
||||
}
|
||||
|
||||
private void DrawActorPanel()
|
||||
{
|
||||
using var raii = ImGuiRaii.NewGroup();
|
||||
DrawPlayerHeader();
|
||||
if (!ImGui.BeginChild("##playerData", -Vector2.One, true))
|
||||
{
|
||||
ImGui.EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_player == null || ModelType(_player) == 0)
|
||||
DrawPlayerPanel();
|
||||
else
|
||||
DrawMonsterPanel();
|
||||
|
||||
ImGui.EndChild();
|
||||
ImGui.EndGroup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ namespace Glamourer.Gui
|
|||
{
|
||||
internal partial class Interface
|
||||
{
|
||||
private Character? _player;
|
||||
private string _currentPlayerName = string.Empty;
|
||||
private string _playerFilter = string.Empty;
|
||||
private string _playerFilterLower = string.Empty;
|
||||
private readonly Dictionary<string, Character?> _playerNames = new(400);
|
||||
private Character? _player;
|
||||
private string _currentLabel = string.Empty;
|
||||
private string _playerFilter = string.Empty;
|
||||
private string _playerFilterLower = string.Empty;
|
||||
private readonly Dictionary<string, int> _playerNames = new(100);
|
||||
private readonly Dictionary<string, Character?> _gPoseActors = new(48);
|
||||
|
||||
private void DrawPlayerFilter()
|
||||
{
|
||||
|
|
@ -26,35 +27,66 @@ namespace Glamourer.Gui
|
|||
_playerFilterLower = _playerFilter.ToLowerInvariant();
|
||||
}
|
||||
|
||||
private void DrawPlayerSelectable(Character player, bool gPose)
|
||||
private void DrawGPoseSelectable(Character player)
|
||||
{
|
||||
var playerName = player.Name.ToString();
|
||||
if (!playerName.Any())
|
||||
return;
|
||||
|
||||
if (_playerNames.ContainsKey(playerName))
|
||||
_gPoseActors[playerName] = null;
|
||||
|
||||
DrawSelectable(player, $"{playerName} (GPose)");
|
||||
}
|
||||
|
||||
private static string GetLabel(Character player, string playerName, int num)
|
||||
{
|
||||
if (player.ObjectKind == ObjectKind.Player)
|
||||
return num == 1 ? playerName : $"{playerName} #{num}";
|
||||
|
||||
if (ModelType(player) == 0)
|
||||
return num == 1 ? $"{playerName} (NPC)" : $"{playerName} #{num} (NPC)";
|
||||
|
||||
return num == 1 ? $"{playerName} (Monster)" : $"{playerName} #{num} (Monster)";
|
||||
}
|
||||
|
||||
private void DrawPlayerSelectable(Character player)
|
||||
{
|
||||
var playerName = player.Name.ToString();
|
||||
if (!playerName.Any())
|
||||
return;
|
||||
|
||||
if (_playerNames.TryGetValue(playerName, out var num))
|
||||
_playerNames[playerName] = ++num;
|
||||
else
|
||||
_playerNames[playerName] = num = 1;
|
||||
|
||||
if (_gPoseActors.ContainsKey(playerName))
|
||||
{
|
||||
_playerNames[playerName] = player;
|
||||
_gPoseActors[playerName] = player;
|
||||
return;
|
||||
}
|
||||
|
||||
_playerNames.Add(playerName, null);
|
||||
var label = GetLabel(player, playerName, num);
|
||||
DrawSelectable(player, label);
|
||||
}
|
||||
|
||||
var label = gPose ? $"{playerName} (GPose)" : playerName;
|
||||
if (!_playerFilterLower.Any() || playerName.ToLowerInvariant().Contains(_playerFilterLower))
|
||||
if (ImGui.Selectable(label, _currentPlayerName == playerName))
|
||||
|
||||
private void DrawSelectable(Character player, string label)
|
||||
{
|
||||
if (!_playerFilterLower.Any() || label.ToLowerInvariant().Contains(_playerFilterLower))
|
||||
if (ImGui.Selectable(label, _currentLabel == label))
|
||||
{
|
||||
_currentPlayerName = playerName;
|
||||
_currentLabel = label;
|
||||
_currentSave.LoadCharacter(player);
|
||||
_player = player;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentPlayerName == playerName)
|
||||
{
|
||||
_currentSave.LoadCharacter(player);
|
||||
_player = player;
|
||||
}
|
||||
if (_currentLabel != label)
|
||||
return;
|
||||
|
||||
_currentSave.LoadCharacter(player);
|
||||
_player = player;
|
||||
}
|
||||
|
||||
private void DrawSelectionButtons()
|
||||
|
|
@ -64,7 +96,7 @@ namespace Glamourer.Gui
|
|||
.PushStyle(ImGuiStyleVar.FrameRounding, 0)
|
||||
.PushFont(UiBuilder.IconFont);
|
||||
Character? select = null;
|
||||
var buttonWidth = Vector2.UnitX * SelectorWidth / 2;
|
||||
var buttonWidth = Vector2.UnitX * SelectorWidth / 2;
|
||||
if (ImGui.Button(FontAwesomeIcon.UserCircle.ToIconString(), buttonWidth))
|
||||
select = Dalamud.ClientState.LocalPlayer;
|
||||
raii.PopFonts();
|
||||
|
|
@ -81,18 +113,18 @@ namespace Glamourer.Gui
|
|||
else
|
||||
{
|
||||
if (ImGui.Button(FontAwesomeIcon.HandPointer.ToIconString(), buttonWidth))
|
||||
select = Dalamud.Targets.Target as Character;
|
||||
select = CreateCharacter(Dalamud.Targets.Target);
|
||||
}
|
||||
|
||||
raii.PopFonts();
|
||||
if (ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Select the current target, if it is a player object.");
|
||||
ImGui.SetTooltip("Select the current target, if it is in the list.");
|
||||
|
||||
if (select == null || select.ObjectKind != ObjectKind.Player)
|
||||
if (select == null)
|
||||
return;
|
||||
|
||||
_player = select;
|
||||
_currentPlayerName = _player.Name.ToString();
|
||||
_player = select;
|
||||
_currentLabel = _player.Name.ToString();
|
||||
_currentSave.LoadCharacter(_player);
|
||||
}
|
||||
|
||||
|
|
@ -102,24 +134,35 @@ namespace Glamourer.Gui
|
|||
DrawPlayerFilter();
|
||||
if (!ImGui.BeginChild("##playerSelector",
|
||||
new Vector2(SelectorWidth * ImGui.GetIO().FontGlobalScale, -ImGui.GetFrameHeight() - 1), true))
|
||||
{
|
||||
ImGui.EndChild();
|
||||
ImGui.EndGroup();
|
||||
return;
|
||||
}
|
||||
|
||||
_playerNames.Clear();
|
||||
_gPoseActors.Clear();
|
||||
for (var i = GPoseObjectId; i < GPoseObjectId + 48; ++i)
|
||||
{
|
||||
var player = Dalamud.Objects[i] as Character;
|
||||
var player = CreateCharacter(Dalamud.Objects[i]);
|
||||
if (player == null)
|
||||
break;
|
||||
|
||||
if (player.ObjectKind == ObjectKind.Player)
|
||||
DrawPlayerSelectable(player, true);
|
||||
DrawGPoseSelectable(player);
|
||||
}
|
||||
|
||||
for (var i = 0; i < GPoseObjectId; i += 2)
|
||||
for (var i = 0; i < GPoseObjectId; ++i)
|
||||
{
|
||||
var player = Dalamud.Objects[i] as Character;
|
||||
if (player != null && player.ObjectKind == ObjectKind.Player)
|
||||
DrawPlayerSelectable(player, false);
|
||||
var player = CreateCharacter(Dalamud.Objects[i])!;
|
||||
if (player != null)
|
||||
DrawPlayerSelectable(player);
|
||||
}
|
||||
|
||||
for (var i = GPoseObjectId + 48; i < Dalamud.Objects.Length; ++i)
|
||||
{
|
||||
var player = CreateCharacter(Dalamud.Objects[i])!;
|
||||
if (player != null)
|
||||
DrawPlayerSelectable(player);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -135,17 +178,17 @@ namespace Glamourer.Gui
|
|||
private void DrawPlayerTab()
|
||||
{
|
||||
using var raii = new ImGuiRaii();
|
||||
_player = null;
|
||||
if (!raii.Begin(() => ImGui.BeginTabItem("Current Players"), ImGui.EndTabItem))
|
||||
return;
|
||||
|
||||
_player = null;
|
||||
DrawPlayerSelector();
|
||||
|
||||
if (!_currentPlayerName.Any())
|
||||
if (!_currentLabel.Any())
|
||||
return;
|
||||
|
||||
ImGui.SameLine();
|
||||
DrawPlayerPanel();
|
||||
DrawActorPanel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace Glamourer.Gui
|
|||
{
|
||||
private int _totalObject;
|
||||
|
||||
private bool _inDesignMode;
|
||||
private Design? _selection;
|
||||
private string _newChildName = string.Empty;
|
||||
|
||||
|
|
@ -207,7 +208,8 @@ namespace Glamourer.Gui
|
|||
{
|
||||
using var raii = new ImGuiRaii();
|
||||
raii.PushStyle(ImGuiStyleVar.IndentSpacing, 12.5f * ImGui.GetIO().FontGlobalScale);
|
||||
if (!raii.Begin(() => ImGui.BeginTabItem("Saves"), ImGui.EndTabItem))
|
||||
_inDesignMode = raii.Begin(() => ImGui.BeginTabItem("Designs"), ImGui.EndTabItem);
|
||||
if (!_inDesignMode)
|
||||
return;
|
||||
|
||||
DrawDesignSelector();
|
||||
|
|
@ -278,7 +280,7 @@ namespace Glamourer.Gui
|
|||
|
||||
private void ContextMenu(IFileSystemBase child)
|
||||
{
|
||||
var label = $"##fsPopup{child.FullName()}";
|
||||
var label = $"##fsPopup{child.FullName()}";
|
||||
if (ImGui.BeginPopup(label))
|
||||
{
|
||||
if (ImGui.MenuItem("Delete"))
|
||||
|
|
|
|||
|
|
@ -15,10 +15,17 @@ namespace Glamourer.Gui
|
|||
stainCombo.PostPreview = () => ImGui.PopStyleColor(previewPush);
|
||||
}
|
||||
|
||||
if (stainCombo.Draw(string.Empty, out var newStain) && _player != null && !newStain.RowIndex.Equals(stainIdx))
|
||||
if (stainCombo.Draw(string.Empty, out var newStain) && !newStain.RowIndex.Equals(stainIdx))
|
||||
{
|
||||
newStain.Write(_player.Address, slot);
|
||||
return true;
|
||||
if (_player != null)
|
||||
{
|
||||
Glamourer.RevertableDesigns.Add(_player);
|
||||
newStain.Write(_player.Address, slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_inDesignMode && (_selection?.Data.WriteStain(slot, newStain.RowIndex) ?? false))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -27,10 +34,17 @@ namespace Glamourer.Gui
|
|||
private bool DrawItemSelector(ComboWithFilter<Item> equipCombo, Lumina.Excel.GeneratedSheets.Item? item)
|
||||
{
|
||||
var currentName = item?.Name.ToString() ?? "Nothing";
|
||||
if (equipCombo.Draw(currentName, out var newItem, _itemComboWidth) && _player != null && newItem.Base.RowId != item?.RowId)
|
||||
if (equipCombo.Draw(currentName, out var newItem, _itemComboWidth) && newItem.Base.RowId != item?.RowId)
|
||||
{
|
||||
newItem.Write(_player.Address);
|
||||
return true;
|
||||
if (_player != null)
|
||||
{
|
||||
Glamourer.RevertableDesigns.Add(_player);
|
||||
newItem.Write(_player.Address);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_inDesignMode && (_selection?.Data.WriteItem(newItem) ?? false))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,17 @@ namespace Glamourer.Gui
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool DrawDisableButton(string label, bool disabled)
|
||||
{
|
||||
if (!disabled)
|
||||
return ImGui.Button(label);
|
||||
|
||||
using var raii = new ImGuiRaii();
|
||||
raii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
|
||||
ImGui.Button(label);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool DrawMiscellaneous(CharacterSave save, Character? player)
|
||||
{
|
||||
var ret = false;
|
||||
|
|
|
|||
88
Glamourer/Gui/InterfaceRevertables.cs
Normal file
88
Glamourer/Gui/InterfaceRevertables.cs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Glamourer.Gui
|
||||
{
|
||||
internal partial class Interface
|
||||
{
|
||||
private string? _currentRevertableName;
|
||||
private CharacterSave? _currentRevertable;
|
||||
|
||||
private void DrawRevertablesSelector()
|
||||
{
|
||||
ImGui.BeginGroup();
|
||||
DrawPlayerFilter();
|
||||
if (!ImGui.BeginChild("##playerSelector",
|
||||
new Vector2(SelectorWidth * ImGui.GetIO().FontGlobalScale, -ImGui.GetFrameHeight() - 1), true))
|
||||
{
|
||||
ImGui.EndChild();
|
||||
ImGui.EndGroup();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (name, save) in Glamourer.RevertableDesigns.Saves)
|
||||
{
|
||||
if (name.ToLowerInvariant().Contains(_playerFilterLower) && ImGui.Selectable(name, name == _currentRevertableName))
|
||||
{
|
||||
_currentRevertableName = name;
|
||||
_currentRevertable = save;
|
||||
}
|
||||
}
|
||||
|
||||
using (var _ = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
|
||||
{
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
DrawSelectionButtons();
|
||||
ImGui.EndGroup();
|
||||
}
|
||||
|
||||
private void DrawRevertablePanel()
|
||||
{
|
||||
using var group = ImGuiRaii.NewGroup();
|
||||
{
|
||||
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
|
||||
using var raii = new ImGuiRaii()
|
||||
.PushColor(ImGuiCol.Text, GreenHeaderColor)
|
||||
.PushColor(ImGuiCol.Button, buttonColor)
|
||||
.PushColor(ImGuiCol.ButtonHovered, buttonColor)
|
||||
.PushColor(ImGuiCol.ButtonActive, buttonColor)
|
||||
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
|
||||
ImGui.Button($"{_currentRevertableName}##playerHeader", -Vector2.UnitX * 0.0001f);
|
||||
}
|
||||
|
||||
if (!ImGui.BeginChild("##revertableData", -Vector2.One, true))
|
||||
{
|
||||
ImGui.EndChild();
|
||||
return;
|
||||
}
|
||||
|
||||
var save = _currentRevertable!.Copy();
|
||||
DrawCustomization(ref save.Customizations);
|
||||
DrawEquip(save.Equipment);
|
||||
DrawMiscellaneous(save, null);
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private void DrawRevertablesTab()
|
||||
{
|
||||
using var raii = new ImGuiRaii();
|
||||
if (!raii.Begin(() => ImGui.BeginTabItem("Revertables"), ImGui.EndTabItem))
|
||||
return;
|
||||
|
||||
DrawRevertablesSelector();
|
||||
|
||||
if (_currentRevertableName == null)
|
||||
return;
|
||||
|
||||
ImGui.SameLine();
|
||||
DrawRevertablePanel();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue