mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-21 16:09:28 +01:00
Remove GlamourerOld.
This commit is contained in:
parent
d621369094
commit
c505286220
75 changed files with 384 additions and 8734 deletions
|
|
@ -25,6 +25,14 @@ public class AutoDesign
|
|||
public JobGroup Jobs;
|
||||
public Type ApplicationType;
|
||||
|
||||
public AutoDesign Clone()
|
||||
=> new()
|
||||
{
|
||||
Design = Design,
|
||||
ApplicationType = ApplicationType,
|
||||
Jobs = Jobs,
|
||||
};
|
||||
|
||||
public unsafe bool IsActive(Actor actor)
|
||||
=> actor.IsCharacter && Jobs.Fits(actor.AsCharacter->CharacterData.ClassJob);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.AccessControl;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
|
|
@ -8,6 +9,8 @@ using Glamourer.Interop.Structs;
|
|||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Structs;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
|
|
@ -46,13 +49,19 @@ public class AutoDesignApplier : IDisposable
|
|||
if (!_config.EnableAutoDesigns || !actor.Identifier(_actors.AwaitedService, out var id))
|
||||
return;
|
||||
|
||||
if (!_manager.EnabledSets.TryGetValue(id, out var set))
|
||||
if (!GetPlayerSet(id, out var set))
|
||||
{
|
||||
if (_state.TryGetValue(id, out var s))
|
||||
s.LastJob = actor.Job;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_state.GetOrCreate(id, actor, out var state))
|
||||
return;
|
||||
|
||||
Reduce(actor, state, set);
|
||||
var sameJob = state.LastJob == actor.Job;
|
||||
state.LastJob = actor.Job;
|
||||
Reduce(actor, state, set, sameJob);
|
||||
_state.ReapplyState(actor);
|
||||
}
|
||||
|
||||
|
|
@ -63,14 +72,14 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
if (!GetPlayerSet(identifier, out var set))
|
||||
return;
|
||||
Reduce(actor, state, set);
|
||||
|
||||
Reduce(actor, state, set, true);
|
||||
}
|
||||
|
||||
private unsafe void Reduce(Actor actor, ActorState state, AutoDesignSet set)
|
||||
private unsafe void Reduce(Actor actor, ActorState state, AutoDesignSet set, bool respectManual)
|
||||
{
|
||||
EquipFlag totalEquipFlags = 0;
|
||||
//var totalCustomizeFlags = _phrasing.Phrasing2 ? 0 : CustomizeFlagExtensions.RedrawRequired;
|
||||
var totalCustomizeFlags = CustomizeFlagExtensions.RedrawRequired;
|
||||
var totalCustomizeFlags = _phrasing.Phrasing2 ? 0 : CustomizeFlagExtensions.RedrawRequired;
|
||||
byte totalMetaFlags = 0;
|
||||
foreach (var design in set.Designs)
|
||||
{
|
||||
|
|
@ -84,9 +93,9 @@ public class AutoDesignApplier : IDisposable
|
|||
continue;
|
||||
|
||||
var (equipFlags, customizeFlags, applyHat, applyVisor, applyWeapon, applyWet) = design.ApplyWhat();
|
||||
Reduce(state, in design.Design.DesignData, equipFlags, ref totalEquipFlags);
|
||||
Reduce(state, in design.Design.DesignData, customizeFlags, ref totalCustomizeFlags);
|
||||
Reduce(state, in design.Design.DesignData, applyHat, applyVisor, applyWeapon, applyWet, ref totalMetaFlags);
|
||||
Reduce(state, in design.Design.DesignData, equipFlags, ref totalEquipFlags, respectManual);
|
||||
Reduce(state, in design.Design.DesignData, customizeFlags, ref totalCustomizeFlags, respectManual);
|
||||
Reduce(state, in design.Design.DesignData, applyHat, applyVisor, applyWeapon, applyWet, ref totalMetaFlags, respectManual);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +112,7 @@ public class AutoDesignApplier : IDisposable
|
|||
return _manager.EnabledSets.TryGetValue(identifier, out set);
|
||||
}
|
||||
|
||||
private void Reduce(ActorState state, in DesignData design, EquipFlag equipFlags, ref EquipFlag totalEquipFlags)
|
||||
private void Reduce(ActorState state, in DesignData design, EquipFlag equipFlags, ref EquipFlag totalEquipFlags, bool respectManual)
|
||||
{
|
||||
equipFlags &= ~totalEquipFlags;
|
||||
if (equipFlags == 0)
|
||||
|
|
@ -115,6 +124,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var flag = slot.ToFlag();
|
||||
if (equipFlags.HasFlag(flag))
|
||||
{
|
||||
if (!respectManual || state[slot, false] is not StateChanged.Source.Manual)
|
||||
_state.ChangeItem(state, slot, design.Item(slot), StateChanged.Source.Fixed);
|
||||
totalEquipFlags |= flag;
|
||||
}
|
||||
|
|
@ -122,6 +132,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var stainFlag = slot.ToStainFlag();
|
||||
if (equipFlags.HasFlag(stainFlag))
|
||||
{
|
||||
if (!respectManual || state[slot, true] is not StateChanged.Source.Manual)
|
||||
_state.ChangeStain(state, slot, design.Stain(slot), StateChanged.Source.Fixed);
|
||||
totalEquipFlags |= stainFlag;
|
||||
}
|
||||
|
|
@ -132,6 +143,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var item = design.Item(EquipSlot.MainHand);
|
||||
if (state.ModelData.Item(EquipSlot.MainHand).Type == item.Type)
|
||||
{
|
||||
if (!respectManual || state[EquipSlot.MainHand, false] is not StateChanged.Source.Manual)
|
||||
_state.ChangeItem(state, EquipSlot.MainHand, item, StateChanged.Source.Fixed);
|
||||
totalEquipFlags |= EquipFlag.Mainhand;
|
||||
}
|
||||
|
|
@ -142,6 +154,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var item = design.Item(EquipSlot.OffHand);
|
||||
if (state.ModelData.Item(EquipSlot.OffHand).Type == item.Type)
|
||||
{
|
||||
if (!respectManual || state[EquipSlot.OffHand, false] is not StateChanged.Source.Manual)
|
||||
_state.ChangeItem(state, EquipSlot.OffHand, item, StateChanged.Source.Fixed);
|
||||
totalEquipFlags |= EquipFlag.Offhand;
|
||||
}
|
||||
|
|
@ -149,24 +162,55 @@ public class AutoDesignApplier : IDisposable
|
|||
|
||||
if (equipFlags.HasFlag(EquipFlag.MainhandStain))
|
||||
{
|
||||
if (!respectManual || state[EquipSlot.MainHand, true] is not StateChanged.Source.Manual)
|
||||
_state.ChangeStain(state, EquipSlot.MainHand, design.Stain(EquipSlot.MainHand), StateChanged.Source.Fixed);
|
||||
totalEquipFlags |= EquipFlag.MainhandStain;
|
||||
}
|
||||
|
||||
if (equipFlags.HasFlag(EquipFlag.OffhandStain))
|
||||
{
|
||||
if (!respectManual || state[EquipSlot.OffHand, true] is not StateChanged.Source.Manual)
|
||||
_state.ChangeStain(state, EquipSlot.OffHand, design.Stain(EquipSlot.OffHand), StateChanged.Source.Fixed);
|
||||
totalEquipFlags |= EquipFlag.OffhandStain;
|
||||
}
|
||||
}
|
||||
|
||||
private void Reduce(ActorState state, in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag totalCustomizeFlags)
|
||||
private void Reduce(ActorState state, in DesignData design, CustomizeFlag customizeFlags, ref CustomizeFlag totalCustomizeFlags,
|
||||
bool respectManual)
|
||||
{
|
||||
customizeFlags &= ~totalCustomizeFlags;
|
||||
if (customizeFlags == 0)
|
||||
return;
|
||||
|
||||
// TODO add race/gender handling
|
||||
var customize = state.ModelData.Customize;
|
||||
CustomizeFlag fixFlags = 0;
|
||||
if (customizeFlags.HasFlag(CustomizeFlag.Clan))
|
||||
{
|
||||
if (!respectManual || state[CustomizeIndex.Clan] is not StateChanged.Source.Manual)
|
||||
fixFlags |= _customizations.ChangeClan(ref customize, design.Customize.Clan);
|
||||
customizeFlags &= ~(CustomizeFlag.Clan | CustomizeFlag.Race);
|
||||
totalCustomizeFlags |= CustomizeFlag.Clan | CustomizeFlag.Race;
|
||||
}
|
||||
|
||||
if (customizeFlags.HasFlag(CustomizeFlag.Gender))
|
||||
{
|
||||
if (!respectManual || state[CustomizeIndex.Gender] is not StateChanged.Source.Manual)
|
||||
fixFlags |= _customizations.ChangeGender(ref customize, design.Customize.Gender);
|
||||
customizeFlags &= ~CustomizeFlag.Gender;
|
||||
totalCustomizeFlags |= CustomizeFlag.Gender;
|
||||
}
|
||||
|
||||
if (fixFlags != 0)
|
||||
_state.ChangeCustomize(state, customize, fixFlags, StateChanged.Source.Fixed);
|
||||
|
||||
if (customizeFlags.HasFlag(CustomizeFlag.Face))
|
||||
{
|
||||
if (!respectManual || state[CustomizeIndex.Face] is not StateChanged.Source.Manual)
|
||||
_state.ChangeCustomize(state, CustomizeIndex.Face, design.Customize.Face, StateChanged.Source.Fixed);
|
||||
customizeFlags &= ~CustomizeFlag.Face;
|
||||
totalCustomizeFlags |= CustomizeFlag.Face;
|
||||
}
|
||||
|
||||
var set = _customizations.AwaitedService.GetList(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
|
||||
var face = state.ModelData.Customize.Face;
|
||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||
|
|
@ -178,6 +222,7 @@ public class AutoDesignApplier : IDisposable
|
|||
var value = design.Customize[index];
|
||||
if (CustomizationService.IsCustomizationValid(set, face, index, value))
|
||||
{
|
||||
if (!respectManual || state[index] is not StateChanged.Source.Manual)
|
||||
_state.ChangeCustomize(state, index, value, StateChanged.Source.Fixed);
|
||||
totalCustomizeFlags |= flag;
|
||||
}
|
||||
|
|
@ -185,28 +230,32 @@ public class AutoDesignApplier : IDisposable
|
|||
}
|
||||
|
||||
private void Reduce(ActorState state, in DesignData design, bool applyHat, bool applyVisor, bool applyWeapon, bool applyWet,
|
||||
ref byte totalMetaFlags)
|
||||
ref byte totalMetaFlags, bool respectManual)
|
||||
{
|
||||
if (applyHat && (totalMetaFlags & 0x01) == 0)
|
||||
{
|
||||
if (!respectManual || state[ActorState.MetaFlag.HatState] is not StateChanged.Source.Manual)
|
||||
_state.ChangeHatState(state, design.IsHatVisible(), StateChanged.Source.Fixed);
|
||||
totalMetaFlags |= 0x01;
|
||||
}
|
||||
|
||||
if (applyVisor && (totalMetaFlags & 0x02) == 0)
|
||||
{
|
||||
if (!respectManual || state[ActorState.MetaFlag.VisorState] is not StateChanged.Source.Manual)
|
||||
_state.ChangeVisorState(state, design.IsVisorToggled(), StateChanged.Source.Fixed);
|
||||
totalMetaFlags |= 0x02;
|
||||
}
|
||||
|
||||
if (applyWeapon && (totalMetaFlags & 0x04) == 0)
|
||||
{
|
||||
if (!respectManual || state[ActorState.MetaFlag.WeaponState] is not StateChanged.Source.Manual)
|
||||
_state.ChangeWeaponState(state, design.IsWeaponVisible(), StateChanged.Source.Fixed);
|
||||
totalMetaFlags |= 0x04;
|
||||
}
|
||||
|
||||
if (applyWet && (totalMetaFlags & 0x08) == 0)
|
||||
{
|
||||
if (!respectManual || state[ActorState.MetaFlag.Wetness] is not StateChanged.Source.Manual)
|
||||
_state.ChangeWetness(state, design.IsWet(), StateChanged.Source.Fixed);
|
||||
totalMetaFlags |= 0x08;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Utility;
|
||||
using Glamourer.Designs;
|
||||
|
|
@ -66,7 +67,32 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>
|
|||
var newSet = new AutoDesignSet(name, identifier.CreatePermanent()) { Enabled = false };
|
||||
_data.Add(newSet);
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Created new design set for {identifier.Incognito(null)}.");
|
||||
Glamourer.Log.Debug($"Created new design set for {newSet.Identifier.Incognito(null)}.");
|
||||
_event.Invoke(AutomationChanged.Type.AddedSet, newSet, (_data.Count - 1, name));
|
||||
}
|
||||
|
||||
public void DuplicateDesignSet(AutoDesignSet set)
|
||||
{
|
||||
var name = set.Name;
|
||||
var match = Regex.Match(name, @"\(Duplicate( (?<Number>\d+))?\)$",
|
||||
RegexOptions.Compiled | RegexOptions.NonBacktracking | RegexOptions.ExplicitCapture);
|
||||
if (match.Success)
|
||||
{
|
||||
var number = match.Groups["Number"];
|
||||
var replacement = number.Success ? $"(Duplicate {int.Parse(number.Value) + 1})" : "(Duplicate 2)";
|
||||
name = name.Replace(match.Value, replacement);
|
||||
}
|
||||
else
|
||||
{
|
||||
name += " (Duplicate)";
|
||||
}
|
||||
|
||||
var newSet = new AutoDesignSet(name, set.Identifier) { Enabled = false };
|
||||
newSet.Designs.AddRange(set.Designs.Select(d => d.Clone()));
|
||||
_data.Add(newSet);
|
||||
Save();
|
||||
Glamourer.Log.Debug(
|
||||
$"Duplicated new design set for {newSet.Identifier.Incognito(null)} with {newSet.Designs.Count} auto designs from existing set.");
|
||||
_event.Invoke(AutomationChanged.Type.AddedSet, newSet, (_data.Count - 1, name));
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +108,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>
|
|||
_enabled.Remove(set.Identifier);
|
||||
}
|
||||
|
||||
_data.RemoveAt(whichSet);
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Deleted design set {whichSet + 1}.");
|
||||
_event.Invoke(AutomationChanged.Type.DeletedSet, set, whichSet);
|
||||
|
|
@ -156,6 +183,10 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>
|
|||
oldEnabled.Enabled = false;
|
||||
_enabled.Add(set.Identifier, set);
|
||||
}
|
||||
else
|
||||
{
|
||||
_enabled.Remove(set.Identifier, out oldEnabled);
|
||||
}
|
||||
|
||||
Save();
|
||||
Glamourer.Log.Debug($"Changed enabled state of design set {whichSet + 1} to {value}.");
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ public enum ColorId
|
|||
FolderLine,
|
||||
EnabledAutoSet,
|
||||
DisabledAutoSet,
|
||||
AutomationActorAvailable,
|
||||
AutomationActorUnavailable,
|
||||
}
|
||||
|
||||
public static class Colors
|
||||
|
|
@ -32,6 +34,8 @@ public static class Colors
|
|||
ColorId.FolderLine => (0xFFFFF0C0, "Expanded Design Folder Line", "The line signifying which descendants belong to an expanded design folder." ),
|
||||
ColorId.EnabledAutoSet => (0xFFA0F0A0, "Enabled Automation Set", "An automation set that is currently enabled. Only one set can be enabled for each identifier at once." ),
|
||||
ColorId.DisabledAutoSet => (0xFF808080, "Disabled Automation Set", "An automation set that is currently disabled." ),
|
||||
ColorId.AutomationActorAvailable => (0xFFFFFFFF, "Automation Actor Available", "A character associated with the given automated design set is currently visible." ),
|
||||
ColorId.AutomationActorUnavailable => (0xFF808080, "Automation Actor Unavailable", "No character associated with the given automated design set is currently visible." ),
|
||||
_ => (0x00000000, string.Empty, string.Empty ),
|
||||
// @formatter:on
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,9 +5,12 @@ using System.Numerics;
|
|||
using Dalamud.Interface;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Structs;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Widgets;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.AutomationTab;
|
||||
|
||||
|
|
@ -16,10 +19,20 @@ public class SetPanel
|
|||
private readonly AutoDesignManager _manager;
|
||||
private readonly SetSelector _selector;
|
||||
|
||||
public SetPanel(SetSelector selector, AutoDesignManager manager)
|
||||
private readonly DesignCombo _designCombo;
|
||||
private readonly JobGroupCombo _jobGroupCombo;
|
||||
|
||||
private string? _tempName;
|
||||
private int _dragIndex = -1;
|
||||
|
||||
private Action? _endAction;
|
||||
|
||||
public SetPanel(SetSelector selector, AutoDesignManager manager, DesignManager designs, JobService jobs)
|
||||
{
|
||||
_selector = selector;
|
||||
_manager = manager;
|
||||
_designCombo = new DesignCombo(_manager, designs);
|
||||
_jobGroupCombo = new JobGroupCombo(manager, jobs);
|
||||
}
|
||||
|
||||
private AutoDesignSet Selection
|
||||
|
|
@ -43,8 +56,6 @@ public class SetPanel
|
|||
ImGuiUtil.DrawTextButton(Selection.Name, -Vector2.UnitX, buttonColor);
|
||||
}
|
||||
|
||||
private string? _tempName;
|
||||
|
||||
private void DrawPanel()
|
||||
{
|
||||
using var child = ImRaii.Child("##SetPanel", -Vector2.One, true);
|
||||
|
|
@ -66,47 +77,84 @@ public class SetPanel
|
|||
if (ImGui.Checkbox("Enabled", ref enabled))
|
||||
_manager.SetState(_selector.SelectionIndex, enabled);
|
||||
|
||||
using var table = ImRaii.Table("SetTable", 4, ImGuiTableFlags.RowBg);
|
||||
DrawDesignTable();
|
||||
}
|
||||
|
||||
private void DrawDesignTable()
|
||||
{
|
||||
using var table = ImRaii.Table("SetTable", 5, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollX | ImGuiTableFlags.ScrollY);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
ImGui.TableSetupColumn("##Index", ImGuiTableColumnFlags.WidthFixed, 40 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthFixed, 200 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("##del", ImGuiTableColumnFlags.WidthFixed, ImGui.GetFrameHeight());
|
||||
ImGui.TableSetupColumn("##Index", ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthFixed, 220 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("Application", ImGuiTableColumnFlags.WidthFixed, 5 * ImGui.GetFrameHeight() + 4 * 2 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("Job Restrictions", ImGuiTableColumnFlags.WidthStretch);
|
||||
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var (design, idx) in Selection.Designs.WithIndex())
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn($"#{idx:D2}");
|
||||
using var id = ImRaii.PushId(idx);
|
||||
ImGui.TableNextColumn();
|
||||
DrawDesignCombo(Selection, design, idx);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||
"Remove this design from the set.", false, true))
|
||||
_endAction = () => _manager.DeleteDesign(Selection, idx);
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
ImGui.Selectable($"#{idx + 1:D2}");
|
||||
DrawDragDrop(Selection, idx);
|
||||
ImGui.TableNextColumn();
|
||||
DrawJobGroupCombo(Selection, design, idx);
|
||||
}
|
||||
_designCombo.Draw(Selection, design, idx);
|
||||
DrawDragDrop(Selection, idx);
|
||||
ImGui.TableNextColumn();
|
||||
DrawApplicationTypeBoxes(Selection, design, idx);
|
||||
ImGui.TableNextColumn();
|
||||
_jobGroupCombo.Draw(Selection, design, idx);
|
||||
}
|
||||
|
||||
private void DrawDesignCombo(AutoDesignSet set, AutoDesign design, int autoDesignIndex)
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted("New");
|
||||
ImGui.TableNextColumn();
|
||||
_designCombo.Draw(Selection, null, -1);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
_endAction?.Invoke();
|
||||
_endAction = null;
|
||||
}
|
||||
|
||||
private void DrawDragDrop(AutoDesignSet set, int index)
|
||||
{
|
||||
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
||||
using var combo = ImRaii.Combo("##design", design.Design.Name);
|
||||
if (!combo)
|
||||
return;
|
||||
const string dragDropLabel = "DesignDragDrop";
|
||||
using (var target = ImRaii.DragDropTarget())
|
||||
{
|
||||
if (target.Success && ImGuiUtil.IsDropping(dragDropLabel))
|
||||
{
|
||||
if (_dragIndex >= 0)
|
||||
{
|
||||
var idx = _dragIndex;
|
||||
_endAction = () => _manager.MoveDesign(set, idx, index);
|
||||
}
|
||||
|
||||
private void DrawJobGroupCombo(AutoDesignSet set, AutoDesign design, int autoDesignIndex)
|
||||
_dragIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
using (var source = ImRaii.DragDropSource())
|
||||
{
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
using var combo = ImRaii.Combo("##JobGroups", design.Jobs.Name);
|
||||
if (!combo)
|
||||
return;
|
||||
if (source.Success && ImGui.SetDragDropPayload(dragDropLabel, nint.Zero, 0))
|
||||
{
|
||||
_dragIndex = index;
|
||||
ImGui.TextUnformatted($"Moving design #{index + 1:D2}...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawApplicationTypeBoxes(AutoDesignSet set, AutoDesign design, int autoDesignIndex)
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, 2 * ImGuiHelpers.GlobalScale);
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(2 * ImGuiHelpers.GlobalScale));
|
||||
var newType = design.ApplicationType;
|
||||
foreach (var (type, description) in Types)
|
||||
{
|
||||
|
|
@ -114,7 +162,10 @@ public class SetPanel
|
|||
if (ImGui.Checkbox($"##{(byte)type}", ref value))
|
||||
newType = value ? newType | type : newType & ~type;
|
||||
ImGuiUtil.HoverTooltip(description);
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
||||
_manager.ChangeApplicationType(set, autoDesignIndex, newType);
|
||||
}
|
||||
|
||||
private static readonly IReadOnlyList<(AutoDesign.Type, string)> Types = new[]
|
||||
|
|
@ -126,4 +177,64 @@ public class SetPanel
|
|||
(AutoDesign.Type.Stains, "Apply all dye changes that are enabled in this design."),
|
||||
(AutoDesign.Type.Weapons, "Apply all weapon changes that are enabled in this design and that are valid with the current weapon worn."),
|
||||
};
|
||||
|
||||
private sealed class JobGroupCombo : FilterComboCache<JobGroup>
|
||||
{
|
||||
private readonly AutoDesignManager _manager;
|
||||
private readonly JobService _jobs;
|
||||
|
||||
public JobGroupCombo(AutoDesignManager manager, JobService jobs)
|
||||
: base(() => jobs.JobGroups.Values.ToList())
|
||||
{
|
||||
_manager = manager;
|
||||
_jobs = jobs;
|
||||
}
|
||||
|
||||
public void Draw(AutoDesignSet set, AutoDesign design, int autoDesignIndex)
|
||||
{
|
||||
CurrentSelection = design.Jobs;
|
||||
CurrentSelectionIdx = _jobs.JobGroups.Values.IndexOf(j => j.Id == design.Jobs.Id);
|
||||
if (Draw("##JobGroups", design.Jobs.Name,
|
||||
"Select for which job groups this design should be applied.\nControl + Right-Click to set to all classes.",
|
||||
ImGui.GetContentRegionAvail().X, ImGui.GetTextLineHeight())
|
||||
&& CurrentSelectionIdx >= 0)
|
||||
_manager.ChangeJobCondition(set, autoDesignIndex, CurrentSelection);
|
||||
else if (ImGui.GetIO().KeyCtrl && ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
_manager.ChangeJobCondition(set, autoDesignIndex, _jobs.JobGroups[1]);
|
||||
}
|
||||
|
||||
protected override string ToString(JobGroup obj)
|
||||
=> obj.Name;
|
||||
}
|
||||
|
||||
private sealed class DesignCombo : FilterComboCache<Design>
|
||||
{
|
||||
private readonly AutoDesignManager _manager;
|
||||
private readonly DesignManager _designs;
|
||||
|
||||
public DesignCombo(AutoDesignManager manager, DesignManager designs)
|
||||
: base(() => designs.Designs.OrderBy(d => d.Name).ToList())
|
||||
{
|
||||
_designs = designs;
|
||||
_manager = manager;
|
||||
}
|
||||
|
||||
public void Draw(AutoDesignSet set, AutoDesign? design, int autoDesignIndex)
|
||||
{
|
||||
CurrentSelection = design?.Design ?? (Items.Count > 0 ? Items[0] : null);
|
||||
CurrentSelectionIdx = design?.Design.Index ?? (Items.Count > 0 ? 0 : -1);
|
||||
if (Draw("##design", CurrentSelection?.Name.Text ?? string.Empty, string.Empty, 220 * ImGuiHelpers.GlobalScale,
|
||||
ImGui.GetTextLineHeight())
|
||||
&& CurrentSelection != null)
|
||||
{
|
||||
if (autoDesignIndex >= 0)
|
||||
_manager.ChangeDesign(set, autoDesignIndex, CurrentSelection);
|
||||
else
|
||||
_manager.AddDesign(set, CurrentSelection);
|
||||
}
|
||||
}
|
||||
|
||||
protected override string ToString(Design obj)
|
||||
=> obj.Name.Text;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.String;
|
||||
using Vector2 = FFXIVClientStructs.FFXIV.Common.Math.Vector2;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.AutomationTab;
|
||||
|
||||
public class SetSelector : IDisposable
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly AutoDesignManager _manager;
|
||||
private readonly AutomationChanged _event;
|
||||
public AutoDesignSet? Selection { get; private set; }
|
||||
public int SelectionIndex = -1;
|
||||
|
||||
private bool IncognitoMode;
|
||||
private readonly ActorService _actors;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly List<AutoDesignSet> _list = new();
|
||||
|
||||
public SetSelector(AutoDesignManager manager, AutomationChanged @event)
|
||||
public AutoDesignSet? Selection { get; private set; }
|
||||
public int SelectionIndex { get; private set; } = -1;
|
||||
|
||||
private bool IncognitoMode;
|
||||
private int _dragIndex = -1;
|
||||
private Action? _endAction;
|
||||
|
||||
public SetSelector(AutoDesignManager manager, AutomationChanged @event, Configuration config, ActorService actors, ObjectManager objects)
|
||||
{
|
||||
_manager = manager;
|
||||
_event = @event;
|
||||
_config = config;
|
||||
_actors = actors;
|
||||
_objects = objects;
|
||||
_event.Subscribe(OnAutomationChanged, AutomationChanged.Priority.SetSelector);
|
||||
}
|
||||
|
||||
|
|
@ -41,13 +54,17 @@ public class SetSelector : IDisposable
|
|||
case AutomationChanged.Type.DeletedSet:
|
||||
if (set == Selection)
|
||||
{
|
||||
Selection = null;
|
||||
SelectionIndex = -1;
|
||||
SelectionIndex = _manager.Count == 0 ? -1 : SelectionIndex == 0 ? 0 : SelectionIndex - 1;
|
||||
Selection = SelectionIndex >= 0 ? _manager[SelectionIndex] : null;
|
||||
}
|
||||
|
||||
_dirty = true;
|
||||
break;
|
||||
case AutomationChanged.Type.AddedSet:
|
||||
SelectionIndex = (((int, string))data!).Item1;
|
||||
Selection = set!;
|
||||
_dirty = true;
|
||||
break;
|
||||
case AutomationChanged.Type.RenamedSet:
|
||||
case AutomationChanged.Type.MovedSet:
|
||||
case AutomationChanged.Type.ChangeIdentifier:
|
||||
|
|
@ -109,6 +126,7 @@ public class SetSelector : IDisposable
|
|||
_dirty = true;
|
||||
ImGui.SameLine();
|
||||
var f = _enabledFilter;
|
||||
|
||||
if (ImGui.CheckboxFlags("##enabledFilter", ref f, 3))
|
||||
{
|
||||
_enabledFilter = _enabledFilter switch
|
||||
|
|
@ -120,6 +138,11 @@ public class SetSelector : IDisposable
|
|||
_dirty = true;
|
||||
}
|
||||
|
||||
var pos = ImGui.GetItemRectMin();
|
||||
pos.X -= ImGuiHelpers.GlobalScale;
|
||||
ImGui.GetWindowDrawList().AddLine(pos, pos with { Y = ImGui.GetItemRectMax().Y }, ImGui.GetColorU32(ImGuiCol.Border),
|
||||
ImGuiHelpers.GlobalScale);
|
||||
|
||||
ImGuiUtil.HoverTooltip("Filter to show only enabled or disabled sets.");
|
||||
|
||||
DrawSelector();
|
||||
|
|
@ -135,7 +158,10 @@ public class SetSelector : IDisposable
|
|||
UpdateList();
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, _defaultItemSpacing);
|
||||
_selectableSize = new Vector2(0, 2 * ImGui.GetTextLineHeight() + ImGui.GetStyle().ItemSpacing.Y);
|
||||
_objects.Update();
|
||||
ImGuiClip.ClippedDraw(_list, DrawSetSelectable, _selectableSize.Y + 2 * ImGui.GetStyle().ItemSpacing.Y);
|
||||
_endAction?.Invoke();
|
||||
_endAction = null;
|
||||
}
|
||||
|
||||
private void DrawSetSelectable(AutoDesignSet set, int index)
|
||||
|
|
@ -150,22 +176,84 @@ public class SetSelector : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
DrawDragDrop(set, index);
|
||||
|
||||
var text = set.Identifier.ToString();
|
||||
if (IncognitoMode)
|
||||
text = set.Identifier.Incognito(text);
|
||||
var textSize = ImGui.CalcTextSize(text);
|
||||
var textColor = _objects.ContainsKey(set.Identifier) ? ColorId.AutomationActorAvailable : ColorId.AutomationActorUnavailable;
|
||||
ImGui.SetCursorPos(new Vector2(ImGui.GetContentRegionAvail().X - textSize.X,
|
||||
ImGui.GetCursorPosY() - ImGui.GetTextLineHeightWithSpacing()));
|
||||
ImGui.TextUnformatted(text);
|
||||
ImGuiUtil.TextColored(textColor.Value(), text);
|
||||
}
|
||||
|
||||
private void DrawSelectionButtons()
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FrameRounding, 0);
|
||||
var buttonWidth = new Vector2(_width / 1, 0);
|
||||
// TODO
|
||||
ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UserCircle.ToIconString(), buttonWidth
|
||||
, "Select the local player character.", true, true);
|
||||
var buttonWidth = new Vector2(_width / 3, 0);
|
||||
NewSetButton(buttonWidth);
|
||||
ImGui.SameLine();
|
||||
DuplicateSetButton(buttonWidth);
|
||||
ImGui.SameLine();
|
||||
DeleteSetButton(buttonWidth);
|
||||
}
|
||||
|
||||
private void NewSetButton(Vector2 size)
|
||||
{
|
||||
var id = _actors.AwaitedService.GetCurrentPlayer();
|
||||
if (!id.IsValid)
|
||||
id = _actors.AwaitedService.CreatePlayer(ByteString.FromSpanUnsafe("New Design"u8, true, false, true), ushort.MaxValue);
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size,
|
||||
$"Create a new Automatic Design Set for {id}. The associated player can be changed later.", !id.IsValid, true))
|
||||
_manager.AddDesignSet("New Design", id);
|
||||
}
|
||||
|
||||
private void DuplicateSetButton(Vector2 size)
|
||||
{
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clone.ToIconString(), size, "Duplicate the current Automatic Design Set.",
|
||||
Selection == null, true))
|
||||
_manager.DuplicateDesignSet(Selection!);
|
||||
}
|
||||
|
||||
|
||||
private void DeleteSetButton(Vector2 size)
|
||||
{
|
||||
var keyValid = _config.DeleteDesignModifier.IsActive();
|
||||
var (disabled, tt) = HasSelection
|
||||
? keyValid
|
||||
? (false, "Delete the currently selected design set.")
|
||||
: (true, $"Delete the currently selected design set.\n{_config.DeleteDesignModifier.ToString()}")
|
||||
: (true, "No Automatic Design Set selected.");
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), size, tt, disabled, true))
|
||||
_manager.DeleteDesignSet(SelectionIndex);
|
||||
}
|
||||
|
||||
private void DrawDragDrop(AutoDesignSet set, int index)
|
||||
{
|
||||
const string dragDropLabel = "DesignSetDragDrop";
|
||||
using (var target = ImRaii.DragDropTarget())
|
||||
{
|
||||
if (target.Success && ImGuiUtil.IsDropping(dragDropLabel))
|
||||
{
|
||||
if (_dragIndex >= 0)
|
||||
{
|
||||
var idx = _dragIndex;
|
||||
_endAction = () => _manager.MoveSet(idx, index);
|
||||
}
|
||||
|
||||
_dragIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
using (var source = ImRaii.DragDropSource())
|
||||
{
|
||||
if (source.Success && ImGui.SetDragDropPayload(dragDropLabel, nint.Zero, 0))
|
||||
{
|
||||
_dragIndex = index;
|
||||
ImGui.TextUnformatted($"Moving design set {set.Name} from position {index + 1}...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
|
|
@ -133,8 +134,11 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
|||
public int Count
|
||||
=> Identifiers.Count;
|
||||
|
||||
/// <summary> Also (inefficiently) handles All Worlds players. </summary>
|
||||
public bool ContainsKey(ActorIdentifier key)
|
||||
=> Identifiers.ContainsKey(key);
|
||||
=> Identifiers.ContainsKey(key)
|
||||
|| key.HomeWorld == ushort.MaxValue
|
||||
&& Identifiers.Keys.FirstOrDefault(i => i.Type is IdentifierType.Player && i.PlayerName == key.PlayerName).IsValid;
|
||||
|
||||
public bool TryGetValue(ActorIdentifier key, out ActorData value)
|
||||
=> Identifiers.TryGetValue(key, out value);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ public readonly unsafe struct Actor : IEquatable<Actor>
|
|||
public Model Model
|
||||
=> Valid ? AsObject->DrawObject : null;
|
||||
|
||||
public byte Job
|
||||
=> IsCharacter ? AsCharacter->CharacterData.ClassJob : (byte)0;
|
||||
|
||||
public static implicit operator bool(Actor actor)
|
||||
=> actor.Address != nint.Zero;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ public class ActorState
|
|||
/// <summary> This should be the desired state of the draw object. </summary>
|
||||
public DesignData ModelData;
|
||||
|
||||
/// <summary> The last seen job. </summary>
|
||||
public byte LastJob;
|
||||
|
||||
/// <summary> This contains whether a change to the base data was made by the game, the user via manual input or through automatic application. </summary>
|
||||
private readonly StateChanged.Source[] _sources = Enumerable
|
||||
.Repeat(StateChanged.Source.Game, EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5).ToArray();
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
=> GetOrCreate(actor.GetIdentifier(_actors.AwaitedService), actor, out state);
|
||||
|
||||
/// <summary> Try to obtain or create a new state for an existing actor. Returns false if no state could be created. </summary>
|
||||
public bool GetOrCreate(ActorIdentifier identifier, Actor actor, [NotNullWhen(true)] out ActorState? state)
|
||||
public unsafe bool GetOrCreate(ActorIdentifier identifier, Actor actor, [NotNullWhen(true)] out ActorState? state)
|
||||
{
|
||||
if (TryGetValue(identifier, out state))
|
||||
return true;
|
||||
|
|
@ -81,6 +81,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
{
|
||||
ModelData = FromActor(actor, true),
|
||||
BaseData = FromActor(actor, false),
|
||||
LastJob = (byte) (actor.IsCharacter ? actor.AsCharacter->CharacterData.ClassJob : 0),
|
||||
};
|
||||
// state.Identifier is owned.
|
||||
_states.Add(state.Identifier, state);
|
||||
|
|
|
|||
|
|
@ -1,318 +0,0 @@
|
|||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Plugin.Ipc;
|
||||
using System;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.Api.Enums;
|
||||
|
||||
namespace Glamourer;
|
||||
|
||||
public partial class Glamourer
|
||||
{
|
||||
public class GlamourerIpc : IDisposable
|
||||
{
|
||||
public const int CurrentApiVersion = 0;
|
||||
public const string LabelProviderApiVersion = "Glamourer.ApiVersion";
|
||||
public const string LabelProviderGetAllCustomization = "Glamourer.GetAllCustomization";
|
||||
public const string LabelProviderGetAllCustomizationFromCharacter = "Glamourer.GetAllCustomizationFromCharacter";
|
||||
public const string LabelProviderApplyAll = "Glamourer.ApplyAll";
|
||||
public const string LabelProviderApplyAllToCharacter = "Glamourer.ApplyAllToCharacter";
|
||||
public const string LabelProviderApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment";
|
||||
public const string LabelProviderApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter";
|
||||
public const string LabelProviderApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization";
|
||||
public const string LabelProviderApplyOnlyCustomizationToCharacter = "Glamourer.ApplyOnlyCustomizationToCharacter";
|
||||
public const string LabelProviderRevert = "Glamourer.Revert";
|
||||
public const string LabelProviderRevertCharacter = "Glamourer.RevertCharacter";
|
||||
|
||||
private readonly ObjectTable _objectTable;
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly ActiveDesign.Manager _stateManager;
|
||||
private readonly ItemManager _items;
|
||||
private readonly PenumbraAttach _penumbra;
|
||||
private readonly ActorService _actors;
|
||||
|
||||
internal ICallGateProvider<string, string?>? ProviderGetAllCustomization;
|
||||
internal ICallGateProvider<Character?, string?>? ProviderGetAllCustomizationFromCharacter;
|
||||
internal ICallGateProvider<string, string, object>? ProviderApplyAll;
|
||||
internal ICallGateProvider<string, Character?, object>? ProviderApplyAllToCharacter;
|
||||
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyCustomization;
|
||||
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyCustomizationToCharacter;
|
||||
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyEquipment;
|
||||
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyEquipmentToCharacter;
|
||||
internal ICallGateProvider<string, object>? ProviderRevert;
|
||||
internal ICallGateProvider<Character?, object>? ProviderRevertCharacter;
|
||||
internal ICallGateProvider<int>? ProviderGetApiVersion;
|
||||
|
||||
public GlamourerIpc(ObjectTable objectTable, DalamudPluginInterface pluginInterface, ActiveDesign.Manager stateManager,
|
||||
ItemManager items, PenumbraAttach penumbra, ActorService actors)
|
||||
{
|
||||
_objectTable = objectTable;
|
||||
_pluginInterface = pluginInterface;
|
||||
_stateManager = stateManager;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_actors = actors;
|
||||
|
||||
InitializeProviders();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> DisposeProviders();
|
||||
|
||||
private void DisposeProviders()
|
||||
{
|
||||
ProviderGetAllCustomization?.UnregisterFunc();
|
||||
ProviderGetAllCustomizationFromCharacter?.UnregisterFunc();
|
||||
ProviderApplyAll?.UnregisterAction();
|
||||
ProviderApplyAllToCharacter?.UnregisterAction();
|
||||
ProviderApplyOnlyCustomization?.UnregisterAction();
|
||||
ProviderApplyOnlyCustomizationToCharacter?.UnregisterAction();
|
||||
ProviderApplyOnlyEquipment?.UnregisterAction();
|
||||
ProviderApplyOnlyEquipmentToCharacter?.UnregisterAction();
|
||||
ProviderRevert?.UnregisterAction();
|
||||
ProviderRevertCharacter?.UnregisterAction();
|
||||
ProviderGetApiVersion?.UnregisterFunc();
|
||||
}
|
||||
|
||||
private void InitializeProviders()
|
||||
{
|
||||
try
|
||||
{
|
||||
ProviderGetApiVersion = _pluginInterface.GetIpcProvider<int>(LabelProviderApiVersion);
|
||||
ProviderGetApiVersion.RegisterFunc(GetApiVersion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApiVersion}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderGetAllCustomization = _pluginInterface.GetIpcProvider<string, string?>(LabelProviderGetAllCustomization);
|
||||
ProviderGetAllCustomization.RegisterFunc(GetAllCustomization);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderGetAllCustomizationFromCharacter =
|
||||
_pluginInterface.GetIpcProvider<Character?, string?>(LabelProviderGetAllCustomizationFromCharacter);
|
||||
ProviderGetAllCustomizationFromCharacter.RegisterFunc(GetAllCustomization);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderGetAllCustomizationFromCharacter}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyAll =
|
||||
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyAll);
|
||||
ProviderApplyAll.RegisterAction(ApplyAll);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyAllToCharacter =
|
||||
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyAllToCharacter);
|
||||
ProviderApplyAllToCharacter.RegisterAction(ApplyAll);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyOnlyCustomization =
|
||||
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyCustomization);
|
||||
ProviderApplyOnlyCustomization.RegisterAction(ApplyOnlyCustomization);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyOnlyCustomizationToCharacter =
|
||||
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyCustomizationToCharacter);
|
||||
ProviderApplyOnlyCustomizationToCharacter.RegisterAction(ApplyOnlyCustomization);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyOnlyEquipment =
|
||||
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyEquipment);
|
||||
ProviderApplyOnlyEquipment.RegisterAction(ApplyOnlyEquipment);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderApplyOnlyEquipmentToCharacter =
|
||||
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyEquipmentToCharacter);
|
||||
ProviderApplyOnlyEquipmentToCharacter.RegisterAction(ApplyOnlyEquipment);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderRevert =
|
||||
_pluginInterface.GetIpcProvider<string, object>(LabelProviderRevert);
|
||||
ProviderRevert.RegisterAction(Revert);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProviderRevertCharacter =
|
||||
_pluginInterface.GetIpcProvider<Character?, object>(LabelProviderRevertCharacter);
|
||||
ProviderRevertCharacter.RegisterAction(Revert);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetApiVersion()
|
||||
=> CurrentApiVersion;
|
||||
|
||||
private void ApplyAll(string customization, string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() == characterName)
|
||||
{
|
||||
ApplyAll(customization, gameObject as Character);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyAll(string customization, Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return;
|
||||
|
||||
var design = Design.CreateTemporaryFromBase64(_items, customization, true, true);
|
||||
var active = _stateManager.GetOrCreateSave(character.Address);
|
||||
_stateManager.ApplyDesign(active, design, false);
|
||||
}
|
||||
|
||||
private void ApplyOnlyCustomization(string customization, string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() == characterName)
|
||||
{
|
||||
ApplyOnlyCustomization(customization, gameObject as Character);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyOnlyCustomization(string customization, Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return;
|
||||
|
||||
var design = Design.CreateTemporaryFromBase64(_items, customization, true, false);
|
||||
var active = _stateManager.GetOrCreateSave(character.Address);
|
||||
_stateManager.ApplyDesign(active, design, false);
|
||||
}
|
||||
|
||||
private void ApplyOnlyEquipment(string customization, string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() != characterName)
|
||||
continue;
|
||||
|
||||
ApplyOnlyEquipment(customization, gameObject as Character);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyOnlyEquipment(string customization, Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return;
|
||||
|
||||
var design = Design.CreateTemporaryFromBase64(_items, customization, false, true);
|
||||
var active = _stateManager.GetOrCreateSave(character.Address);
|
||||
_stateManager.ApplyDesign(active, design, false);
|
||||
}
|
||||
|
||||
private void Revert(string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() != characterName)
|
||||
continue;
|
||||
|
||||
Revert(gameObject as Character);
|
||||
}
|
||||
}
|
||||
|
||||
private void Revert(Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return;
|
||||
|
||||
var ident = _actors.AwaitedService.FromObject(character, true, false, false);
|
||||
_stateManager.DeleteSave(ident);
|
||||
_penumbra.RedrawObject(character.Address, RedrawType.Redraw);
|
||||
}
|
||||
|
||||
private string? GetAllCustomization(Character? character)
|
||||
{
|
||||
if (character == null)
|
||||
return null;
|
||||
|
||||
var ident = _actors.AwaitedService.FromObject(character, true, false, false);
|
||||
if (!_stateManager.TryGetValue(ident, out var design))
|
||||
design = new ActiveDesign(_items, ident, character.Address);
|
||||
|
||||
return design.CreateOldBase64();
|
||||
}
|
||||
|
||||
private string? GetAllCustomization(string characterName)
|
||||
{
|
||||
foreach (var gameObject in _objectTable)
|
||||
{
|
||||
if (gameObject.Name.ToString() == characterName)
|
||||
return GetAllCustomization(gameObject as Character);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
using System;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Interop;
|
||||
using Penumbra.Api;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Api.Helpers;
|
||||
|
||||
namespace Glamourer.Api;
|
||||
|
||||
public unsafe class PenumbraAttach : IDisposable
|
||||
{
|
||||
public const int RequiredPenumbraBreakingVersion = 4;
|
||||
public const int RequiredPenumbraFeatureVersion = 15;
|
||||
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly EventSubscriber<ChangedItemType, uint> _tooltipSubscriber;
|
||||
private readonly EventSubscriber<MouseButton, ChangedItemType, uint> _clickSubscriber;
|
||||
private readonly EventSubscriber<nint, string, nint, nint, nint> _creatingCharacterBase;
|
||||
private readonly EventSubscriber<nint, string, nint> _createdCharacterBase;
|
||||
private ActionSubscriber<int, RedrawType> _redrawSubscriber;
|
||||
private FuncSubscriber<nint, (nint, string)> _drawObjectInfo;
|
||||
private FuncSubscriber<int, int> _cutsceneParent;
|
||||
|
||||
private readonly EventSubscriber _initializedEvent;
|
||||
private readonly EventSubscriber _disposedEvent;
|
||||
public bool Available { get; private set; }
|
||||
|
||||
public PenumbraAttach(DalamudPluginInterface pi)
|
||||
{
|
||||
_pluginInterface = pi;
|
||||
_initializedEvent = Ipc.Initialized.Subscriber(pi, Reattach);
|
||||
_disposedEvent = Ipc.Disposed.Subscriber(pi, Unattach);
|
||||
_tooltipSubscriber = Ipc.ChangedItemTooltip.Subscriber(pi);
|
||||
_clickSubscriber = Ipc.ChangedItemClick.Subscriber(pi);
|
||||
_createdCharacterBase = Ipc.CreatedCharacterBase.Subscriber(pi);
|
||||
_creatingCharacterBase = Ipc.CreatingCharacterBase.Subscriber(pi);
|
||||
Reattach();
|
||||
}
|
||||
|
||||
public event Action<MouseButton, ChangedItemType, uint> Click
|
||||
{
|
||||
add => _clickSubscriber.Event += value;
|
||||
remove => _clickSubscriber.Event -= value;
|
||||
}
|
||||
|
||||
public event Action<ChangedItemType, uint> Tooltip
|
||||
{
|
||||
add => _tooltipSubscriber.Event += value;
|
||||
remove => _tooltipSubscriber.Event -= value;
|
||||
}
|
||||
|
||||
|
||||
public event Action<nint, string, nint, nint, nint> CreatingCharacterBase
|
||||
{
|
||||
add => _creatingCharacterBase.Event += value;
|
||||
remove => _creatingCharacterBase.Event -= value;
|
||||
}
|
||||
|
||||
public event Action<nint, string, nint> CreatedCharacterBase
|
||||
{
|
||||
add => _createdCharacterBase.Event += value;
|
||||
remove => _createdCharacterBase.Event -= value;
|
||||
}
|
||||
|
||||
public Actor GameObjectFromDrawObject(IntPtr drawObject)
|
||||
=> Available ? _drawObjectInfo.Invoke(drawObject).Item1 : Actor.Null;
|
||||
|
||||
public int CutsceneParent(int idx)
|
||||
=> Available ? _cutsceneParent.Invoke(idx) : -1;
|
||||
|
||||
public void RedrawObject(Actor actor, RedrawType settings)
|
||||
{
|
||||
if (!actor || !Available)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_redrawSubscriber.Invoke(actor.Index, settings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PluginLog.Debug($"Failure redrawing object:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Reattach()
|
||||
{
|
||||
try
|
||||
{
|
||||
Unattach();
|
||||
|
||||
var (breaking, feature) = Ipc.ApiVersions.Subscriber(_pluginInterface).Invoke();
|
||||
if (breaking != RequiredPenumbraBreakingVersion || feature < RequiredPenumbraFeatureVersion)
|
||||
throw new Exception(
|
||||
$"Invalid Version {breaking}.{feature:D4}, required major Version {RequiredPenumbraBreakingVersion} with feature greater or equal to {RequiredPenumbraFeatureVersion}.");
|
||||
|
||||
_tooltipSubscriber.Enable();
|
||||
_clickSubscriber.Enable();
|
||||
_creatingCharacterBase.Enable();
|
||||
_createdCharacterBase.Enable();
|
||||
_drawObjectInfo = Ipc.GetDrawObjectInfo.Subscriber(_pluginInterface);
|
||||
_cutsceneParent = Ipc.GetCutsceneParentIndex.Subscriber(_pluginInterface);
|
||||
_redrawSubscriber = Ipc.RedrawObjectByIndex.Subscriber(_pluginInterface);
|
||||
Available = true;
|
||||
Glamourer.Log.Debug("Glamourer attached to Penumbra.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Glamourer.Log.Debug($"Could not attach to Penumbra:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Unattach()
|
||||
{
|
||||
_tooltipSubscriber.Disable();
|
||||
_clickSubscriber.Disable();
|
||||
_creatingCharacterBase.Disable();
|
||||
_createdCharacterBase.Disable();
|
||||
if (Available)
|
||||
{
|
||||
Available = false;
|
||||
Glamourer.Log.Debug("Glamourer detached from Penumbra.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Unattach();
|
||||
_tooltipSubscriber.Dispose();
|
||||
_clickSubscriber.Dispose();
|
||||
_creatingCharacterBase.Dispose();
|
||||
_createdCharacterBase.Dispose();
|
||||
_initializedEvent.Dispose();
|
||||
_disposedEvent.Dispose();
|
||||
}
|
||||
|
||||
//private static void PenumbraTooltip(ChangedItemType type, uint _)
|
||||
//{
|
||||
// if (type == ChangedItemType.Item)
|
||||
// ImGui.Text("Right click to apply to current Glamourer Set. [Glamourer]");
|
||||
//}
|
||||
//
|
||||
//private void PenumbraRightClick(MouseButton button, ChangedItemType type, uint id)
|
||||
//{
|
||||
// if (button != MouseButton.Right || type != ChangedItemType.Item)
|
||||
// return;
|
||||
//
|
||||
// var item = (Lumina.Excel.GeneratedSheets.Item)type.GetObject(Dalamud.GameData, id)!;
|
||||
// var writeItem = new Item2(item, string.Empty);
|
||||
//
|
||||
// UpdateItem(_objects.GPosePlayer, writeItem);
|
||||
// UpdateItem(_objects.Player, writeItem);
|
||||
//}
|
||||
|
||||
//private static void UpdateItem(Actor actor, Item2 item2)
|
||||
//{
|
||||
// if (!actor || !actor.DrawObject)
|
||||
// return;
|
||||
//
|
||||
// switch (item2.EquippableTo)
|
||||
// {
|
||||
// case EquipSlot.MainHand:
|
||||
// {
|
||||
// var off = item2.HasSubModel
|
||||
// ? new CharacterWeapon(item2.SubModel.id, item2.SubModel.type, item2.SubModel.variant, actor.DrawObject.OffHand.Stain)
|
||||
// : item2.IsBothHand
|
||||
// ? CharacterWeapon.Empty
|
||||
// : actor.OffHand;
|
||||
// var main = new CharacterWeapon(item2.MainModel.id, item2.MainModel.type, item2.MainModel.variant,
|
||||
// actor.DrawObject.MainHand.Stain);
|
||||
// Glamourer.RedrawManager.LoadWeapon(actor, main, off);
|
||||
// return;
|
||||
// }
|
||||
// case EquipSlot.OffHand:
|
||||
// {
|
||||
// var off = new CharacterWeapon(item2.MainModel.id, item2.MainModel.type, item2.MainModel.variant,
|
||||
// actor.DrawObject.OffHand.Stain);
|
||||
// var main = actor.MainHand;
|
||||
// Glamourer.RedrawManager.LoadWeapon(actor, main, off);
|
||||
// return;
|
||||
// }
|
||||
// default:
|
||||
// {
|
||||
// var current = actor.DrawObject.Equip[item2.EquippableTo];
|
||||
// var armor = new CharacterArmor(item2.MainModel.id, (byte)item2.MainModel.variant, current.Stain);
|
||||
// Glamourer.RedrawManager.UpdateSlot(actor.DrawObject, item2.EquippableTo, armor);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// Update objects without triggering PlayerWatcher Events,
|
||||
// then manually redraw using Penumbra.
|
||||
public void UpdateCharacters(Character character, Character? gPoseOriginalCharacter = null)
|
||||
{
|
||||
//RedrawObject(character, RedrawType.Redraw, true);
|
||||
//
|
||||
//// Special case for carrying over changes to the gPose player to the regular player, too.
|
||||
//if (gPoseOriginalCharacter == null)
|
||||
// return;
|
||||
//
|
||||
//newEquip.Write(gPoseOriginalCharacter.Address);
|
||||
//RedrawObject(gPoseOriginalCharacter, RedrawType.AfterGPose, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
|
||||
|
||||
namespace Glamourer;
|
||||
|
||||
public class Configuration : IPluginConfiguration, ISavable
|
||||
{
|
||||
public bool UseRestrictedGearProtection = true;
|
||||
|
||||
public int Version { get; set; } = 2;
|
||||
|
||||
public Dictionary<ColorId, uint> Colors { get; set; }
|
||||
= Enum.GetValues<ColorId>().ToDictionary(c => c, c => c.Data().DefaultColor);
|
||||
|
||||
[JsonIgnore]
|
||||
private readonly SaveService _saveService;
|
||||
|
||||
public Configuration(SaveService saveService, ConfigMigrationService migrator)
|
||||
{
|
||||
_saveService = saveService;
|
||||
Load(migrator);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
=> _saveService.QueueSave(this);
|
||||
|
||||
public void Load(ConfigMigrationService migrator)
|
||||
{
|
||||
static void HandleDeserializationError(object? sender, ErrorEventArgs errorArgs)
|
||||
{
|
||||
Glamourer.Log.Error(
|
||||
$"Error parsing Configuration at {errorArgs.ErrorContext.Path}, using default or migrating:\n{errorArgs.ErrorContext.Error}");
|
||||
errorArgs.ErrorContext.Handled = true;
|
||||
}
|
||||
|
||||
if (!File.Exists(_saveService.FileNames.ConfigFile))
|
||||
return;
|
||||
|
||||
if (File.Exists(_saveService.FileNames.ConfigFile))
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(_saveService.FileNames.ConfigFile);
|
||||
JsonConvert.PopulateObject(text, this, new JsonSerializerSettings
|
||||
{
|
||||
Error = HandleDeserializationError,
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.ChatService.NotificationMessage(ex,
|
||||
"Error reading Configuration, reverting to default.\nYou may be able to restore your configuration using the rolling backups in the XIVLauncher/backups/Glamourer directory.",
|
||||
"Error reading Configuration", "Error", NotificationType.Error);
|
||||
}
|
||||
|
||||
migrator.Migrate(this);
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.ConfigFile;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
|
||||
var serializer = new JsonSerializer { Formatting = Formatting.Indented };
|
||||
serializer.Serialize(jWriter, this);
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfigMigrationService
|
||||
{
|
||||
private readonly SaveService _saveService;
|
||||
|
||||
private Configuration _config = null!;
|
||||
private JObject _data = null!;
|
||||
|
||||
public ConfigMigrationService(SaveService saveService)
|
||||
=> _saveService = saveService;
|
||||
|
||||
public void Migrate(Configuration config)
|
||||
{
|
||||
_config = config;
|
||||
_data = JObject.Parse(File.ReadAllText(_saveService.FileNames.ConfigFile));
|
||||
MigrateV1To2();
|
||||
}
|
||||
|
||||
private void MigrateV1To2()
|
||||
{
|
||||
if (_config.Version > 1)
|
||||
return;
|
||||
|
||||
_config.Version = 2;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfigurationOld : IPluginConfiguration, ISavable
|
||||
{
|
||||
[JsonIgnore]
|
||||
private readonly SaveService _saveService;
|
||||
|
||||
public class FixedDesign
|
||||
{
|
||||
public string Name = string.Empty;
|
||||
public string Path = string.Empty;
|
||||
public uint JobGroups;
|
||||
public bool Enabled;
|
||||
}
|
||||
|
||||
public int Version { get; set; } = 1;
|
||||
|
||||
public const uint DefaultCustomizationColor = 0xFFC000C0;
|
||||
public const uint DefaultStateColor = 0xFF00C0C0;
|
||||
public const uint DefaultEquipmentColor = 0xFF00C000;
|
||||
|
||||
public bool UseRestrictedGearProtection { get; set; } = true;
|
||||
|
||||
public bool FoldersFirst { get; set; } = false;
|
||||
public bool ColorDesigns { get; set; } = true;
|
||||
public bool ShowLocks { get; set; } = true;
|
||||
public bool ApplyFixedDesigns { get; set; } = true;
|
||||
|
||||
public uint CustomizationColor { get; set; } = DefaultCustomizationColor;
|
||||
public uint StateColor { get; set; } = DefaultStateColor;
|
||||
public uint EquipmentColor { get; set; } = DefaultEquipmentColor;
|
||||
|
||||
public List<FixedDesign> FixedDesigns { get; set; } = new();
|
||||
|
||||
public void Save()
|
||||
=> _saveService.QueueSave(this);
|
||||
|
||||
public ConfigurationOld(SaveService saveService)
|
||||
{
|
||||
_saveService = saveService;
|
||||
Load();
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
static void HandleDeserializationError(object? sender, ErrorEventArgs errorArgs)
|
||||
{
|
||||
Glamourer.Log.Error(
|
||||
$"Error parsing Configuration at {errorArgs.ErrorContext.Path}, using default or migrating:\n{errorArgs.ErrorContext.Error}");
|
||||
errorArgs.ErrorContext.Handled = true;
|
||||
}
|
||||
|
||||
if (!File.Exists(_saveService.FileNames.ConfigFile))
|
||||
return;
|
||||
|
||||
var text = File.ReadAllText(_saveService.FileNames.ConfigFile);
|
||||
JsonConvert.PopulateObject(text, this, new JsonSerializerSettings
|
||||
{
|
||||
Error = HandleDeserializationError,
|
||||
});
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.ConfigFile;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
|
||||
var serializer = new JsonSerializer { Formatting = Formatting.Indented };
|
||||
serializer.Serialize(jWriter, this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
using Dalamud.Data;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.Plugin;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Glamourer;
|
||||
|
||||
public class DalamudServices
|
||||
{
|
||||
public DalamudServices(DalamudPluginInterface pi)
|
||||
{
|
||||
pi.Inject(this);
|
||||
}
|
||||
|
||||
public void AddServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton(PluginInterface);
|
||||
services.AddSingleton(Commands);
|
||||
services.AddSingleton(GameData);
|
||||
services.AddSingleton(ClientState);
|
||||
services.AddSingleton(GameGui);
|
||||
services.AddSingleton(Chat);
|
||||
services.AddSingleton(Framework);
|
||||
services.AddSingleton(Targets);
|
||||
services.AddSingleton(Objects);
|
||||
services.AddSingleton(KeyState);
|
||||
services.AddSingleton(this);
|
||||
services.AddSingleton(PluginInterface.UiBuilder);
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
[PluginService][RequiredVersion("1.0")] public DalamudPluginInterface PluginInterface { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public CommandManager Commands { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public DataManager GameData { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public ClientState ClientState { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public GameGui GameGui { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public ChatGui Chat { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public Framework Framework { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public TargetManager Targets { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public ObjectTable Objects { get; private set; } = null!;
|
||||
[PluginService][RequiredVersion("1.0")] public KeyState KeyState { get; private set; } = null!;
|
||||
// @formatter:on
|
||||
}
|
||||
|
|
@ -1,320 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public partial class Design : DesignData, ISavable
|
||||
{
|
||||
public const int FileVersion = 1;
|
||||
|
||||
|
||||
public Guid Identifier { get; internal init; }
|
||||
public DateTimeOffset CreationDate { get; internal init; }
|
||||
public LowerString Name { get; internal set; } = LowerString.Empty;
|
||||
public string Description { get; internal set; } = string.Empty;
|
||||
public string[] Tags { get; internal set; } = Array.Empty<string>();
|
||||
public int Index { get; internal set; }
|
||||
|
||||
public EquipFlag ApplyEquip { get; internal set; }
|
||||
public CustomizeFlag ApplyCustomize { get; internal set; }
|
||||
public QuadBool Wetness { get; internal set; } = QuadBool.NullFalse;
|
||||
public QuadBool Visor { get; internal set; } = QuadBool.NullFalse;
|
||||
public QuadBool Hat { get; internal set; } = QuadBool.NullFalse;
|
||||
public QuadBool Weapon { get; internal set; } = QuadBool.NullFalse;
|
||||
public bool WriteProtected { get; internal set; }
|
||||
|
||||
public bool DoApplyEquip(EquipSlot slot)
|
||||
=> ApplyEquip.HasFlag(slot.ToFlag());
|
||||
|
||||
public bool DoApplyStain(EquipSlot slot)
|
||||
=> ApplyEquip.HasFlag(slot.ToStainFlag());
|
||||
|
||||
public bool DoApplyCustomize(CustomizeIndex idx)
|
||||
=> ApplyCustomize.HasFlag(idx.ToFlag());
|
||||
|
||||
internal bool SetApplyEquip(EquipSlot slot, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyEquip | slot.ToFlag() : ApplyEquip & ~slot.ToFlag();
|
||||
if (newValue == ApplyEquip)
|
||||
return false;
|
||||
|
||||
ApplyEquip = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyStain(EquipSlot slot, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyEquip | slot.ToStainFlag() : ApplyEquip & ~slot.ToStainFlag();
|
||||
if (newValue == ApplyEquip)
|
||||
return false;
|
||||
|
||||
ApplyEquip = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyCustomize(CustomizeIndex idx, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyCustomize | idx.ToFlag() : ApplyCustomize & ~idx.ToFlag();
|
||||
if (newValue == ApplyCustomize)
|
||||
return false;
|
||||
|
||||
ApplyCustomize = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
internal Design(ItemManager items)
|
||||
: base(items)
|
||||
{ }
|
||||
|
||||
public JObject JsonSerialize()
|
||||
{
|
||||
var ret = new JObject
|
||||
{
|
||||
[nameof(FileVersion)] = FileVersion,
|
||||
[nameof(Identifier)] = Identifier,
|
||||
[nameof(CreationDate)] = CreationDate,
|
||||
[nameof(Name)] = Name.Text,
|
||||
[nameof(Description)] = Description,
|
||||
[nameof(Tags)] = JArray.FromObject(Tags),
|
||||
[nameof(WriteProtected)] = WriteProtected,
|
||||
["Equipment"] = SerializeEquipment(),
|
||||
[nameof(ModelData.Customize)] = SerializeCustomize(),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
public JObject SerializeEquipment()
|
||||
{
|
||||
static JObject Serialize(uint itemId, StainId stain, bool apply, bool applyStain)
|
||||
=> new()
|
||||
{
|
||||
[nameof(Item.ItemId)] = itemId,
|
||||
[nameof(Item.Stain)] = stain.Value,
|
||||
["Apply"] = apply,
|
||||
["ApplyStain"] = applyStain,
|
||||
};
|
||||
|
||||
var ret = new JObject()
|
||||
{
|
||||
[nameof(MainHandId)] =
|
||||
Serialize(MainHandId, ModelData.MainHand.Stain, DoApplyEquip(EquipSlot.MainHand), DoApplyStain(EquipSlot.MainHand)),
|
||||
[nameof(OffHandId)] = Serialize(OffHandId, ModelData.OffHand.Stain, DoApplyEquip(EquipSlot.OffHand),
|
||||
DoApplyStain(EquipSlot.OffHand)),
|
||||
};
|
||||
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var armor = Armor(slot);
|
||||
ret[slot.ToString()] = Serialize(armor.ItemId, armor.Stain, DoApplyEquip(slot), DoApplyStain(slot));
|
||||
}
|
||||
|
||||
ret[nameof(Hat)] = Hat.ToJObject("Show", "Apply");
|
||||
ret[nameof(Weapon)] = Weapon.ToJObject("Show", "Apply");
|
||||
ret[nameof(Visor)] = Visor.ToJObject("IsToggled", "Apply");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public JObject SerializeCustomize()
|
||||
{
|
||||
var ret = new JObject()
|
||||
{
|
||||
[nameof(ModelId)] = ModelId,
|
||||
};
|
||||
var customize = ModelData.Customize;
|
||||
foreach (var idx in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
var data = customize[idx];
|
||||
ret[idx.ToString()] = new JObject()
|
||||
{
|
||||
["Value"] = data.Value,
|
||||
["Apply"] = true,
|
||||
};
|
||||
}
|
||||
|
||||
ret[nameof(Wetness)] = Wetness.ToJObject("IsWet", "Apply");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Design LoadDesign(ItemManager items, JObject json, out bool changes)
|
||||
{
|
||||
var version = json[nameof(FileVersion)]?.ToObject<int>() ?? 0;
|
||||
return version switch
|
||||
{
|
||||
1 => LoadDesignV1(items, json, out changes),
|
||||
_ => throw new Exception("The design to be loaded has no valid Version."),
|
||||
};
|
||||
}
|
||||
|
||||
internal static Design LoadDesignV1(ItemManager items, JObject json, out bool changes)
|
||||
{
|
||||
static string[] ParseTags(JObject json)
|
||||
{
|
||||
var tags = json["Tags"]?.ToObject<string[]>() ?? Array.Empty<string>();
|
||||
return tags.OrderBy(t => t).Distinct().ToArray();
|
||||
}
|
||||
|
||||
var design = new Design(items)
|
||||
{
|
||||
CreationDate = json["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate"),
|
||||
Identifier = json["Identifier"]?.ToObject<Guid>() ?? throw new ArgumentNullException("Identifier"),
|
||||
Name = new LowerString(json["Name"]?.ToObject<string>() ?? throw new ArgumentNullException("Name")),
|
||||
Description = json["Description"]?.ToObject<string>() ?? string.Empty,
|
||||
Tags = ParseTags(json),
|
||||
};
|
||||
|
||||
changes = LoadEquip(items, json["Equipment"], design);
|
||||
changes |= LoadCustomize(json["Customize"], design);
|
||||
return design;
|
||||
}
|
||||
|
||||
internal static bool LoadEquip(ItemManager items, JToken? equip, Design design)
|
||||
{
|
||||
if (equip == null)
|
||||
return true;
|
||||
|
||||
static (uint, StainId, bool, bool) ParseItem(EquipSlot slot, JToken? item)
|
||||
{
|
||||
var id = item?["ItemId"]?.ToObject<uint>() ?? ItemManager.NothingId(slot);
|
||||
var stain = (StainId)(item?["Stain"]?.ToObject<byte>() ?? 0);
|
||||
var apply = item?["Apply"]?.ToObject<bool>() ?? false;
|
||||
var applyStain = item?["ApplyStain"]?.ToObject<bool>() ?? false;
|
||||
return (id, stain, apply, applyStain);
|
||||
}
|
||||
|
||||
var changes = false;
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var (id, stain, apply, applyStain) = ParseItem(slot, equip[slot.ToString()]);
|
||||
changes |= !design.SetArmor(items, slot, id);
|
||||
changes |= !design.SetStain(slot, stain);
|
||||
design.SetApplyEquip(slot, apply);
|
||||
design.SetApplyStain(slot, applyStain);
|
||||
}
|
||||
|
||||
var main = equip["MainHand"];
|
||||
if (main == null)
|
||||
{
|
||||
changes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = main["ItemId"]?.ToObject<uint>() ?? items.DefaultSword.RowId;
|
||||
var stain = (StainId)(main["Stain"]?.ToObject<byte>() ?? 0);
|
||||
var apply = main["Apply"]?.ToObject<bool>() ?? false;
|
||||
var applyStain = main["ApplyStain"]?.ToObject<bool>() ?? false;
|
||||
changes |= !design.SetMainhand(items, id);
|
||||
changes |= !design.SetStain(EquipSlot.MainHand, stain);
|
||||
design.SetApplyEquip(EquipSlot.MainHand, apply);
|
||||
design.SetApplyStain(EquipSlot.MainHand, applyStain);
|
||||
}
|
||||
|
||||
var off = equip["OffHand"];
|
||||
if (off == null)
|
||||
{
|
||||
changes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = off["ItemId"]?.ToObject<uint>() ?? ItemManager.NothingId(design.MainhandType.Offhand());
|
||||
var stain = (StainId)(off["Stain"]?.ToObject<byte>() ?? 0);
|
||||
var apply = off["Apply"]?.ToObject<bool>() ?? false;
|
||||
var applyStain = off["ApplyStain"]?.ToObject<bool>() ?? false;
|
||||
changes |= !design.SetOffhand(items, id);
|
||||
changes |= !design.SetStain(EquipSlot.OffHand, stain);
|
||||
design.SetApplyEquip(EquipSlot.OffHand, apply);
|
||||
design.SetApplyStain(EquipSlot.OffHand, applyStain);
|
||||
}
|
||||
|
||||
design.Hat = QuadBool.FromJObject(equip["Hat"], "Show", "Apply", QuadBool.NullFalse);
|
||||
design.Weapon = QuadBool.FromJObject(equip["Weapon"], "Show", "Apply", QuadBool.NullFalse);
|
||||
design.Visor = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
internal static bool LoadCustomize(JToken? json, Design design)
|
||||
{
|
||||
if (json == null)
|
||||
return true;
|
||||
|
||||
ref var customize = ref design.ModelData.Customize;
|
||||
foreach (var idx in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
var tok = json[idx.ToString()];
|
||||
var data = (CustomizeValue)(tok?["Value"]?.ToObject<byte>() ?? 0);
|
||||
var apply = tok?["Apply"]?.ToObject<bool>() ?? false;
|
||||
customize[idx] = data;
|
||||
design.SetApplyCustomize(idx, apply);
|
||||
}
|
||||
|
||||
design.Wetness = QuadBool.FromJObject(json["Wetness"], "IsWet", "Apply", QuadBool.NullFalse);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void MigrateBase64(ItemManager items, string base64)
|
||||
{
|
||||
var data = DesignBase64Migration.MigrateBase64(base64, out var applyEquip, out var applyCustomize, out var writeProtected, out var wet,
|
||||
out var hat,
|
||||
out var visor, out var weapon);
|
||||
UpdateMainhand(items, data.MainHand);
|
||||
UpdateOffhand(items, data.OffHand);
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
UpdateArmor(items, slot, data.Armor(slot), true);
|
||||
ModelData.Customize = data.Customize;
|
||||
ApplyEquip = applyEquip;
|
||||
ApplyCustomize = applyCustomize;
|
||||
WriteProtected = writeProtected;
|
||||
Wetness = wet;
|
||||
Hat = hat;
|
||||
Visor = visor;
|
||||
Weapon = weapon;
|
||||
}
|
||||
|
||||
public static Design CreateTemporaryFromBase64(ItemManager items, string base64, bool customize, bool equip)
|
||||
{
|
||||
var ret = new Design(items);
|
||||
ret.MigrateBase64(items, base64);
|
||||
if (!customize)
|
||||
ret.ApplyCustomize = 0;
|
||||
if (!equip)
|
||||
ret.ApplyEquip = 0;
|
||||
ret.Wetness = ret.Wetness.SetEnabled(customize);
|
||||
ret.Visor = ret.Visor.SetEnabled(equip);
|
||||
ret.Hat = ret.Hat.SetEnabled(equip);
|
||||
ret.Weapon = ret.Weapon.SetEnabled(equip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Outdated.
|
||||
public string CreateOldBase64()
|
||||
=> DesignBase64Migration.CreateOldBase64(in ModelData, ApplyEquip, ApplyCustomize, Wetness == QuadBool.True, Hat.ForcedValue,
|
||||
Hat.Enabled,
|
||||
Visor.ForcedValue, Visor.Enabled, Weapon.ForcedValue, Weapon.Enabled, WriteProtected, 1f);
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.DesignFile(this);
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
using var j = new JsonTextWriter(writer)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
};
|
||||
var obj = JsonSerialize();
|
||||
obj.WriteTo(j);
|
||||
}
|
||||
|
||||
public string LogName(string fileName)
|
||||
=> Path.GetFileNameWithoutExtension(fileName);
|
||||
}
|
||||
|
|
@ -1,430 +0,0 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public class DesignData
|
||||
{
|
||||
internal ModelData ModelData;
|
||||
public FullEquipType MainhandType { get; internal set; }
|
||||
|
||||
public uint Head = ItemManager.NothingId(EquipSlot.Head);
|
||||
public uint Body = ItemManager.NothingId(EquipSlot.Body);
|
||||
public uint Hands = ItemManager.NothingId(EquipSlot.Hands);
|
||||
public uint Legs = ItemManager.NothingId(EquipSlot.Legs);
|
||||
public uint Feet = ItemManager.NothingId(EquipSlot.Feet);
|
||||
public uint Ears = ItemManager.NothingId(EquipSlot.Ears);
|
||||
public uint Neck = ItemManager.NothingId(EquipSlot.Neck);
|
||||
public uint Wrists = ItemManager.NothingId(EquipSlot.Wrists);
|
||||
public uint RFinger = ItemManager.NothingId(EquipSlot.RFinger);
|
||||
public uint LFinger = ItemManager.NothingId(EquipSlot.RFinger);
|
||||
public uint MainHandId;
|
||||
public uint OffHandId;
|
||||
|
||||
public string HeadName = ItemManager.Nothing;
|
||||
public string BodyName = ItemManager.Nothing;
|
||||
public string HandsName = ItemManager.Nothing;
|
||||
public string LegsName = ItemManager.Nothing;
|
||||
public string FeetName = ItemManager.Nothing;
|
||||
public string EarsName = ItemManager.Nothing;
|
||||
public string NeckName = ItemManager.Nothing;
|
||||
public string WristsName = ItemManager.Nothing;
|
||||
public string RFingerName = ItemManager.Nothing;
|
||||
public string LFingerName = ItemManager.Nothing;
|
||||
public string MainhandName;
|
||||
public string OffhandName;
|
||||
|
||||
public DesignData(ItemManager items)
|
||||
{
|
||||
MainHandId = items.DefaultSword.RowId;
|
||||
(_, var set, var type, var variant, MainhandName, MainhandType) = items.Resolve(MainHandId, items.DefaultSword);
|
||||
|
||||
ModelData = new ModelData(new CharacterWeapon(set, type, variant, 0));
|
||||
OffHandId = ItemManager.NothingId(MainhandType.Offhand());
|
||||
(_, ModelData.OffHand.Set, ModelData.OffHand.Type, ModelData.OffHand.Variant, OffhandName, _) =
|
||||
items.Resolve(OffHandId, MainhandType);
|
||||
}
|
||||
|
||||
public uint ModelId
|
||||
=> ModelData.ModelId;
|
||||
|
||||
public Item Armor(EquipSlot slot)
|
||||
{
|
||||
return slot switch
|
||||
{
|
||||
EquipSlot.Head => new Item(HeadName, Head, ModelData.Head),
|
||||
EquipSlot.Body => new Item(BodyName, Body, ModelData.Body),
|
||||
EquipSlot.Hands => new Item(HandsName, Hands, ModelData.Hands),
|
||||
EquipSlot.Legs => new Item(LegsName, Legs, ModelData.Legs),
|
||||
EquipSlot.Feet => new Item(FeetName, Feet, ModelData.Feet),
|
||||
EquipSlot.Ears => new Item(EarsName, Ears, ModelData.Ears),
|
||||
EquipSlot.Neck => new Item(NeckName, Neck, ModelData.Neck),
|
||||
EquipSlot.Wrists => new Item(WristsName, Wrists, ModelData.Wrists),
|
||||
EquipSlot.RFinger => new Item(RFingerName, RFinger, ModelData.RFinger),
|
||||
EquipSlot.LFinger => new Item(LFingerName, LFinger, ModelData.LFinger),
|
||||
_ => throw new Exception("Invalid equip slot for item."),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public Weapon WeaponMain
|
||||
=> new(MainhandName, MainHandId, ModelData.MainHand, MainhandType);
|
||||
|
||||
public Weapon WeaponOff
|
||||
=> Weapon.Offhand(OffhandName, OffHandId, ModelData.OffHand, MainhandType);
|
||||
|
||||
public CustomizeValue GetCustomize(CustomizeIndex idx)
|
||||
=> ModelData.Customize[idx];
|
||||
|
||||
internal bool SetCustomize(CustomizeIndex idx, CustomizeValue value)
|
||||
=> ModelData.Customize.Set(idx, value);
|
||||
|
||||
internal bool SetArmor(ItemManager items, EquipSlot slot, uint itemId, Lumina.Excel.GeneratedSheets.Item? item = null)
|
||||
{
|
||||
var (valid, set, variant, name) = items.Resolve(slot, itemId, item);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
return SetArmor(slot, set, variant, name, itemId);
|
||||
}
|
||||
|
||||
internal bool SetArmor(EquipSlot slot, Item item)
|
||||
=> SetArmor(slot, item.ModelBase, item.Variant, item.Name, item.ItemId);
|
||||
|
||||
internal bool UpdateArmor(ItemManager items, EquipSlot slot, CharacterArmor armor, bool force)
|
||||
{
|
||||
var (valid, id, name) = items.Identify(slot, armor.Set, armor.Variant);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
return SetArmor(slot, armor.Set, armor.Variant, name, id) | SetStain(slot, armor.Stain);
|
||||
}
|
||||
|
||||
internal bool SetMainhand(ItemManager items, uint mainId, Lumina.Excel.GeneratedSheets.Item? main = null)
|
||||
{
|
||||
if (mainId == MainHandId)
|
||||
return false;
|
||||
|
||||
var (valid, set, weapon, variant, name, type) = items.Resolve(mainId, main);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
var fixOffhand = type.Offhand() != MainhandType.Offhand();
|
||||
|
||||
MainHandId = mainId;
|
||||
MainhandName = name;
|
||||
MainhandType = type;
|
||||
ModelData.MainHand.Set = set;
|
||||
ModelData.MainHand.Type = weapon;
|
||||
ModelData.MainHand.Variant = variant;
|
||||
if (fixOffhand)
|
||||
SetOffhand(items, ItemManager.NothingId(type.Offhand()));
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetOffhand(ItemManager items, uint offId, Lumina.Excel.GeneratedSheets.Item? off = null)
|
||||
{
|
||||
if (offId == OffHandId)
|
||||
return false;
|
||||
|
||||
var (valid, set, weapon, variant, name, type) = items.Resolve(offId, MainhandType, off);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
OffHandId = offId;
|
||||
OffhandName = name;
|
||||
ModelData.OffHand.Set = set;
|
||||
ModelData.OffHand.Type = weapon;
|
||||
ModelData.OffHand.Variant = variant;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool UpdateMainhand(ItemManager items, CharacterWeapon weapon)
|
||||
{
|
||||
if (weapon.Value == ModelData.MainHand.Value)
|
||||
return false;
|
||||
|
||||
var (valid, id, name, type) = items.Identify(EquipSlot.MainHand, weapon.Set, weapon.Type, (byte)weapon.Variant);
|
||||
if (!valid || id == MainHandId)
|
||||
return false;
|
||||
|
||||
var fixOffhand = type.Offhand() != MainhandType.Offhand();
|
||||
|
||||
MainHandId = id;
|
||||
MainhandName = name;
|
||||
MainhandType = type;
|
||||
ModelData.MainHand.Set = weapon.Set;
|
||||
ModelData.MainHand.Type = weapon.Type;
|
||||
ModelData.MainHand.Variant = weapon.Variant;
|
||||
ModelData.MainHand.Stain = weapon.Stain;
|
||||
if (fixOffhand)
|
||||
SetOffhand(items, ItemManager.NothingId(type.Offhand()));
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool UpdateOffhand(ItemManager items, CharacterWeapon weapon)
|
||||
{
|
||||
if (weapon.Value == ModelData.OffHand.Value)
|
||||
return false;
|
||||
|
||||
var (valid, id, name, _) = items.Identify(EquipSlot.OffHand, weapon.Set, weapon.Type, (byte)weapon.Variant, MainhandType);
|
||||
if (!valid || id == OffHandId)
|
||||
return false;
|
||||
|
||||
OffHandId = id;
|
||||
OffhandName = name;
|
||||
ModelData.OffHand.Set = weapon.Set;
|
||||
ModelData.OffHand.Type = weapon.Type;
|
||||
ModelData.OffHand.Variant = weapon.Variant;
|
||||
ModelData.OffHand.Stain = weapon.Stain;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetStain(EquipSlot slot, StainId id)
|
||||
{
|
||||
return slot switch
|
||||
{
|
||||
EquipSlot.MainHand => SetIfDifferent(ref ModelData.MainHand.Stain, id),
|
||||
EquipSlot.OffHand => SetIfDifferent(ref ModelData.OffHand.Stain, id),
|
||||
EquipSlot.Head => SetIfDifferent(ref ModelData.Head.Stain, id),
|
||||
EquipSlot.Body => SetIfDifferent(ref ModelData.Body.Stain, id),
|
||||
EquipSlot.Hands => SetIfDifferent(ref ModelData.Hands.Stain, id),
|
||||
EquipSlot.Legs => SetIfDifferent(ref ModelData.Legs.Stain, id),
|
||||
EquipSlot.Feet => SetIfDifferent(ref ModelData.Feet.Stain, id),
|
||||
EquipSlot.Ears => SetIfDifferent(ref ModelData.Ears.Stain, id),
|
||||
EquipSlot.Neck => SetIfDifferent(ref ModelData.Neck.Stain, id),
|
||||
EquipSlot.Wrists => SetIfDifferent(ref ModelData.Wrists.Stain, id),
|
||||
EquipSlot.RFinger => SetIfDifferent(ref ModelData.RFinger.Stain, id),
|
||||
EquipSlot.LFinger => SetIfDifferent(ref ModelData.LFinger.Stain, id),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
internal static bool SetIfDifferent<T>(ref T old, T value) where T : IEquatable<T>
|
||||
{
|
||||
if (old.Equals(value))
|
||||
return false;
|
||||
|
||||
old = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool SetArmor(EquipSlot slot, SetId set, byte variant, string name, uint id)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref ModelData.Head.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Head.Variant, variant);
|
||||
changes |= HeadName != name;
|
||||
HeadName = name;
|
||||
changes |= Head != id;
|
||||
Head = id;
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref ModelData.Body.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Body.Variant, variant);
|
||||
changes |= BodyName != name;
|
||||
BodyName = name;
|
||||
changes |= Body != id;
|
||||
Body = id;
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref ModelData.Hands.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Hands.Variant, variant);
|
||||
changes |= HandsName != name;
|
||||
HandsName = name;
|
||||
changes |= Hands != id;
|
||||
Hands = id;
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref ModelData.Legs.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Legs.Variant, variant);
|
||||
changes |= LegsName != name;
|
||||
LegsName = name;
|
||||
changes |= Legs != id;
|
||||
Legs = id;
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref ModelData.Feet.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Feet.Variant, variant);
|
||||
changes |= FeetName != name;
|
||||
FeetName = name;
|
||||
changes |= Feet != id;
|
||||
Feet = id;
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref ModelData.Ears.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Ears.Variant, variant);
|
||||
changes |= EarsName != name;
|
||||
EarsName = name;
|
||||
changes |= Ears != id;
|
||||
Ears = id;
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref ModelData.Neck.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Neck.Variant, variant);
|
||||
changes |= NeckName != name;
|
||||
NeckName = name;
|
||||
changes |= Neck != id;
|
||||
Neck = id;
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref ModelData.Wrists.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Wrists.Variant, variant);
|
||||
changes |= WristsName != name;
|
||||
WristsName = name;
|
||||
changes |= Wrists != id;
|
||||
Wrists = id;
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref ModelData.RFinger.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.RFinger.Variant, variant);
|
||||
changes |= RFingerName != name;
|
||||
RFingerName = name;
|
||||
changes |= RFinger != id;
|
||||
RFinger = id;
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref ModelData.LFinger.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.LFinger.Variant, variant);
|
||||
changes |= LFingerName != name;
|
||||
LFingerName = name;
|
||||
changes |= LFinger != id;
|
||||
LFinger = id;
|
||||
return changes;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DesignBase64Migration
|
||||
{
|
||||
public const int Base64Size = 91;
|
||||
|
||||
public static ModelData MigrateBase64(string base64, out EquipFlag equipFlags, out CustomizeFlag customizeFlags,
|
||||
out bool writeinternal, out QuadBool wet, out QuadBool hat, out QuadBool visor, out QuadBool weapon)
|
||||
{
|
||||
static void CheckSize(int length, int requiredLength)
|
||||
{
|
||||
if (length != requiredLength)
|
||||
throw new Exception(
|
||||
$"Can not parse Base64 string into CharacterSave:\n\tInvalid size {length} instead of {requiredLength}.");
|
||||
}
|
||||
|
||||
byte applicationFlags;
|
||||
ushort equipFlagsS;
|
||||
var bytes = Convert.FromBase64String(base64);
|
||||
hat = QuadBool.Null;
|
||||
visor = QuadBool.Null;
|
||||
weapon = QuadBool.Null;
|
||||
switch (bytes[0])
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
CheckSize(bytes.Length, 86);
|
||||
applicationFlags = bytes[1];
|
||||
equipFlagsS = BitConverter.ToUInt16(bytes, 2);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
CheckSize(bytes.Length, Base64Size);
|
||||
applicationFlags = bytes[1];
|
||||
equipFlagsS = BitConverter.ToUInt16(bytes, 2);
|
||||
hat = hat.SetValue((bytes[90] & 0x01) == 0);
|
||||
visor = visor.SetValue((bytes[90] & 0x10) != 0);
|
||||
weapon = weapon.SetValue((bytes[90] & 0x02) == 0);
|
||||
break;
|
||||
}
|
||||
default: throw new Exception($"Can not parse Base64 string into design for migration:\n\tInvalid Version {bytes[0]}.");
|
||||
}
|
||||
|
||||
customizeFlags = (applicationFlags & 0x01) != 0 ? CustomizeFlagExtensions.All : 0;
|
||||
wet = (applicationFlags & 0x02) != 0 ? QuadBool.True : QuadBool.NullFalse;
|
||||
hat = hat.SetEnabled((applicationFlags & 0x04) != 0);
|
||||
weapon = weapon.SetEnabled((applicationFlags & 0x08) != 0);
|
||||
visor = visor.SetEnabled((applicationFlags & 0x10) != 0);
|
||||
writeinternal = (applicationFlags & 0x20) != 0;
|
||||
|
||||
equipFlags = 0;
|
||||
equipFlags |= (equipFlagsS & 0x0001) != 0 ? EquipFlag.Mainhand | EquipFlag.MainhandStain : 0;
|
||||
equipFlags |= (equipFlagsS & 0x0002) != 0 ? EquipFlag.Offhand | EquipFlag.OffhandStain : 0;
|
||||
var flag = 0x0002u;
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
flag <<= 1;
|
||||
equipFlags |= (equipFlagsS & flag) != 0 ? slot.ToFlag() | slot.ToStainFlag() : 0;
|
||||
}
|
||||
|
||||
var data = new ModelData();
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = bytes)
|
||||
{
|
||||
data.Customize.Load(*(Customize*) (ptr + 4));
|
||||
var cur = (CharacterWeapon*)(ptr + 30);
|
||||
data.MainHand = cur[0];
|
||||
data.OffHand = cur[1];
|
||||
var eq = (CharacterArmor*)(cur + 2);
|
||||
foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex())
|
||||
{
|
||||
var mdl = eq[idx];
|
||||
data.SetPiece(slot, mdl.Set, mdl.Variant, mdl.Stain, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static unsafe string CreateOldBase64(in ModelData save, EquipFlag equipFlags, CustomizeFlag customizeFlags, bool wet, bool hat,
|
||||
bool setHat, bool visor, bool setVisor, bool weapon, bool setWeapon, bool writeinternal, float alpha)
|
||||
{
|
||||
var data = stackalloc byte[Base64Size];
|
||||
data[0] = 2;
|
||||
data[1] = (byte)((customizeFlags == CustomizeFlagExtensions.All ? 0x01 : 0)
|
||||
| (wet ? 0x02 : 0)
|
||||
| (setHat ? 0x04 : 0)
|
||||
| (setWeapon ? 0x08 : 0)
|
||||
| (setVisor ? 0x10 : 0)
|
||||
| (writeinternal ? 0x20 : 0));
|
||||
data[2] = (byte)((equipFlags.HasFlag(EquipFlag.Mainhand) ? 0x01 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Offhand) ? 0x02 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Head) ? 0x04 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Body) ? 0x08 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Hands) ? 0x10 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Legs) ? 0x20 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Feet) ? 0x40 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Ears) ? 0x80 : 0));
|
||||
data[3] = (byte)((equipFlags.HasFlag(EquipFlag.Neck) ? 0x01 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Wrist) ? 0x02 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.RFinger) ? 0x04 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.LFinger) ? 0x08 : 0));
|
||||
save.Customize.Load(*(Customize*) (data + 4));
|
||||
((CharacterWeapon*)(data + 30))[0] = save.MainHand;
|
||||
((CharacterWeapon*)(data + 30))[1] = save.OffHand;
|
||||
((CharacterArmor*)(data + 44))[0] = save.Head;
|
||||
((CharacterArmor*)(data + 44))[1] = save.Body;
|
||||
((CharacterArmor*)(data + 44))[2] = save.Hands;
|
||||
((CharacterArmor*)(data + 44))[3] = save.Legs;
|
||||
((CharacterArmor*)(data + 44))[4] = save.Feet;
|
||||
((CharacterArmor*)(data + 44))[5] = save.Ears;
|
||||
((CharacterArmor*)(data + 44))[6] = save.Neck;
|
||||
((CharacterArmor*)(data + 44))[7] = save.Wrists;
|
||||
((CharacterArmor*)(data + 44))[8] = save.RFinger;
|
||||
((CharacterArmor*)(data + 44))[9] = save.LFinger;
|
||||
*(float*)(data + 84) = 1f;
|
||||
data[88] = (byte)((hat ? 0x01 : 0)
|
||||
| (visor ? 0x10 : 0)
|
||||
| (weapon ? 0x02 : 0));
|
||||
|
||||
return Convert.ToBase64String(new Span<byte>(data, Base64Size));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Filesystem;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
|
||||
{
|
||||
public static string GetDesignFileSystemFile(DalamudPluginInterface pi)
|
||||
=> Path.Combine(pi.GetPluginConfigDirectory(), "sort_order.json");
|
||||
|
||||
public readonly string DesignFileSystemFile;
|
||||
private readonly SaveService _saveService;
|
||||
private readonly DesignManager _designManager;
|
||||
|
||||
public DesignFileSystem(DesignManager designManager, DalamudPluginInterface pi, SaveService saveService)
|
||||
{
|
||||
DesignFileSystemFile = GetDesignFileSystemFile(pi);
|
||||
_designManager = designManager;
|
||||
_saveService = saveService;
|
||||
_designManager.DesignChange += OnDataChange;
|
||||
Changed += OnChange;
|
||||
Reload();
|
||||
}
|
||||
|
||||
private void Reload()
|
||||
{
|
||||
if (Load(new FileInfo(DesignFileSystemFile), _designManager.Designs, DesignToIdentifier, DesignToName))
|
||||
_saveService.ImmediateSave(this);
|
||||
|
||||
Glamourer.Log.Debug("Reloaded design filesystem.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_designManager.DesignChange -= OnDataChange;
|
||||
}
|
||||
|
||||
public struct CreationDate : ISortMode<Design>
|
||||
{
|
||||
public string Name
|
||||
=> "Creation Date (Older First)";
|
||||
|
||||
public string Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date.";
|
||||
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.CreationDate));
|
||||
}
|
||||
|
||||
public struct InverseCreationDate : ISortMode<Design>
|
||||
{
|
||||
public string Name
|
||||
=> "Creation Date (Newer First)";
|
||||
|
||||
public string Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date.";
|
||||
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.CreationDate));
|
||||
}
|
||||
|
||||
private void OnChange(FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3)
|
||||
{
|
||||
if (type != FileSystemChangeType.Reload)
|
||||
_saveService.QueueSave(this);
|
||||
}
|
||||
|
||||
private void OnDataChange(DesignManager.DesignChangeType type, Design design, object? data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DesignManager.DesignChangeType.Created:
|
||||
var originalName = design.Name.Text.FixName();
|
||||
var name = originalName;
|
||||
var counter = 1;
|
||||
while (Find(name, out _))
|
||||
name = $"{originalName} ({++counter})";
|
||||
|
||||
CreateLeaf(Root, name, design);
|
||||
break;
|
||||
case DesignManager.DesignChangeType.Deleted:
|
||||
if (FindLeaf(design, out var leaf))
|
||||
Delete(leaf);
|
||||
break;
|
||||
case DesignManager.DesignChangeType.ReloadedAll:
|
||||
Reload();
|
||||
break;
|
||||
case DesignManager.DesignChangeType.Renamed when data is string oldName:
|
||||
var old = oldName.FixName();
|
||||
if (Find(old, out var child) && child is not Folder)
|
||||
Rename(child, design.Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Used for saving and loading.
|
||||
private static string DesignToIdentifier(Design design)
|
||||
=> design.Identifier.ToString();
|
||||
|
||||
private static string DesignToName(Design design)
|
||||
=> design.Name.Text.FixName();
|
||||
|
||||
private static bool DesignHasDefaultPath(Design design, string fullPath)
|
||||
{
|
||||
var regex = new Regex($@"^{Regex.Escape(DesignToName(design))}( \(\d+\))?$");
|
||||
return regex.IsMatch(fullPath);
|
||||
}
|
||||
|
||||
private static (string, bool) SaveDesign(Design design, string fullPath)
|
||||
// Only save pairs with non-default paths.
|
||||
=> DesignHasDefaultPath(design, fullPath)
|
||||
? (string.Empty, false)
|
||||
: (DesignToIdentifier(design), true);
|
||||
|
||||
// Search the entire filesystem for the leaf corresponding to a design.
|
||||
public bool FindLeaf(Design design, [NotNullWhen(true)] out Leaf? leaf)
|
||||
{
|
||||
leaf = Root.GetAllDescendants(ISortMode<Design>.Lexicographical)
|
||||
.OfType<Leaf>()
|
||||
.FirstOrDefault(l => l.Value == design);
|
||||
return leaf != null;
|
||||
}
|
||||
|
||||
internal static void MigrateOldPaths(DalamudPluginInterface pi, Dictionary<string, string> oldPaths)
|
||||
{
|
||||
if (oldPaths.Count == 0)
|
||||
return;
|
||||
|
||||
var file = GetDesignFileSystemFile(pi);
|
||||
try
|
||||
{
|
||||
JObject jObject;
|
||||
if (File.Exists(file))
|
||||
{
|
||||
var text = File.ReadAllText(file);
|
||||
jObject = JObject.Parse(text);
|
||||
var dict = jObject["Data"]?.ToObject<Dictionary<string, string>>();
|
||||
if (dict != null)
|
||||
foreach (var (key, value) in dict)
|
||||
oldPaths.TryAdd(key, value);
|
||||
|
||||
jObject["Data"] = JToken.FromObject(oldPaths);
|
||||
}
|
||||
else
|
||||
{
|
||||
jObject = new JObject
|
||||
{
|
||||
["Data"] = JToken.FromObject(oldPaths),
|
||||
["EmptyFolders"] = JToken.FromObject(Array.Empty<string>()),
|
||||
};
|
||||
}
|
||||
|
||||
var data = jObject.ToString(Formatting.Indented);
|
||||
File.WriteAllText(file, data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not migrate old folder paths to new version:\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.DesignFileSystem;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
SaveToFile(writer, SaveDesign, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,372 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Utility;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public class DesignManager
|
||||
{
|
||||
public const string DesignFolderName = "designs";
|
||||
public readonly string DesignFolder;
|
||||
|
||||
private readonly CustomizationManager _customizations;
|
||||
private readonly ItemManager _items;
|
||||
private readonly SaveService _saveService;
|
||||
private readonly List<Design> _designs = new();
|
||||
|
||||
public enum DesignChangeType
|
||||
{
|
||||
Created,
|
||||
Deleted,
|
||||
ReloadedAll,
|
||||
Renamed,
|
||||
ChangedDescription,
|
||||
AddedTag,
|
||||
RemovedTag,
|
||||
ChangedTag,
|
||||
Customize,
|
||||
Equip,
|
||||
Weapon,
|
||||
Stain,
|
||||
ApplyCustomize,
|
||||
ApplyEquip,
|
||||
Other,
|
||||
}
|
||||
|
||||
public delegate void DesignChangeDelegate(DesignChangeType type, Design design, object? changeData = null);
|
||||
|
||||
public event DesignChangeDelegate? DesignChange;
|
||||
|
||||
public IReadOnlyList<Design> Designs
|
||||
=> _designs;
|
||||
|
||||
public DesignManager(DalamudPluginInterface pi, SaveService saveService, ItemManager items)
|
||||
{
|
||||
_saveService = saveService;
|
||||
_items = items;
|
||||
DesignFolder = SetDesignFolder(pi);
|
||||
LoadDesigns();
|
||||
MigrateOldDesigns(pi, Path.Combine(new DirectoryInfo(DesignFolder).Parent!.FullName, "Designs.json"));
|
||||
}
|
||||
|
||||
private static string SetDesignFolder(DalamudPluginInterface pi)
|
||||
{
|
||||
var ret = Path.Combine(pi.GetPluginConfigDirectory(), DesignFolderName);
|
||||
if (Directory.Exists(ret))
|
||||
return ret;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not create design folder directory at {ret}:\n{ex}");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void LoadDesigns()
|
||||
{
|
||||
_designs.Clear();
|
||||
List<(Design, string)> invalidNames = new();
|
||||
var skipped = 0;
|
||||
foreach (var file in new DirectoryInfo(DesignFolder).EnumerateFiles("*.json", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(file.FullName);
|
||||
var data = JObject.Parse(text);
|
||||
var design = Design.LoadDesign(_items, data, out var changes);
|
||||
if (design.Identifier.ToString() != Path.GetFileNameWithoutExtension(file.Name))
|
||||
invalidNames.Add((design, file.FullName));
|
||||
if (_designs.Any(f => f.Identifier == design.Identifier))
|
||||
throw new Exception($"Identifier {design.Identifier} was not unique.");
|
||||
|
||||
// TODO something when changed?
|
||||
design.Index = _designs.Count;
|
||||
_designs.Add(design);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not load design, skipped:\n{ex}");
|
||||
++skipped;
|
||||
}
|
||||
}
|
||||
|
||||
var failed = 0;
|
||||
foreach (var (design, name) in invalidNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
var correctName = _saveService.FileNames.DesignFile(design);
|
||||
File.Move(name, correctName, false);
|
||||
Glamourer.Log.Information($"Moved invalid design file from {Path.GetFileName(name)} to {Path.GetFileName(correctName)}.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
++failed;
|
||||
Glamourer.Log.Error($"Failed to move invalid design file from {Path.GetFileName(name)}:\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidNames.Count > 0)
|
||||
Glamourer.Log.Information(
|
||||
$"Moved {invalidNames.Count - failed} designs to correct names.{(failed > 0 ? $" Failed to move {failed} designs to correct names." : string.Empty)}");
|
||||
|
||||
Glamourer.Log.Information(
|
||||
$"Loaded {_designs.Count} designs.{(skipped > 0 ? $" Skipped loading {skipped} designs due to errors." : string.Empty)}");
|
||||
DesignChange?.Invoke(DesignChangeType.ReloadedAll, null!);
|
||||
}
|
||||
|
||||
public Design Create(string name)
|
||||
{
|
||||
var design = new Design(_items)
|
||||
{
|
||||
CreationDate = DateTimeOffset.UtcNow,
|
||||
Identifier = CreateNewGuid(),
|
||||
Index = _designs.Count,
|
||||
Name = name,
|
||||
};
|
||||
_designs.Add(design);
|
||||
Glamourer.Log.Debug($"Added new design {design.Identifier}.");
|
||||
_saveService.ImmediateSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Created, design);
|
||||
return design;
|
||||
}
|
||||
|
||||
public void Delete(Design design)
|
||||
{
|
||||
_designs.RemoveAt(design.Index);
|
||||
foreach (var d in _designs.Skip(design.Index + 1))
|
||||
--d.Index;
|
||||
_saveService.ImmediateDelete(design);
|
||||
}
|
||||
|
||||
public void Rename(Design design, string newName)
|
||||
{
|
||||
var oldName = design.Name.Text;
|
||||
_saveService.ImmediateDelete(design);
|
||||
design.Name = newName;
|
||||
Glamourer.Log.Debug($"Renamed design {design.Identifier}.");
|
||||
_saveService.ImmediateSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Renamed, design, oldName);
|
||||
}
|
||||
|
||||
public void ChangeDescription(Design design, string description)
|
||||
{
|
||||
design.Description = description;
|
||||
Glamourer.Log.Debug($"Changed description of design {design.Identifier}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.ChangedDescription, design);
|
||||
}
|
||||
|
||||
public void AddTag(Design design, string tag)
|
||||
{
|
||||
if (design.Tags.Contains(tag))
|
||||
return;
|
||||
|
||||
design.Tags = design.Tags.Append(tag).OrderBy(t => t).ToArray();
|
||||
var idx = design.Tags.IndexOf(tag);
|
||||
Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.AddedTag, design);
|
||||
}
|
||||
|
||||
public void RemoveTag(Design design, string tag)
|
||||
{
|
||||
var idx = design.Tags.IndexOf(tag);
|
||||
if (idx >= 0)
|
||||
RemoveTag(design, idx);
|
||||
}
|
||||
|
||||
public void RemoveTag(Design design, int tagIdx)
|
||||
{
|
||||
var oldTag = design.Tags[tagIdx];
|
||||
design.Tags = design.Tags.Take(tagIdx).Concat(design.Tags.Skip(tagIdx + 1)).ToArray();
|
||||
Glamourer.Log.Debug($"Removed tag {oldTag} at {tagIdx} from design {design.Identifier}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.RemovedTag, design);
|
||||
}
|
||||
|
||||
|
||||
public void RenameTag(Design design, int tagIdx, string newTag)
|
||||
{
|
||||
var oldTag = design.Tags[tagIdx];
|
||||
if (oldTag == newTag)
|
||||
return;
|
||||
|
||||
design.Tags[tagIdx] = newTag;
|
||||
Array.Sort(design.Tags);
|
||||
Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.ChangedTag, design);
|
||||
}
|
||||
|
||||
public void ChangeCustomize(Design design, CustomizeIndex idx, CustomizeValue value)
|
||||
{
|
||||
var old = design.GetCustomize(idx);
|
||||
if (!design.SetCustomize(idx, value))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Changed customize {idx} in design {design.Identifier} from {old.Value} to {value.Value}");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Customize, design, idx);
|
||||
}
|
||||
|
||||
public void ChangeApplyCustomize(Design design, CustomizeIndex idx, bool value)
|
||||
{
|
||||
if (!design.SetApplyCustomize(idx, value))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Set applying of customization {idx} to {value}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.ApplyCustomize, design, idx);
|
||||
}
|
||||
|
||||
public void ChangeEquip(Design design, EquipSlot slot, uint itemId, Lumina.Excel.GeneratedSheets.Item? item = null)
|
||||
{
|
||||
var old = design.Armor(slot);
|
||||
if (!design.SetArmor(_items, slot, itemId, item))
|
||||
return;
|
||||
|
||||
var n = design.Armor(slot);
|
||||
Glamourer.Log.Debug(
|
||||
$"Set {slot} equipment piece in design {design.Identifier} from {old.Name} ({old.ItemId}) to {n.Name} ({n.ItemId}).");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Equip, design, slot);
|
||||
}
|
||||
|
||||
public void ChangeWeapon(Design design, uint itemId, EquipSlot offhand, Lumina.Excel.GeneratedSheets.Item? item = null)
|
||||
{
|
||||
var (old, change, n) = offhand == EquipSlot.OffHand
|
||||
? (design.WeaponOff, design.SetOffhand(_items, itemId, item), design.WeaponOff)
|
||||
: (design.WeaponMain, design.SetMainhand(_items, itemId, item), design.WeaponMain);
|
||||
if (!change)
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug(
|
||||
$"Set {offhand} weapon in design {design.Identifier} from {old.Name} ({old.ItemId}) to {n.Name} ({n.ItemId}).");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Weapon, design, offhand);
|
||||
}
|
||||
|
||||
public void ChangeApplyEquip(Design design, EquipSlot slot, bool value)
|
||||
{
|
||||
if (!design.SetApplyEquip(slot, value))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Set applying of {slot} equipment piece to {value}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.ApplyEquip, design, slot);
|
||||
}
|
||||
|
||||
public void ChangeStain(Design design, EquipSlot slot, StainId stain)
|
||||
{
|
||||
if (!design.SetStain(slot, stain))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stain.Value}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Stain, design, slot);
|
||||
}
|
||||
|
||||
public void ChangeApplyStain(Design design, EquipSlot slot, bool value)
|
||||
{
|
||||
if (!design.SetApplyStain(slot, value))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Set applying of stain of {slot} equipment piece to {value}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Stain, design, slot);
|
||||
}
|
||||
|
||||
private Guid CreateNewGuid()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var guid = Guid.NewGuid();
|
||||
if (_designs.All(d => d.Identifier != guid))
|
||||
return guid;
|
||||
}
|
||||
}
|
||||
|
||||
private bool Add(Design design, string? message)
|
||||
{
|
||||
if (_designs.Any(d => d == design || d.Identifier == design.Identifier))
|
||||
return false;
|
||||
|
||||
design.Index = _designs.Count;
|
||||
_designs.Add(design);
|
||||
if (!message.IsNullOrEmpty())
|
||||
Glamourer.Log.Debug(message);
|
||||
_saveService.ImmediateSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Created, design);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void MigrateOldDesigns(DalamudPluginInterface pi, string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
return;
|
||||
|
||||
var errors = 0;
|
||||
var successes = 0;
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(filePath);
|
||||
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(text) ?? new Dictionary<string, string>();
|
||||
var migratedFileSystemPaths = new Dictionary<string, string>(dict.Count);
|
||||
foreach (var (name, base64) in dict)
|
||||
{
|
||||
try
|
||||
{
|
||||
var actualName = Path.GetFileName(name);
|
||||
var design = new Design(_items)
|
||||
{
|
||||
CreationDate = DateTimeOffset.UtcNow,
|
||||
Identifier = CreateNewGuid(),
|
||||
Name = actualName,
|
||||
};
|
||||
design.MigrateBase64(_items, base64);
|
||||
Add(design, $"Migrated old design to {design.Identifier}.");
|
||||
migratedFileSystemPaths.Add(design.Identifier.ToString(), name);
|
||||
++successes;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not migrate design {name}:\n{ex}");
|
||||
++errors;
|
||||
}
|
||||
}
|
||||
|
||||
DesignFileSystem.MigrateOldPaths(pi, migratedFileSystemPaths);
|
||||
Glamourer.Log.Information($"Successfully migrated {successes} old designs. Failed to migrate {errors} designs.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not migrate old design file {filePath}:\n{e}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Move(filePath, Path.ChangeExtension(filePath, ".json.bak"));
|
||||
Glamourer.Log.Information($"Moved migrated design file {filePath} to backup file.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not move migrated design file {filePath} to backup file:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
using System;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
[Flags]
|
||||
public enum EquipFlag : uint
|
||||
{
|
||||
Head = 0x00000001,
|
||||
Body = 0x00000002,
|
||||
Hands = 0x00000004,
|
||||
Legs = 0x00000008,
|
||||
Feet = 0x00000010,
|
||||
Ears = 0x00000020,
|
||||
Neck = 0x00000040,
|
||||
Wrist = 0x00000080,
|
||||
RFinger = 0x00000100,
|
||||
LFinger = 0x00000200,
|
||||
Mainhand = 0x00000400,
|
||||
Offhand = 0x00000800,
|
||||
HeadStain = 0x00001000,
|
||||
BodyStain = 0x00002000,
|
||||
HandsStain = 0x00004000,
|
||||
LegsStain = 0x00008000,
|
||||
FeetStain = 0x00010000,
|
||||
EarsStain = 0x00020000,
|
||||
NeckStain = 0x00040000,
|
||||
WristStain = 0x00080000,
|
||||
RFingerStain = 0x00100000,
|
||||
LFingerStain = 0x00200000,
|
||||
MainhandStain = 0x00400000,
|
||||
OffhandStain = 0x00800000,
|
||||
}
|
||||
|
||||
public static class EquipFlagExtensions
|
||||
{
|
||||
public const EquipFlag All = (EquipFlag)(((uint)EquipFlag.OffhandStain << 1) - 1);
|
||||
public const int NumEquipFlags = 24;
|
||||
|
||||
public static EquipFlag ToFlag(this EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => EquipFlag.Mainhand,
|
||||
EquipSlot.OffHand => EquipFlag.Offhand,
|
||||
EquipSlot.Head => EquipFlag.Head,
|
||||
EquipSlot.Body => EquipFlag.Body,
|
||||
EquipSlot.Hands => EquipFlag.Hands,
|
||||
EquipSlot.Legs => EquipFlag.Legs,
|
||||
EquipSlot.Feet => EquipFlag.Feet,
|
||||
EquipSlot.Ears => EquipFlag.Ears,
|
||||
EquipSlot.Neck => EquipFlag.Neck,
|
||||
EquipSlot.Wrists => EquipFlag.Wrist,
|
||||
EquipSlot.RFinger => EquipFlag.RFinger,
|
||||
EquipSlot.LFinger => EquipFlag.LFinger,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
public static EquipFlag ToStainFlag(this EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => EquipFlag.MainhandStain,
|
||||
EquipSlot.OffHand => EquipFlag.OffhandStain,
|
||||
EquipSlot.Head => EquipFlag.HeadStain,
|
||||
EquipSlot.Body => EquipFlag.BodyStain,
|
||||
EquipSlot.Hands => EquipFlag.HandsStain,
|
||||
EquipSlot.Legs => EquipFlag.LegsStain,
|
||||
EquipSlot.Feet => EquipFlag.FeetStain,
|
||||
EquipSlot.Ears => EquipFlag.EarsStain,
|
||||
EquipSlot.Neck => EquipFlag.NeckStain,
|
||||
EquipSlot.Wrists => EquipFlag.WristStain,
|
||||
EquipSlot.RFinger => EquipFlag.RFingerStain,
|
||||
EquipSlot.LFinger => EquipFlag.LFingerStain,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,496 +0,0 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.String.Functions;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public struct ItemPiece
|
||||
{
|
||||
public string Name;
|
||||
public uint ItemId;
|
||||
public SetId ModelId;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ModelFlags : ushort
|
||||
{
|
||||
HatVisible = 0x01,
|
||||
WeaponVisible = 0x02,
|
||||
VisorToggled = 0x04,
|
||||
}
|
||||
|
||||
public struct ModelData
|
||||
{
|
||||
public readonly unsafe ModelData Clone()
|
||||
{
|
||||
var data = new ModelData(MainHand);
|
||||
fixed (void* ptr = &this)
|
||||
{
|
||||
MemoryUtility.MemCpyUnchecked(&data, ptr, sizeof(ModelData));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public Customize Customize = Customize.Default;
|
||||
public ModelFlags Flags = ModelFlags.HatVisible | ModelFlags.WeaponVisible;
|
||||
public CharacterWeapon MainHand;
|
||||
public CharacterWeapon OffHand = CharacterWeapon.Empty;
|
||||
|
||||
public uint ModelId = 0;
|
||||
public CharacterArmor Head = CharacterArmor.Empty;
|
||||
public CharacterArmor Body = CharacterArmor.Empty;
|
||||
public CharacterArmor Hands = CharacterArmor.Empty;
|
||||
public CharacterArmor Legs = CharacterArmor.Empty;
|
||||
public CharacterArmor Feet = CharacterArmor.Empty;
|
||||
public CharacterArmor Ears = CharacterArmor.Empty;
|
||||
public CharacterArmor Neck = CharacterArmor.Empty;
|
||||
public CharacterArmor Wrists = CharacterArmor.Empty;
|
||||
public CharacterArmor RFinger = CharacterArmor.Empty;
|
||||
public CharacterArmor LFinger = CharacterArmor.Empty;
|
||||
|
||||
public ModelData(CharacterWeapon mainHand)
|
||||
=> MainHand = mainHand;
|
||||
|
||||
public readonly CharacterArmor Armor(EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => MainHand.ToArmor(),
|
||||
EquipSlot.OffHand => OffHand.ToArmor(),
|
||||
EquipSlot.Head => Head,
|
||||
EquipSlot.Body => Body,
|
||||
EquipSlot.Hands => Hands,
|
||||
EquipSlot.Legs => Legs,
|
||||
EquipSlot.Feet => Feet,
|
||||
EquipSlot.Ears => Ears,
|
||||
EquipSlot.Neck => Neck,
|
||||
EquipSlot.Wrists => Wrists,
|
||||
EquipSlot.RFinger => RFinger,
|
||||
EquipSlot.LFinger => LFinger,
|
||||
_ => CharacterArmor.Empty,
|
||||
};
|
||||
|
||||
public readonly CharacterWeapon Piece(EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => MainHand,
|
||||
EquipSlot.OffHand => OffHand,
|
||||
EquipSlot.Head => Head.ToWeapon(),
|
||||
EquipSlot.Body => Body.ToWeapon(),
|
||||
EquipSlot.Hands => Hands.ToWeapon(),
|
||||
EquipSlot.Legs => Legs.ToWeapon(),
|
||||
EquipSlot.Feet => Feet.ToWeapon(),
|
||||
EquipSlot.Ears => Ears.ToWeapon(),
|
||||
EquipSlot.Neck => Neck.ToWeapon(),
|
||||
EquipSlot.Wrists => Wrists.ToWeapon(),
|
||||
EquipSlot.RFinger => RFinger.ToWeapon(),
|
||||
EquipSlot.LFinger => LFinger.ToWeapon(),
|
||||
_ => CharacterWeapon.Empty,
|
||||
};
|
||||
|
||||
public bool SetPiece(EquipSlot slot, SetId model, byte variant, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes |= SetIfDifferent(ref MainHand.Set, model);
|
||||
changes |= SetIfDifferent(ref MainHand.Variant, variant);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes |= SetIfDifferent(ref OffHand.Set, model);
|
||||
changes |= SetIfDifferent(ref OffHand.Variant, variant);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref Head.Set, model);
|
||||
changes |= SetIfDifferent(ref Head.Variant, variant);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref Body.Set, model);
|
||||
changes |= SetIfDifferent(ref Body.Variant, variant);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref Hands.Set, model);
|
||||
changes |= SetIfDifferent(ref Hands.Variant, variant);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref Legs.Set, model);
|
||||
changes |= SetIfDifferent(ref Legs.Variant, variant);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref Feet.Set, model);
|
||||
changes |= SetIfDifferent(ref Feet.Variant, variant);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref Ears.Set, model);
|
||||
changes |= SetIfDifferent(ref Ears.Variant, variant);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref Neck.Set, model);
|
||||
changes |= SetIfDifferent(ref Neck.Variant, variant);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref Wrists.Set, model);
|
||||
changes |= SetIfDifferent(ref Wrists.Variant, variant);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref RFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref RFinger.Variant, variant);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref LFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref LFinger.Variant, variant);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetPiece(EquipSlot slot, SetId model, WeaponType type, byte variant, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes |= SetIfDifferent(ref MainHand.Set, model);
|
||||
changes |= SetIfDifferent(ref MainHand.Type, type);
|
||||
changes |= SetIfDifferent(ref MainHand.Variant, variant);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes |= SetIfDifferent(ref OffHand.Set, model);
|
||||
changes |= SetIfDifferent(ref OffHand.Type, type);
|
||||
changes |= SetIfDifferent(ref OffHand.Variant, variant);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref Head.Set, model);
|
||||
changes |= SetIfDifferent(ref Head.Variant, variant);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref Body.Set, model);
|
||||
changes |= SetIfDifferent(ref Body.Variant, variant);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref Hands.Set, model);
|
||||
changes |= SetIfDifferent(ref Hands.Variant, variant);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref Legs.Set, model);
|
||||
changes |= SetIfDifferent(ref Legs.Variant, variant);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref Feet.Set, model);
|
||||
changes |= SetIfDifferent(ref Feet.Variant, variant);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref Ears.Set, model);
|
||||
changes |= SetIfDifferent(ref Ears.Variant, variant);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref Neck.Set, model);
|
||||
changes |= SetIfDifferent(ref Neck.Variant, variant);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref Wrists.Set, model);
|
||||
changes |= SetIfDifferent(ref Wrists.Variant, variant);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref RFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref RFinger.Variant, variant);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref LFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref LFinger.Variant, variant);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetPiece(EquipSlot slot, SetId model, byte variant, StainId stain, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes |= SetIfDifferent(ref MainHand.Set, model);
|
||||
changes |= SetIfDifferent(ref MainHand.Variant, variant);
|
||||
changes |= SetIfDifferent(ref MainHand.Stain, stain);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes |= SetIfDifferent(ref OffHand.Set, model);
|
||||
changes |= SetIfDifferent(ref OffHand.Variant, variant);
|
||||
changes |= SetIfDifferent(ref OffHand.Stain, stain);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref Head.Set, model);
|
||||
changes |= SetIfDifferent(ref Head.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Head.Stain, stain);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref Body.Set, model);
|
||||
changes |= SetIfDifferent(ref Body.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Body.Stain, stain);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref Hands.Set, model);
|
||||
changes |= SetIfDifferent(ref Hands.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Hands.Stain, stain);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref Legs.Set, model);
|
||||
changes |= SetIfDifferent(ref Legs.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Legs.Stain, stain);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref Feet.Set, model);
|
||||
changes |= SetIfDifferent(ref Feet.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Feet.Stain, stain);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref Ears.Set, model);
|
||||
changes |= SetIfDifferent(ref Ears.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Ears.Stain, stain);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref Neck.Set, model);
|
||||
changes |= SetIfDifferent(ref Neck.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Neck.Stain, stain);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref Wrists.Set, model);
|
||||
changes |= SetIfDifferent(ref Wrists.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Wrists.Stain, stain);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref RFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref RFinger.Variant, variant);
|
||||
changes |= SetIfDifferent(ref RFinger.Stain, stain);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref LFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref LFinger.Variant, variant);
|
||||
changes |= SetIfDifferent(ref LFinger.Stain, stain);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetPiece(EquipSlot slot, CharacterWeapon weapon, out CharacterWeapon ret)
|
||||
=> SetPiece(slot, weapon.Set, weapon.Type, (byte)weapon.Variant, weapon.Stain, out ret);
|
||||
|
||||
public bool SetPiece(EquipSlot slot, CharacterArmor armor, out CharacterWeapon ret)
|
||||
=> SetPiece(slot, armor.Set, armor.Variant, armor.Stain, out ret);
|
||||
|
||||
public bool SetPiece(EquipSlot slot, SetId model, WeaponType type, byte variant, StainId stain, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes |= SetIfDifferent(ref MainHand.Set, model);
|
||||
changes |= SetIfDifferent(ref MainHand.Type, type);
|
||||
changes |= SetIfDifferent(ref MainHand.Variant, variant);
|
||||
changes |= SetIfDifferent(ref MainHand.Stain, stain);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes |= SetIfDifferent(ref OffHand.Set, model);
|
||||
changes |= SetIfDifferent(ref OffHand.Type, type);
|
||||
changes |= SetIfDifferent(ref OffHand.Variant, variant);
|
||||
changes |= SetIfDifferent(ref OffHand.Stain, stain);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref Head.Set, model);
|
||||
changes |= SetIfDifferent(ref Head.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Head.Stain, stain);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref Body.Set, model);
|
||||
changes |= SetIfDifferent(ref Body.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Body.Stain, stain);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref Hands.Set, model);
|
||||
changes |= SetIfDifferent(ref Hands.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Hands.Stain, stain);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref Legs.Set, model);
|
||||
changes |= SetIfDifferent(ref Legs.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Legs.Stain, stain);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref Feet.Set, model);
|
||||
changes |= SetIfDifferent(ref Feet.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Feet.Stain, stain);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref Ears.Set, model);
|
||||
changes |= SetIfDifferent(ref Ears.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Ears.Stain, stain);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref Neck.Set, model);
|
||||
changes |= SetIfDifferent(ref Neck.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Neck.Stain, stain);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref Wrists.Set, model);
|
||||
changes |= SetIfDifferent(ref Wrists.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Wrists.Stain, stain);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref RFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref RFinger.Variant, variant);
|
||||
changes |= SetIfDifferent(ref RFinger.Stain, stain);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref LFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref LFinger.Variant, variant);
|
||||
changes |= SetIfDifferent(ref LFinger.Stain, stain);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public StainId Stain(EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => MainHand.Stain,
|
||||
EquipSlot.OffHand => OffHand.Stain,
|
||||
EquipSlot.Head => Head.Stain,
|
||||
EquipSlot.Body => Body.Stain,
|
||||
EquipSlot.Hands => Hands.Stain,
|
||||
EquipSlot.Legs => Legs.Stain,
|
||||
EquipSlot.Feet => Feet.Stain,
|
||||
EquipSlot.Ears => Ears.Stain,
|
||||
EquipSlot.Neck => Neck.Stain,
|
||||
EquipSlot.Wrists => Wrists.Stain,
|
||||
EquipSlot.RFinger => RFinger.Stain,
|
||||
EquipSlot.LFinger => LFinger.Stain,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
public bool SetStain(EquipSlot slot, StainId stain, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes = SetIfDifferent(ref MainHand.Stain, stain);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes = SetIfDifferent(ref OffHand.Stain, stain);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes = SetIfDifferent(ref Head.Stain, stain);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes = SetIfDifferent(ref Body.Stain, stain);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes = SetIfDifferent(ref Hands.Stain, stain);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes = SetIfDifferent(ref Legs.Stain, stain);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes = SetIfDifferent(ref Feet.Stain, stain);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes = SetIfDifferent(ref Ears.Stain, stain);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes = SetIfDifferent(ref Neck.Stain, stain);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes = SetIfDifferent(ref Wrists.Stain, stain);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes = SetIfDifferent(ref RFinger.Stain, stain);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes = SetIfDifferent(ref LFinger.Stain, stain);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SetIfDifferent<T>(ref T old, T value) where T : IEquatable<T>
|
||||
{
|
||||
if (old.Equals(value))
|
||||
return false;
|
||||
|
||||
old = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
using Dalamud.Utility;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public readonly struct Item
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly uint ItemId;
|
||||
public readonly CharacterArmor Model;
|
||||
|
||||
public SetId ModelBase
|
||||
=> Model.Set;
|
||||
|
||||
public byte Variant
|
||||
=> Model.Variant;
|
||||
|
||||
public StainId Stain
|
||||
=> Model.Stain;
|
||||
|
||||
public Item WithStain(StainId id)
|
||||
=> new(Name, ItemId, Model with { Stain = id });
|
||||
|
||||
public Item(string name, uint itemId, CharacterArmor armor)
|
||||
{
|
||||
Name = name;
|
||||
ItemId = itemId;
|
||||
Model.Set = armor.Set;
|
||||
Model.Variant = armor.Variant;
|
||||
Model.Stain = armor.Stain;
|
||||
}
|
||||
|
||||
public Item(Lumina.Excel.GeneratedSheets.Item item)
|
||||
{
|
||||
Name = string.Intern(item.Name.ToDalamudString().TextValue);
|
||||
ItemId = item.RowId;
|
||||
Model.Set = (SetId)item.ModelMain;
|
||||
Model.Variant = (byte)(item.ModelMain >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Weapon
|
||||
{
|
||||
public readonly string Name = string.Empty;
|
||||
public readonly uint ItemId;
|
||||
public readonly FullEquipType Type;
|
||||
public readonly bool Valid;
|
||||
public readonly CharacterWeapon Model;
|
||||
|
||||
public SetId ModelBase
|
||||
=> Model.Set;
|
||||
|
||||
public WeaponType WeaponBase
|
||||
=> Model.Type;
|
||||
|
||||
public byte Variant
|
||||
=> (byte)Model.Variant;
|
||||
|
||||
public StainId Stain
|
||||
=> Model.Stain;
|
||||
|
||||
|
||||
public Weapon WithStain(StainId id)
|
||||
=> new(Name, ItemId, Model with { Stain = id }, Type);
|
||||
|
||||
public Weapon(string name, uint itemId, CharacterWeapon weapon, FullEquipType type)
|
||||
{
|
||||
Name = name;
|
||||
ItemId = itemId;
|
||||
Type = type;
|
||||
Valid = true;
|
||||
Model.Set = weapon.Set;
|
||||
Model.Type = weapon.Type;
|
||||
Model.Variant = (byte)weapon.Variant;
|
||||
Model.Stain = weapon.Stain;
|
||||
}
|
||||
|
||||
public static Weapon Offhand(string name, uint itemId, CharacterWeapon weapon, FullEquipType type)
|
||||
{
|
||||
var offType = type.Offhand();
|
||||
return offType is FullEquipType.Unknown
|
||||
? new Weapon()
|
||||
: new Weapon(name, itemId, weapon, offType);
|
||||
}
|
||||
|
||||
public Weapon(Lumina.Excel.GeneratedSheets.Item item, bool offhand)
|
||||
{
|
||||
Name = string.Intern(item.Name.ToDalamudString().TextValue);
|
||||
ItemId = item.RowId;
|
||||
Type = item.ToEquipType();
|
||||
var offType = Type.Offhand();
|
||||
var model = offhand && offType == Type ? item.ModelSub : item.ModelMain;
|
||||
Valid = Type.ToSlot() switch
|
||||
{
|
||||
EquipSlot.MainHand when !offhand => true,
|
||||
EquipSlot.MainHand when offhand && offType == Type => true,
|
||||
EquipSlot.OffHand when offhand => true,
|
||||
_ => false,
|
||||
};
|
||||
Model.Set = (SetId)model;
|
||||
Model.Type = (WeaponType)(model >> 16);
|
||||
Model.Variant = (byte)(model >> 32);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
using System;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||
|
||||
namespace Glamourer;
|
||||
|
||||
public class DrawObjectManager : IDisposable
|
||||
{
|
||||
private readonly ItemManager _items;
|
||||
private readonly ActorService _actors;
|
||||
private readonly ActiveDesign.Manager _manager;
|
||||
private readonly PenumbraAttach _penumbra;
|
||||
|
||||
public DrawObjectManager(ItemManager items, ActorService actors, ActiveDesign.Manager manager,
|
||||
PenumbraAttach penumbra)
|
||||
{
|
||||
_items = items;
|
||||
_actors = actors;
|
||||
_manager = manager;
|
||||
_penumbra = penumbra;
|
||||
|
||||
//_interop.EquipUpdate += FixEquipment;
|
||||
_penumbra.CreatingCharacterBase += ApplyActiveDesign;
|
||||
}
|
||||
|
||||
private void FixEquipment(DrawObject drawObject, EquipSlot slot, ref CharacterArmor item)
|
||||
{
|
||||
var customize = drawObject.Customize;
|
||||
var (changed, newArmor) = _items.ResolveRestrictedGear(item, slot, customize.Race, customize.Gender);
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
Glamourer.Log.Verbose(
|
||||
$"Invalid armor {item.Set.Value}-{item.Variant} for {customize.Gender} {customize.Race} changed to {newArmor.Set.Value}-{newArmor.Variant}.");
|
||||
item = newArmor;
|
||||
}
|
||||
|
||||
private unsafe void ApplyActiveDesign(nint gameObjectPtr, string collectionName, nint modelIdPtr, nint customizePtr, nint equipDataPtr)
|
||||
{
|
||||
var gameObject = (Actor)gameObjectPtr;
|
||||
ref var modelId = ref *(uint*)modelIdPtr;
|
||||
// Do not apply anything if the game object model id does not correspond to the draw object model id.
|
||||
// This is the case if the actor is transformed to a different creature.
|
||||
if (gameObject.ModelId != modelId)
|
||||
return;
|
||||
|
||||
var identifier = _actors.AwaitedService.FromObject((GameObject*)gameObjectPtr, out _, true, true, false);
|
||||
if (!identifier.IsValid || !_manager.TryGetValue(identifier, out var design))
|
||||
return;
|
||||
|
||||
// Compare game object customize data against draw object customize data for transformations.
|
||||
// Apply customization if they correspond and there is customization to apply.
|
||||
var gameObjectCustomize = gameObject.Customize;
|
||||
var customize = new Customize(*(CustomizeData*)customizePtr);
|
||||
if (gameObjectCustomize.Equals(customize))
|
||||
customize.Load(design.ModelData.Customize);
|
||||
|
||||
// Compare game object equip data against draw object equip data for transformations.
|
||||
// Apply each piece of equip that should be applied if they correspond.
|
||||
var gameObjectEquip = gameObject.Equip;
|
||||
var equip = new CharacterEquip((CharacterArmor*)equipDataPtr);
|
||||
if (gameObjectEquip.Equals(equip))
|
||||
{
|
||||
foreach (var slot in EquipSlotExtensions.EquipmentSlots)
|
||||
{
|
||||
(_, equip[slot]) =
|
||||
_items.ResolveRestrictedGear(design.ModelData.Armor(slot), slot, customize.Race, customize.Gender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_penumbra.CreatingCharacterBase -= ApplyActiveDesign;
|
||||
//_interop.EquipUpdate -= FixEquipment;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
using Glamourer.Interop;
|
||||
using Glamourer.Structs;
|
||||
|
||||
namespace Glamourer.Fixed;
|
||||
|
||||
public struct FixedCondition
|
||||
{
|
||||
private const ulong _territoryFlag = 1ul << 32;
|
||||
private const ulong _jobFlag = 1ul << 33;
|
||||
private ulong _data;
|
||||
|
||||
public static FixedCondition TerritoryCondition(ushort territoryType)
|
||||
=> new() { _data = territoryType | _territoryFlag };
|
||||
|
||||
public static FixedCondition JobCondition(JobGroup group)
|
||||
=> new() { _data = group.Id | _jobFlag };
|
||||
|
||||
public bool Check(Actor actor)
|
||||
{
|
||||
//if ((_data & (_territoryFlag | _jobFlag)) == 0)
|
||||
// return true;
|
||||
//
|
||||
//if ((_data & _territoryFlag) != 0)
|
||||
// return Dalamud.ClientState.TerritoryType == (ushort)_data;
|
||||
//
|
||||
//if (actor && GameData.JobGroups(Dalamud.GameData).TryGetValue((ushort)_data, out var group) && group.Fits(actor.Job))
|
||||
// return true;
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> _data.ToString();
|
||||
}
|
||||
|
|
@ -1,315 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Dalamud.Logging;
|
||||
using System.Text;
|
||||
using Dalamud.Utility;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Glamourer.Designs;
|
||||
|
||||
namespace Glamourer.Fixed;
|
||||
|
||||
public class FixedDesign
|
||||
{
|
||||
public const int CurrentVersion = 0;
|
||||
|
||||
public string Name { get; private set; }
|
||||
public bool Enabled;
|
||||
public List<ActorIdentifier> Actors;
|
||||
public List<(FixedCondition, Design)> Customization;
|
||||
public List<(FixedCondition, Design)> Equipment;
|
||||
public List<(FixedCondition, Design)> Weapons;
|
||||
|
||||
public FixedDesign(string name)
|
||||
{
|
||||
Name = name;
|
||||
Actors = new List<ActorIdentifier>();
|
||||
Customization = new List<(FixedCondition, Design)>();
|
||||
Equipment = new List<(FixedCondition, Design)>();
|
||||
Weapons = new List<(FixedCondition, Design)>();
|
||||
}
|
||||
|
||||
public static FixedDesign? Load(JObject j)
|
||||
{
|
||||
try
|
||||
{
|
||||
var name = j[nameof(Name)]?.Value<string>();
|
||||
if (name.IsNullOrEmpty())
|
||||
return null;
|
||||
|
||||
var version = j["Version"]?.Value<int>();
|
||||
if (version == null)
|
||||
return null;
|
||||
|
||||
return version switch
|
||||
{
|
||||
CurrentVersion => LoadCurrentVersion(j, name),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PluginLog.Error($"Error loading fixed design:\n{e}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static FixedDesign? LoadCurrentVersion(JObject j, string name)
|
||||
{
|
||||
var enabled = j[nameof(Enabled)]?.Value<bool>() ?? false;
|
||||
var ret = new FixedDesign(name)
|
||||
{
|
||||
Enabled = enabled,
|
||||
};
|
||||
|
||||
var actors = j[nameof(Actors)];
|
||||
//foreach(var pair in actors?.Children().)
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void Save(FileInfo file)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var s = file.Exists ? file.Open(FileMode.Truncate) : file.Open(FileMode.CreateNew);
|
||||
using var w = new StreamWriter(s, Encoding.UTF8);
|
||||
using var j = new JsonTextWriter(w)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
};
|
||||
j.WriteStartObject();
|
||||
j.WritePropertyName(nameof(Name));
|
||||
j.WriteValue(Name);
|
||||
j.WritePropertyName("Version");
|
||||
j.WriteValue(CurrentVersion);
|
||||
j.WritePropertyName(nameof(Enabled));
|
||||
j.WriteValue(Enabled);
|
||||
j.WritePropertyName(nameof(Actors));
|
||||
j.WriteStartArray();
|
||||
foreach (var actor in Actors)
|
||||
actor.ToJson().WriteTo(j);
|
||||
j.WriteEndArray();
|
||||
j.WritePropertyName(nameof(Customization));
|
||||
j.WriteStartArray();
|
||||
foreach (var (condition, design) in Customization)
|
||||
{
|
||||
j.WritePropertyName(condition.ToString());
|
||||
j.WriteValue(design.Name);
|
||||
}
|
||||
|
||||
j.WriteEndArray();
|
||||
j.WritePropertyName(nameof(Equipment));
|
||||
j.WriteStartArray();
|
||||
foreach (var (condition, design) in Equipment)
|
||||
{
|
||||
j.WritePropertyName(condition.ToString());
|
||||
j.WriteValue(design.Name);
|
||||
}
|
||||
|
||||
j.WriteEndArray();
|
||||
j.WritePropertyName(nameof(Weapons));
|
||||
j.WriteStartArray();
|
||||
foreach (var (condition, design) in Weapons)
|
||||
{
|
||||
j.WritePropertyName(condition.ToString());
|
||||
j.WriteValue(design.Name);
|
||||
}
|
||||
|
||||
j.WriteEndArray();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PluginLog.Error($"Could not save collection {Name}:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Load(FileInfo path, [NotNullWhen(true)] out FixedDesign? result)
|
||||
{
|
||||
result = null!;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class FixedDesigns : IDisposable
|
||||
{
|
||||
//public class FixedDesign
|
||||
//{
|
||||
// public string Name;
|
||||
// public JobGroup Jobs;
|
||||
// public Design Design;
|
||||
// public bool Enabled;
|
||||
//
|
||||
// public GlamourerConfig.FixedDesign ToSave()
|
||||
// => new()
|
||||
// {
|
||||
// Name = Name,
|
||||
// Path = Design.FullName(),
|
||||
// Enabled = Enabled,
|
||||
// JobGroups = Jobs.Id,
|
||||
// };
|
||||
//
|
||||
// public FixedDesign(string name, Design design, bool enabled, JobGroup jobs)
|
||||
// {
|
||||
// Name = name;
|
||||
// Design = design;
|
||||
// Enabled = enabled;
|
||||
// Jobs = jobs;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//public List<FixedDesign> Data;
|
||||
//public Dictionary<string, List<FixedDesign>> EnabledDesigns;
|
||||
//public readonly IReadOnlyDictionary<ushort, JobGroup> JobGroups;
|
||||
//
|
||||
//public bool EnableDesign(FixedDesign design)
|
||||
//{
|
||||
// var changes = !design.Enabled;
|
||||
//
|
||||
// if (!EnabledDesigns.TryGetValue(design.Name, out var designs))
|
||||
// {
|
||||
// EnabledDesigns[design.Name] = new List<FixedDesign> { design };
|
||||
// // TODO
|
||||
// changes = true;
|
||||
// }
|
||||
// else if (!designs.Contains(design))
|
||||
// {
|
||||
// designs.Add(design);
|
||||
// changes = true;
|
||||
// }
|
||||
//
|
||||
// design.Enabled = true;
|
||||
// // TODO
|
||||
// //if (Glamourer.Config.ApplyFixedDesigns)
|
||||
// //{
|
||||
// // var character =
|
||||
// // CharacterFactory.Convert(Dalamud.Objects.FirstOrDefault(o
|
||||
// // => o.ObjectKind == ObjectKind.Player && o.Name.ToString() == design.Name));
|
||||
// // if (character != null)
|
||||
// // OnPlayerChange(character);
|
||||
// //}
|
||||
//
|
||||
// return changes;
|
||||
//}
|
||||
//
|
||||
//public bool DisableDesign(FixedDesign design)
|
||||
//{
|
||||
// if (!design.Enabled)
|
||||
// return false;
|
||||
//
|
||||
// design.Enabled = false;
|
||||
// if (!EnabledDesigns.TryGetValue(design.Name, out var designs))
|
||||
// return false;
|
||||
// if (!designs.Remove(design))
|
||||
// return false;
|
||||
//
|
||||
// if (designs.Count == 0)
|
||||
// {
|
||||
// EnabledDesigns.Remove(design.Name);
|
||||
// // TODO
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
//}
|
||||
//
|
||||
//public FixedDesigns(DesignManager designs)
|
||||
//{
|
||||
// JobGroups = GameData.JobGroups(Dalamud.GameData);
|
||||
// Data = new List<FixedDesign>(Glamourer.Config.FixedDesigns.Count);
|
||||
// EnabledDesigns = new Dictionary<string, List<FixedDesign>>(Glamourer.Config.FixedDesigns.Count);
|
||||
// var changes = false;
|
||||
// for (var i = 0; i < Glamourer.Config.FixedDesigns.Count; ++i)
|
||||
// {
|
||||
// var save = Glamourer.Config.FixedDesigns[i];
|
||||
// if (designs.FileSystem.Find(save.Path, out var d) && d is Design design)
|
||||
// {
|
||||
// if (!JobGroups.TryGetValue((ushort)save.JobGroups, out var jobGroup))
|
||||
// jobGroup = JobGroups[1];
|
||||
// Data.Add(new FixedDesign(save.Name, design, save.Enabled, jobGroup));
|
||||
// if (save.Enabled)
|
||||
// changes |= EnableDesign(Data.Last());
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// PluginLog.Warning($"{save.Path} does not exist anymore, removing {save.Name} from fixed designs.");
|
||||
// Glamourer.Config.FixedDesigns.RemoveAt(i--);
|
||||
// changes = true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (changes)
|
||||
// Glamourer.Config.Save();
|
||||
//}
|
||||
//
|
||||
//private void OnPlayerChange(Character character)
|
||||
//{
|
||||
// //var name = character.Name.ToString();
|
||||
// //if (!EnabledDesigns.TryGetValue(name, out var designs))
|
||||
// // return;
|
||||
// //
|
||||
// //var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(character.ClassJob.Id));
|
||||
// //if (design == null)
|
||||
// // return;
|
||||
// //
|
||||
// //PluginLog.Debug("Redrawing {CharacterName} with {DesignName} for job {JobGroup}.", name, design.Design.FullName(),
|
||||
// // design.Jobs.Name);
|
||||
// //design.Design.Data.Apply(character);
|
||||
// //Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
|
||||
// //Glamourer.Penumbra.RedrawObject(character, RedrawType.Redraw, false);
|
||||
//}
|
||||
//
|
||||
//public void Add(string name, Design design, JobGroup group, bool enabled = false)
|
||||
//{
|
||||
// Data.Add(new FixedDesign(name, design, enabled, group));
|
||||
// Glamourer.Config.FixedDesigns.Add(Data.Last().ToSave());
|
||||
//
|
||||
// if (enabled)
|
||||
// EnableDesign(Data.Last());
|
||||
//
|
||||
// Glamourer.Config.Save();
|
||||
//}
|
||||
//
|
||||
//public void Remove(FixedDesign design)
|
||||
//{
|
||||
// var idx = Data.IndexOf(design);
|
||||
// if (idx < 0)
|
||||
// return;
|
||||
//
|
||||
// Data.RemoveAt(idx);
|
||||
// Glamourer.Config.FixedDesigns.RemoveAt(idx);
|
||||
// if (design.Enabled)
|
||||
// {
|
||||
// EnabledDesigns.Remove(design.Name);
|
||||
// // TODO
|
||||
// }
|
||||
//
|
||||
// Glamourer.Config.Save();
|
||||
//}
|
||||
//
|
||||
//public void Move(FixedDesign design, int newIdx)
|
||||
//{
|
||||
// if (newIdx < 0)
|
||||
// newIdx = 0;
|
||||
// if (newIdx >= Data.Count)
|
||||
// newIdx = Data.Count - 1;
|
||||
//
|
||||
// var idx = Data.IndexOf(design);
|
||||
// if (idx < 0 || idx == newIdx)
|
||||
// return;
|
||||
//
|
||||
// Data.RemoveAt(idx);
|
||||
// Data.Insert(newIdx, design);
|
||||
// Glamourer.Config.FixedDesigns.RemoveAt(idx);
|
||||
// Glamourer.Config.FixedDesigns.Insert(newIdx, design.ToSave());
|
||||
// Glamourer.Config.Save();
|
||||
//}
|
||||
//
|
||||
public void Dispose()
|
||||
{
|
||||
//Glamourer.Config.FixedDesigns = Data.Select(d => d.ToSave()).ToList();
|
||||
//Glamourer.Config.Save();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
using System.Reflection;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
|
||||
namespace Glamourer;
|
||||
|
||||
public partial class Glamourer : IDalamudPlugin
|
||||
{
|
||||
public string Name
|
||||
=> "Glamourer";
|
||||
|
||||
public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? string.Empty;
|
||||
|
||||
public static readonly string CommitHash =
|
||||
Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "Unknown";
|
||||
|
||||
|
||||
public static readonly Logger Log = new();
|
||||
public static ChatService ChatService { get; private set; } = null!;
|
||||
private readonly ServiceProvider _services;
|
||||
|
||||
public Glamourer(DalamudPluginInterface pluginInterface)
|
||||
{
|
||||
try
|
||||
{
|
||||
EventWrapper.ChangeLogger(Log);
|
||||
_services = ServiceManager.CreateProvider(pluginInterface, Log);
|
||||
ChatService = _services.GetRequiredService<ChatService>();
|
||||
_services.GetRequiredService<BackupService>();
|
||||
_services.GetRequiredService<GlamourerWindowSystem>();
|
||||
_services.GetRequiredService<CommandService>();
|
||||
_services.GetRequiredService<GlamourerIpc>();
|
||||
_services.GetRequiredService<ChangeCustomizeService>();
|
||||
_services.GetRequiredService<JobService>();
|
||||
_services.GetRequiredService<UpdateSlotService>();
|
||||
_services.GetRequiredService<VisorService>();
|
||||
_services.GetRequiredService<WeaponService>();
|
||||
_services.GetRequiredService<RedrawManager>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_services?.Dispose();
|
||||
}
|
||||
|
||||
//private static GameObject? GetPlayer(string name)
|
||||
//{
|
||||
// var lowerName = name.ToLowerInvariant();
|
||||
// return lowerName switch
|
||||
// {
|
||||
// "" => null,
|
||||
// "<me>" => Dalamud.Objects[Interface.GPoseObjectId] ?? Dalamud.ClientState.LocalPlayer,
|
||||
// "self" => Dalamud.Objects[Interface.GPoseObjectId] ?? Dalamud.ClientState.LocalPlayer,
|
||||
// "<t>" => Dalamud.Targets.Target,
|
||||
// "target" => Dalamud.Targets.Target,
|
||||
// "<f>" => Dalamud.Targets.FocusTarget,
|
||||
// "focus" => Dalamud.Targets.FocusTarget,
|
||||
// "<mo>" => Dalamud.Targets.MouseOverTarget,
|
||||
// "mouseover" => Dalamud.Targets.MouseOverTarget,
|
||||
// _ => Dalamud.Objects.LastOrDefault(
|
||||
// a => string.Equals(a.Name.ToString(), lowerName, StringComparison.InvariantCultureIgnoreCase)),
|
||||
// };
|
||||
//}
|
||||
//
|
||||
//public void CopyToClipboard(Character player)
|
||||
//{
|
||||
// var save = new CharacterSave();
|
||||
// save.LoadCharacter(player);
|
||||
// ImGui.SetClipboardText(save.ToBase64());
|
||||
//}
|
||||
//
|
||||
//public void ApplyCommand(Character player, string target)
|
||||
//{
|
||||
// CharacterSave? save = null;
|
||||
// if (target.ToLowerInvariant() == "clipboard")
|
||||
// try
|
||||
// {
|
||||
// save = CharacterSave.FromString(ImGui.GetClipboardText());
|
||||
// }
|
||||
// catch (Exception)
|
||||
// {
|
||||
// Dalamud.Chat.PrintError("Clipboard does not contain a valid customization string.");
|
||||
// }
|
||||
// else if (!Designs.FileSystem.Find(target, out var child) || child is not Design d)
|
||||
// Dalamud.Chat.PrintError("The given path to a saved design does not exist or does not point to a design.");
|
||||
// else
|
||||
// save = d.Data;
|
||||
//
|
||||
// save?.Apply(player);
|
||||
// Penumbra.UpdateCharacters(player);
|
||||
//}
|
||||
//
|
||||
//public void SaveCommand(Character player, string path)
|
||||
//{
|
||||
// var save = new CharacterSave();
|
||||
// save.LoadCharacter(player);
|
||||
// try
|
||||
// {
|
||||
// var (folder, name) = Designs.FileSystem.CreateAllFolders(path);
|
||||
// var design = new Design(folder, name) { Data = save };
|
||||
// folder.FindOrAddChild(design);
|
||||
// Designs.Designs.Add(design.FullName(), design.Data);
|
||||
// Designs.SaveToFile();
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// Dalamud.Chat.PrintError("Could not save file:");
|
||||
// Dalamud.Chat.PrintError($" {e.Message}");
|
||||
// }
|
||||
//}
|
||||
//
|
||||
public void OnGlamour(string command, string arguments)
|
||||
{
|
||||
//static void PrintHelp()
|
||||
//{
|
||||
// Dalamud.Chat.Print("Usage:");
|
||||
// Dalamud.Chat.Print($" {HelpString}");
|
||||
//}
|
||||
|
||||
//arguments = arguments.Trim();
|
||||
//if (!arguments.Any())
|
||||
//{
|
||||
// PrintHelp();
|
||||
// return;
|
||||
//}
|
||||
//
|
||||
//var split = arguments.Split(new[]
|
||||
//{
|
||||
// ',',
|
||||
//}, 3, StringSplitOptions.RemoveEmptyEntries);
|
||||
//
|
||||
//if (split.Length < 2)
|
||||
//{
|
||||
// PrintHelp();
|
||||
// return;
|
||||
//}
|
||||
//
|
||||
//var player = GetPlayer(split[1]) as Character;
|
||||
//if (player == null)
|
||||
//{
|
||||
// Dalamud.Chat.Print($"Could not find object for {split[1]} or it was not a Character.");
|
||||
// return;
|
||||
//}
|
||||
//
|
||||
//switch (split[0].ToLowerInvariant())
|
||||
//{
|
||||
// case "copy":
|
||||
// CopyToClipboard(player);
|
||||
// return;
|
||||
// case "apply":
|
||||
// {
|
||||
// if (split.Length < 3)
|
||||
// {
|
||||
// Dalamud.Chat.Print("Applying requires a name for the save to be applied or 'clipboard'.");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// ApplyCommand(player, split[2]);
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
// case "save":
|
||||
// {
|
||||
// if (split.Length < 3)
|
||||
// {
|
||||
// Dalamud.Chat.Print("Saving requires a name for the save.");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// SaveCommand(player, split[2]);
|
||||
// return;
|
||||
// }
|
||||
// default:
|
||||
// PrintHelp();
|
||||
// return;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<RootNamespace>Glamourer</RootNamespace>
|
||||
<AssemblyName>GlamourerOld</AssemblyName>
|
||||
<FileVersion>0.2.0.0</FileVersion>
|
||||
<AssemblyVersion>0.2.0.0</AssemblyVersion>
|
||||
<Company>SoftOtter</Company>
|
||||
<Product>Glamourer</Product>
|
||||
<Copyright>Copyright © 2020</Copyright>
|
||||
<Deterministic>true</Deterministic>
|
||||
<OutputType>Library</OutputType>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="LegacyTattoo.raw" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="LegacyTattoo.raw" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DalamudLibPath>$(AppData)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud">
|
||||
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="FFXIVClientStructs">
|
||||
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImGui.NET">
|
||||
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImGuiScene">
|
||||
<HintPath>$(DalamudLibPath)ImGuiScene.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Lumina">
|
||||
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Lumina.Excel">
|
||||
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Glamourer.GameData\Glamourer.GameData.csproj" />
|
||||
<ProjectReference Include="..\..\Penumbra\Penumbra.Api\Penumbra.Api.csproj" />
|
||||
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion">
|
||||
<Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" StandardOutputImportance="low">
|
||||
<Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHash" />
|
||||
</Exec>
|
||||
|
||||
<PropertyGroup>
|
||||
<InformationalVersion>$(GitCommitHash)</InformationalVersion>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Glamourer.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="if $(Configuration) == Release powershell Compress-Archive -Force $(TargetPath), $(TargetDir)$(SolutionName).json, $(TargetDir)$(SolutionName).GameData.dll, $(TargetDir)Penumbra.GameData.dll, $(TargetDir)Penumbra.Api.dll, $(TargetDir)Penumbra.String.dll $(SolutionDir)$(SolutionName).zip" />
|
||||
<Exec Command="if $(Configuration) == Release powershell Copy-Item -Force $(TargetDir)$(SolutionName).json -Destination $(SolutionDir)" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
using Glamourer.Designs;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public static class ActorDebug
|
||||
{
|
||||
/// <summary> Draw the model data values as straight table data without evaluation. </summary>
|
||||
public static unsafe void Draw(in ModelData model)
|
||||
{
|
||||
using var table = ImRaii.Table("##drawObjectData", 3, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Model ID");
|
||||
ImGuiUtil.DrawTableColumn(model.ModelId.ToString());
|
||||
ImGuiUtil.DrawTableColumn(model.ModelId.ToString("X8"));
|
||||
|
||||
for (var i = 0; i < Penumbra.GameData.Structs.CustomizeData.Size; ++i)
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn($"Customize[{i:D2}]");
|
||||
ImGuiUtil.DrawTableColumn(model.Customize.Data.Data[i].ToString());
|
||||
ImGuiUtil.DrawTableColumn(model.Customize.Data.Data[i].ToString("X2"));
|
||||
}
|
||||
ImGuiUtil.DrawTableColumn("Race");
|
||||
ImGuiUtil.DrawTableColumn(model.Customize.Race.ToString());
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Clan");
|
||||
ImGuiUtil.DrawTableColumn(model.Customize.Clan.ToString());
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Gender");
|
||||
ImGuiUtil.DrawTableColumn(model.Customize.Gender.ToString());
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
for (var i = 0; i < 10; ++i)
|
||||
{
|
||||
var slot = EquipSlotExtensions.EqdpSlots[i];
|
||||
ImGuiUtil.DrawTableColumn($"Equipment[{i}] ({slot})");
|
||||
var armor = model.Armor(slot);
|
||||
ImGuiUtil.DrawTableColumn($"{armor.Set.Value}, {armor.Variant}, {armor.Stain.Value}");
|
||||
ImGuiUtil.DrawTableColumn(armor.Value.ToString("X8"));
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Mainhand");
|
||||
ImGuiUtil.DrawTableColumn($"{model.MainHand.Set.Value}, {model.MainHand.Type.Value}, {model.MainHand.Variant}, {model.MainHand.Stain.Value}");
|
||||
ImGuiUtil.DrawTableColumn(model.MainHand.Value.ToString("X16"));
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Offhand");
|
||||
ImGuiUtil.DrawTableColumn($"{model.OffHand.Set.Value}, {model.OffHand.Type.Value}, {model.OffHand.Variant}, {model.OffHand.Stain.Value}");
|
||||
ImGuiUtil.DrawTableColumn(model.OffHand.Value.ToString("X16"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Glamourer.Customization;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
|
||||
namespace Glamourer.Gui.Customization;
|
||||
|
||||
public partial class CustomizationDrawer
|
||||
{
|
||||
private const string ColorPickerPopupName = "ColorPicker";
|
||||
|
||||
private void DrawColorPicker(CustomizeIndex index)
|
||||
{
|
||||
using var _ = SetId(index);
|
||||
var (current, custom) = GetCurrentCustomization(index);
|
||||
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(_currentIndex, i, _customize[CustomizeIndex.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, CustomizeData) GetCurrentCustomization(CustomizeIndex index)
|
||||
{
|
||||
var current = _set.DataByValue(index, _customize[index], out var custom, _customize.Face);
|
||||
if (_set.IsAvailable(index) && current < 0)
|
||||
throw new Exception($"Read invalid customization value {_customize[index]} for {index}.");
|
||||
|
||||
return (current, custom!.Value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
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;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Customization;
|
||||
|
||||
public partial class CustomizationDrawer
|
||||
{
|
||||
private void DrawRaceGenderSelector()
|
||||
{
|
||||
DrawGenderSelector();
|
||||
ImGui.SameLine();
|
||||
using var group = ImRaii.Group();
|
||||
DrawRaceCombo();
|
||||
var gender = _service.AwaitedService.GetName(CustomName.Gender);
|
||||
var clan = _service.AwaitedService.GetName(CustomName.Clan);
|
||||
ImGui.TextUnformatted($"{gender} & {clan}");
|
||||
}
|
||||
|
||||
private void DrawGenderSelector()
|
||||
{
|
||||
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||
var icon = _customize.Gender switch
|
||||
{
|
||||
Gender.Male when _customize.Race is Race.Hrothgar => FontAwesomeIcon.MarsDouble,
|
||||
Gender.Male => FontAwesomeIcon.Mars,
|
||||
Gender.Female => FontAwesomeIcon.Venus,
|
||||
|
||||
_ => throw new Exception($"Gender value {_customize.Gender} is not a valid gender for a design."),
|
||||
};
|
||||
|
||||
if (!ImGuiUtil.DrawDisabledButton(icon.ToIconString(), _framedIconSize, string.Empty, icon == FontAwesomeIcon.MarsDouble, true))
|
||||
return;
|
||||
|
||||
Changed |= _customize.ChangeGender(CharacterEquip.Null, _customize.Gender is Gender.Male ? Gender.Female : Gender.Male, _items, _service.AwaitedService);
|
||||
}
|
||||
|
||||
private void DrawRaceCombo()
|
||||
{
|
||||
ImGui.SetNextItemWidth(_raceSelectorWidth);
|
||||
using var combo = ImRaii.Combo("##subRaceCombo", _customize.ClanName(_service.AwaitedService));
|
||||
if (!combo)
|
||||
return;
|
||||
|
||||
foreach (var subRace in Enum.GetValues<SubRace>().Skip(1)) // Skip Unknown
|
||||
{
|
||||
if (ImGui.Selectable(CustomizeExtensions.ClanName(_service.AwaitedService, subRace, _customize.Gender), subRace == _customize.Clan))
|
||||
Changed |= _customize.ChangeRace(CharacterEquip.Null, subRace, _items, _service.AwaitedService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Glamourer.Customization;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Gui.Customization;
|
||||
|
||||
public partial class CustomizationDrawer
|
||||
{
|
||||
private const string IconSelectorPopup = "Style Picker";
|
||||
|
||||
private void DrawIconSelector(CustomizeIndex index)
|
||||
{
|
||||
using var _ = SetId(index);
|
||||
using var bigGroup = ImRaii.Group();
|
||||
var label = _currentOption;
|
||||
|
||||
var current = _set.DataByValue(index, _currentByte, out var custom, _customize.Face);
|
||||
if (current < 0)
|
||||
{
|
||||
label = $"{_currentOption} (Custom #{_customize[index]})";
|
||||
current = 0;
|
||||
custom = _set.Data(index, 0);
|
||||
}
|
||||
|
||||
var icon = _service.AwaitedService.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 (_currentIndex == CustomizeIndex.Face)
|
||||
FaceInputInt(current);
|
||||
else
|
||||
DataInputInt(current);
|
||||
|
||||
ImGui.TextUnformatted($"{label} ({custom.Value.Value})");
|
||||
}
|
||||
|
||||
DrawIconPickerPopup();
|
||||
}
|
||||
|
||||
private bool UpdateFace(CustomizeData data)
|
||||
{
|
||||
// Hrothgar Hack
|
||||
var value = _set.Race == Race.Hrothgar ? data.Value + 4 : data.Value;
|
||||
if (_customize.Face == value)
|
||||
return false;
|
||||
|
||||
_customize.Face = value;
|
||||
Changed |= CustomizeFlag.Face;
|
||||
return true;
|
||||
}
|
||||
|
||||
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(_currentIndex, 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(_currentIndex, i, _customize.Face);
|
||||
var icon = _service.AwaitedService.GetIcon(custom.IconId);
|
||||
using (var _ = ImRaii.Group())
|
||||
{
|
||||
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
|
||||
{
|
||||
if (_currentIndex == CustomizeIndex.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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Only used for facial features, so fixed ID.
|
||||
private void DrawMultiIconSelector()
|
||||
{
|
||||
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(CustomizeIndex.LegacyTattoo));
|
||||
}
|
||||
|
||||
private void DrawMultiIcons()
|
||||
{
|
||||
var options = _set.Order[CharaMakeParams.MenuType.IconCheckmark];
|
||||
using var _ = ImRaii.Group();
|
||||
foreach (var (featureIdx, idx) in options.WithIndex())
|
||||
{
|
||||
using var id = SetId(featureIdx);
|
||||
var enabled = _customize.Get(featureIdx) != CustomizeValue.Zero;
|
||||
var feature = _set.Data(featureIdx, 0, _customize.Face);
|
||||
var icon = featureIdx == CustomizeIndex.LegacyTattoo
|
||||
? _legacyTattoo ?? _service.AwaitedService.GetIcon(feature.IconId)
|
||||
: _service.AwaitedService.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.Set(featureIdx, enabled ? CustomizeValue.Zero : CustomizeValue.Max);
|
||||
Changed |= _currentFlag;
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
|
||||
if (idx % 4 != 3)
|
||||
ImGui.SameLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
|
||||
namespace Glamourer.Gui.Customization;
|
||||
|
||||
public partial class CustomizationDrawer
|
||||
{
|
||||
private void PercentageSelector(CustomizeIndex index)
|
||||
{
|
||||
using var _ = SetId(index);
|
||||
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((CustomizeValue)tmp);
|
||||
}
|
||||
|
||||
private void PercentageInputInt()
|
||||
{
|
||||
var tmp = (int)_currentByte.Value;
|
||||
ImGui.SetNextItemWidth(_inputIntSize);
|
||||
if (ImGui.InputInt("##text", ref tmp, 1, 1))
|
||||
UpdateValue((CustomizeValue)Math.Clamp(tmp, 0, _currentCount - 1));
|
||||
ImGuiUtil.HoverTooltip($"Input Range: [0, {_currentCount - 1}]");
|
||||
}
|
||||
|
||||
// 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(_currentIndex, currentIndex, _customize.Face);
|
||||
UpdateValue(data.Value);
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]");
|
||||
}
|
||||
|
||||
private void DrawListSelector(CustomizeIndex index)
|
||||
{
|
||||
using var _ = SetId(index);
|
||||
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((CustomizeValue)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((CustomizeValue)Math.Clamp(tmp - 1, 0, _currentCount - 1));
|
||||
ImGuiUtil.HoverTooltip($"Input Range: [1, {_currentCount}]");
|
||||
}
|
||||
|
||||
// Draw a customize checkbox.
|
||||
private void DrawCheckbox(CustomizeIndex idx)
|
||||
{
|
||||
using var id = SetId(idx);
|
||||
var tmp = _currentByte != CustomizeValue.Zero;
|
||||
if (ImGui.Checkbox(_currentOption, ref tmp))
|
||||
{
|
||||
_customize.Set(idx, tmp ? CustomizeValue.Max : CustomizeValue.Zero);
|
||||
Changed |= _currentFlag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
|
||||
namespace Glamourer.Gui.Customization;
|
||||
|
||||
public partial class CustomizationDrawer : IDisposable
|
||||
{
|
||||
private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f);
|
||||
private readonly ImGuiScene.TextureWrap? _legacyTattoo;
|
||||
|
||||
private bool _withFlags = false;
|
||||
private Exception? _terminate = null;
|
||||
|
||||
private Customize _customize;
|
||||
private CustomizationSet _set = null!;
|
||||
|
||||
public Customize Customize;
|
||||
|
||||
public CustomizeFlag CurrentFlag { get; private set; }
|
||||
public CustomizeFlag Changed { get; private set; }
|
||||
|
||||
public bool RequiresRedraw
|
||||
=> Changed.RequiresRedraw();
|
||||
|
||||
private bool _locked = false;
|
||||
private Vector2 _iconSize;
|
||||
private Vector2 _framedIconSize;
|
||||
private float _inputIntSize;
|
||||
private float _comboSelectorSize;
|
||||
private float _raceSelectorWidth;
|
||||
|
||||
private readonly CustomizationService _service;
|
||||
private readonly ItemManager _items;
|
||||
|
||||
public CustomizationDrawer(DalamudPluginInterface pi, CustomizationService service, ItemManager items)
|
||||
{
|
||||
_service = service;
|
||||
_items = items;
|
||||
_legacyTattoo = GetLegacyTattooIcon(pi);
|
||||
Customize = Customize.Default;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_legacyTattoo?.Dispose();
|
||||
}
|
||||
|
||||
public bool Draw(Customize current, bool locked)
|
||||
{
|
||||
_withFlags = false;
|
||||
CurrentFlag = CustomizeFlagExtensions.All;
|
||||
Init(current, locked);
|
||||
return DrawInternal();
|
||||
}
|
||||
|
||||
public bool Draw(Customize current, CustomizeFlag currentFlags, bool locked)
|
||||
{
|
||||
_withFlags = true;
|
||||
CurrentFlag = currentFlags;
|
||||
Init(current, locked);
|
||||
return DrawInternal();
|
||||
}
|
||||
|
||||
private void Init(Customize current, bool locked)
|
||||
{
|
||||
UpdateSizes();
|
||||
_terminate = null;
|
||||
Changed = 0;
|
||||
_customize.Load(current);
|
||||
_locked = locked;
|
||||
}
|
||||
|
||||
// Set state for drawing of current customization.
|
||||
private CustomizeIndex _currentIndex;
|
||||
private CustomizeFlag _currentFlag;
|
||||
private CustomizeValue _currentByte = CustomizeValue.Zero;
|
||||
private int _currentCount;
|
||||
private string _currentOption = string.Empty;
|
||||
|
||||
// Prepare a new customization option.
|
||||
private ImRaii.Id SetId(CustomizeIndex index)
|
||||
{
|
||||
_currentIndex = index;
|
||||
_currentFlag = index.ToFlag();
|
||||
_currentByte = _customize[index];
|
||||
_currentCount = _set.Count(index, _customize.Face);
|
||||
_currentOption = _set.Option(index);
|
||||
return ImRaii.PushId((int)index);
|
||||
}
|
||||
|
||||
// Update the current id with a new value.
|
||||
private void UpdateValue(CustomizeValue value)
|
||||
{
|
||||
if (_currentByte == value)
|
||||
return;
|
||||
|
||||
_customize[_currentIndex] = value;
|
||||
Changed |= _currentFlag;
|
||||
}
|
||||
|
||||
private bool DrawInternal()
|
||||
{
|
||||
using var disabled = ImRaii.Disabled(_locked);
|
||||
|
||||
try
|
||||
{
|
||||
DrawRaceGenderSelector();
|
||||
_set = _service.AwaitedService.GetList(_customize.Clan, _customize.Gender);
|
||||
|
||||
foreach (var id in _set.Order[CharaMakeParams.MenuType.Percentage])
|
||||
PercentageSelector(id);
|
||||
|
||||
CustomGui.IteratePairwise(_set.Order[CharaMakeParams.MenuType.IconSelector], DrawIconSelector, ImGui.SameLine);
|
||||
|
||||
DrawMultiIconSelector();
|
||||
|
||||
foreach (var id in _set.Order[CharaMakeParams.MenuType.ListSelector])
|
||||
DrawListSelector(id);
|
||||
|
||||
CustomGui.IteratePairwise(_set.Order[CharaMakeParams.MenuType.ColorPicker], DrawColorPicker, ImGui.SameLine);
|
||||
|
||||
CustomGui.IteratePairwise(_set.Order[CharaMakeParams.MenuType.Checkmark], DrawCheckbox,
|
||||
() => ImGui.SameLine(_inputIntSize + _framedIconSize.X + 3 * ImGui.GetStyle().ItemSpacing.X));
|
||||
return Changed != 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_terminate = ex;
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF4040FF);
|
||||
ImGui.NewLine();
|
||||
ImGuiUtil.TextWrapped(_terminate.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSizes()
|
||||
{
|
||||
_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(DalamudPluginInterface pi)
|
||||
{
|
||||
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
|
||||
? pi.UiBuilder.LoadImageRaw(rawImage, 192, 192, 4)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
using System.Numerics;
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Designs;
|
||||
using OtterGui;
|
||||
using OtterGui.FileSystem.Selector;
|
||||
|
||||
namespace Glamourer.Gui.Designs;
|
||||
|
||||
public sealed class DesignFileSystemSelector : FileSystemSelector<Design, DesignFileSystemSelector.DesignState>
|
||||
{
|
||||
private readonly DesignManager _designManager;
|
||||
|
||||
public struct DesignState
|
||||
{ }
|
||||
|
||||
public DesignFileSystemSelector(DesignManager designManager, DesignFileSystem fileSystem, KeyState keyState)
|
||||
: base(fileSystem, keyState)
|
||||
{
|
||||
_designManager = designManager;
|
||||
_designManager.DesignChange += OnDesignChange;
|
||||
AddButton(DeleteButton, 1000);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
_designManager.DesignChange -= OnDesignChange;
|
||||
}
|
||||
|
||||
private void OnDesignChange(DesignManager.DesignChangeType type, Design design, object? oldData)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DesignManager.DesignChangeType.ReloadedAll:
|
||||
case DesignManager.DesignChangeType.Renamed:
|
||||
case DesignManager.DesignChangeType.AddedTag:
|
||||
case DesignManager.DesignChangeType.ChangedTag:
|
||||
case DesignManager.DesignChangeType.RemovedTag:
|
||||
SetFilterDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteButton(Vector2 size)
|
||||
{
|
||||
var keys = true;
|
||||
var tt = SelectedLeaf == null
|
||||
? "No design selected."
|
||||
: "Delete the currently selected design entirely from your drive.\n"
|
||||
+ "This can not be undone.";
|
||||
//if (!keys)
|
||||
// tt += $"\nHold {_config.DeleteModModifier} while clicking to delete the mod.";
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), size, tt, SelectedLeaf == null || !keys, true)
|
||||
&& Selected != null)
|
||||
_designManager.Delete(Selected);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,368 +0,0 @@
|
|||
using Dalamud.Game.ClientState.Keys;
|
||||
using Glamourer.Designs;
|
||||
using OtterGui.Filesystem;
|
||||
using OtterGui.FileSystem.Selector;
|
||||
|
||||
namespace Glamourer.Gui.Designs;
|
||||
|
||||
//internal partial class Interface
|
||||
//{
|
||||
// private int _totalObject;
|
||||
//
|
||||
// private bool _inDesignMode;
|
||||
// private Design? _selection;
|
||||
// private string _newChildName = string.Empty;
|
||||
//
|
||||
// private void DrawDesignSelector()
|
||||
// {
|
||||
// _totalObject = 0;
|
||||
// ImGui.BeginGroup();
|
||||
// if (ImGui.BeginChild("##selector", new Vector2(SelectorWidth * ImGui.GetIO().FontGlobalScale, -ImGui.GetFrameHeight() - 1), true))
|
||||
// {
|
||||
// DrawFolderContent(_designs.FileSystem.Root, Glamourer.Config.FoldersFirst ? SortMode.FoldersFirst : SortMode.Lexicographical);
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||
// ImGui.EndChild();
|
||||
// ImGui.PopStyleVar();
|
||||
// }
|
||||
//
|
||||
// DrawDesignSelectorButtons();
|
||||
// ImGui.EndGroup();
|
||||
// }
|
||||
//
|
||||
// private void DrawPasteClipboardButton()
|
||||
// {
|
||||
// if (_selection!.Data.WriteProtected)
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
|
||||
//
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// var applyButton = ImGui.Button(FontAwesomeIcon.Paste.ToIconString());
|
||||
// ImGui.PopFont();
|
||||
// if (_selection!.Data.WriteProtected)
|
||||
// ImGui.PopStyleVar();
|
||||
//
|
||||
// ImGuiUtil.HoverTooltip("Overwrite with customization code from clipboard.");
|
||||
//
|
||||
// if (_selection!.Data.WriteProtected || !applyButton)
|
||||
// return;
|
||||
//
|
||||
// var text = ImGui.GetClipboardText();
|
||||
// if (!text.Any())
|
||||
// return;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// _selection!.Data.Load(text, out _);
|
||||
// _designs.SaveToFile();
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// PluginLog.Information($"{e}");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void DrawNewFolderButton()
|
||||
// {
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// if (ImGui.Button(FontAwesomeIcon.FolderPlus.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
|
||||
// OpenDesignNamePopup(DesignNameUse.NewFolder);
|
||||
// ImGui.PopFont();
|
||||
// ImGuiUtil.HoverTooltip("Create a new, empty Folder.");
|
||||
//
|
||||
// DrawDesignNamePopup(DesignNameUse.NewFolder);
|
||||
// }
|
||||
//
|
||||
// private void DrawNewDesignButton()
|
||||
// {
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
|
||||
// OpenDesignNamePopup(DesignNameUse.NewDesign);
|
||||
// ImGui.PopFont();
|
||||
// ImGuiUtil.HoverTooltip("Create a new, empty Design.");
|
||||
//
|
||||
// DrawDesignNamePopup(DesignNameUse.NewDesign);
|
||||
// }
|
||||
//
|
||||
// private void DrawClipboardDesignButton()
|
||||
// {
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// if (ImGui.Button(FontAwesomeIcon.Paste.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
|
||||
// OpenDesignNamePopup(DesignNameUse.FromClipboard);
|
||||
// ImGui.PopFont();
|
||||
// ImGuiUtil.HoverTooltip("Create a new design from the customization string in your clipboard.");
|
||||
//
|
||||
// DrawDesignNamePopup(DesignNameUse.FromClipboard);
|
||||
// }
|
||||
//
|
||||
// private void DrawDeleteDesignButton()
|
||||
// {
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// var style = _selection == null;
|
||||
// if (style)
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
|
||||
// if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString(), Vector2.UnitX * SelectorWidth / 5) && _selection != null)
|
||||
// {
|
||||
// _designs.DeleteAllChildren(_selection, false);
|
||||
// _selection = null;
|
||||
// }
|
||||
//
|
||||
// ImGui.PopFont();
|
||||
// if (style)
|
||||
// ImGui.PopStyleVar();
|
||||
// ImGuiUtil.HoverTooltip("Delete the currently selected Design.");
|
||||
// }
|
||||
//
|
||||
// private void DrawDuplicateDesignButton()
|
||||
// {
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// if (_selection == null)
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.5f);
|
||||
// if (ImGui.Button(FontAwesomeIcon.Clone.ToIconString(), Vector2.UnitX * SelectorWidth / 5) && _selection != null)
|
||||
// OpenDesignNamePopup(DesignNameUse.DuplicateDesign);
|
||||
// ImGui.PopFont();
|
||||
// if (_selection == null)
|
||||
// ImGui.PopStyleVar();
|
||||
// ImGuiUtil.HoverTooltip(
|
||||
// "Clone the currently selected Design.\nHold Shift to only clone the customizations.\nHold Control to only clone the equipment.");
|
||||
//
|
||||
// DrawDesignNamePopup(DesignNameUse.DuplicateDesign);
|
||||
// }
|
||||
//
|
||||
// private void DrawDesignSelectorButtons()
|
||||
// {
|
||||
// using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
// .Push(ImGuiStyleVar.FrameRounding, 0f);
|
||||
//
|
||||
// DrawNewFolderButton();
|
||||
// ImGui.SameLine();
|
||||
// DrawNewDesignButton();
|
||||
// ImGui.SameLine();
|
||||
// DrawClipboardDesignButton();
|
||||
// ImGui.SameLine();
|
||||
// DrawDuplicateDesignButton();
|
||||
// ImGui.SameLine();
|
||||
// DrawDeleteDesignButton();
|
||||
// }
|
||||
//
|
||||
// private void DrawDesignHeaderButtons()
|
||||
// {
|
||||
// DrawCopyClipboardButton(_selection!.Data);
|
||||
// ImGui.SameLine();
|
||||
// DrawPasteClipboardButton();
|
||||
// ImGui.SameLine();
|
||||
// DrawApplyToPlayerButton(_selection!.Data);
|
||||
// if (!_inGPose)
|
||||
// {
|
||||
// ImGui.SameLine();
|
||||
// DrawApplyToTargetButton(_selection!.Data);
|
||||
// }
|
||||
//
|
||||
// ImGui.SameLine();
|
||||
// DrawCheckbox("Write Protected", _selection!.Data.WriteProtected, v => _selection!.Data.WriteProtected = v, false);
|
||||
// }
|
||||
//
|
||||
// private void DrawDesignPanel()
|
||||
// {
|
||||
// if (ImGui.BeginChild("##details", -Vector2.One * 0.001f, true))
|
||||
// {
|
||||
// DrawDesignHeaderButtons();
|
||||
// var data = _selection!.Data;
|
||||
// var prot = _selection!.Data.WriteProtected;
|
||||
// if (prot)
|
||||
// {
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.8f);
|
||||
// data = data.Copy();
|
||||
// }
|
||||
//
|
||||
// DrawGeneralSettings(data, prot);
|
||||
// var mask = data.WriteEquipment;
|
||||
// if (DrawEquip(data.Equipment, ref mask) && !prot)
|
||||
// {
|
||||
// data.WriteEquipment = mask;
|
||||
// _designs.SaveToFile();
|
||||
// }
|
||||
//
|
||||
// if (DrawCustomization(ref data.Customizations) && !prot)
|
||||
// _designs.SaveToFile();
|
||||
//
|
||||
// if (DrawMiscellaneous(data, null) && !prot)
|
||||
// _designs.SaveToFile();
|
||||
//
|
||||
// if (prot)
|
||||
// ImGui.PopStyleVar();
|
||||
//
|
||||
// ImGui.EndChild();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void DrawSaves()
|
||||
// {
|
||||
// using var style = ImRaii.PushStyle(ImGuiStyleVar.IndentSpacing, 12.5f * ImGui.GetIO().FontGlobalScale);
|
||||
// using var tab = ImRaii.TabItem("Designs");
|
||||
// _inDesignMode = tab.Success;
|
||||
// if (!_inDesignMode)
|
||||
// return;
|
||||
//
|
||||
// DrawDesignSelector();
|
||||
//
|
||||
// if (_selection != null)
|
||||
// {
|
||||
// ImGui.SameLine();
|
||||
// DrawDesignPanel();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void DrawCheckbox(string label, bool value, Action<bool> setter, bool prot)
|
||||
// {
|
||||
// var tmp = value;
|
||||
// if (ImGui.Checkbox(label, ref tmp) && tmp != value)
|
||||
// {
|
||||
// setter(tmp);
|
||||
// if (!prot)
|
||||
// _designs.SaveToFile();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void DrawGeneralSettings(CharacterSave data, bool prot)
|
||||
// {
|
||||
// ImGui.BeginGroup();
|
||||
// DrawCheckbox("Apply Customizations", data.WriteCustomizations, v => data.WriteCustomizations = v, prot);
|
||||
// DrawCheckbox("Write Weapon State", data.SetWeaponState, v => data.SetWeaponState = v, prot);
|
||||
// ImGui.EndGroup();
|
||||
// ImGui.SameLine();
|
||||
// ImGui.BeginGroup();
|
||||
// DrawCheckbox("Write Hat State", data.SetHatState, v => data.SetHatState = v, prot);
|
||||
// DrawCheckbox("Write Visor State", data.SetVisorState, v => data.SetVisorState = v, prot);
|
||||
// ImGui.EndGroup();
|
||||
// }
|
||||
//
|
||||
// private void RenameChildInput(IFileSystemBase child)
|
||||
// {
|
||||
// ImGui.SetNextItemWidth(150);
|
||||
// if (!ImGui.InputTextWithHint("##fsNewName", "Rename...", ref _newChildName, 64,
|
||||
// ImGuiInputTextFlags.EnterReturnsTrue))
|
||||
// return;
|
||||
//
|
||||
// if (_newChildName.Any() && _newChildName != child.Name)
|
||||
// try
|
||||
// {
|
||||
// var oldPath = child.FullName();
|
||||
// if (_designs.FileSystem.Rename(child, _newChildName))
|
||||
// _designs.UpdateAllChildren(oldPath, child);
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// PluginLog.Error($"Could not rename {child.Name} to {_newChildName}:\n{e}");
|
||||
// }
|
||||
// else if (child is Folder f)
|
||||
// try
|
||||
// {
|
||||
// var oldPath = child.FullName();
|
||||
// if (_designs.FileSystem.Merge(f, f.Parent, true))
|
||||
// _designs.UpdateAllChildren(oldPath, f.Parent);
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// PluginLog.Error($"Could not merge folder {child.Name} into parent:\n{e}");
|
||||
// }
|
||||
//
|
||||
// _newChildName = string.Empty;
|
||||
// }
|
||||
//
|
||||
// private void ContextMenu(IFileSystemBase child)
|
||||
// {
|
||||
// var label = $"##fsPopup{child.FullName()}";
|
||||
// if (ImGui.BeginPopup(label))
|
||||
// {
|
||||
// if (ImGui.MenuItem("Delete") && ImGui.GetIO().KeyCtrl && ImGui.GetIO().KeyShift)
|
||||
// _designs.DeleteAllChildren(child, false);
|
||||
// ImGuiUtil.HoverTooltip("Hold Control and Shift to delete.");
|
||||
//
|
||||
// RenameChildInput(child);
|
||||
//
|
||||
// if (child is Design d && ImGui.MenuItem("Copy to Clipboard"))
|
||||
// ImGui.SetClipboardText(d.Data.ToBase64());
|
||||
//
|
||||
// ImGui.EndPopup();
|
||||
// }
|
||||
//
|
||||
// if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
// {
|
||||
// _newChildName = child.Name;
|
||||
// ImGui.OpenPopup(label);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static uint GetDesignColor(CharacterSave save)
|
||||
// {
|
||||
// const uint white = 0xFFFFFFFF;
|
||||
// const uint grey = 0xFF808080;
|
||||
// if (!Glamourer.Config.ColorDesigns)
|
||||
// return white;
|
||||
//
|
||||
// var changesStates = save.SetHatState || save.SetVisorState || save.SetWeaponState || save.IsWet || save.Alpha != 1.0f;
|
||||
// if (save.WriteCustomizations)
|
||||
// if (save.WriteEquipment != CharacterEquipMask.None)
|
||||
// return white;
|
||||
// else
|
||||
// return changesStates ? white : Glamourer.Config.CustomizationColor;
|
||||
//
|
||||
// if (save.WriteEquipment != CharacterEquipMask.None)
|
||||
// return changesStates ? white : Glamourer.Config.EquipmentColor;
|
||||
//
|
||||
// return changesStates ? Glamourer.Config.StateColor : grey;
|
||||
// }
|
||||
//
|
||||
// private void DrawFolderContent(Folder folder, SortMode mode)
|
||||
// {
|
||||
// foreach (var child in folder.AllChildren(mode).ToArray())
|
||||
// {
|
||||
// if (child.IsFolder(out var subFolder))
|
||||
// {
|
||||
// var treeNode = ImGui.TreeNodeEx($"{subFolder.Name}##{_totalObject}");
|
||||
// DrawOrnaments(child);
|
||||
//
|
||||
// if (treeNode)
|
||||
// {
|
||||
// DrawFolderContent(subFolder, mode);
|
||||
// ImGui.TreePop();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// _totalObject += subFolder.TotalDescendantLeaves();
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (child is not Design d)
|
||||
// continue;
|
||||
//
|
||||
// ++_totalObject;
|
||||
// var color = GetDesignColor(d.Data);
|
||||
// using var c = ImRaii.PushColor(ImGuiCol.Text, color);
|
||||
//
|
||||
// var selected = ImGui.Selectable($"{child.Name}##{_totalObject}", ReferenceEquals(child, _selection));
|
||||
// c.Pop();
|
||||
// DrawOrnaments(child);
|
||||
//
|
||||
// if (Glamourer.Config.ShowLocks && d.Data.WriteProtected)
|
||||
// {
|
||||
// ImGui.SameLine();
|
||||
// using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||
// c.Push(ImGuiCol.Text, color);
|
||||
// ImGui.TextUnformatted(FontAwesomeIcon.Lock.ToIconString());
|
||||
// }
|
||||
//
|
||||
// if (selected)
|
||||
// _selection = d;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void DrawOrnaments(IFileSystemBase child)
|
||||
// {
|
||||
// FileSystemImGui.DragDropSource(child);
|
||||
// if (FileSystemImGui.DragDropTarget(_designs.FileSystem, child, out var oldPath, out var draggedFolder))
|
||||
// _designs.UpdateAllChildren(oldPath, draggedFolder!);
|
||||
// ContextMenu(child);
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Equipment;
|
||||
|
||||
public class EquipmentDrawer
|
||||
{
|
||||
private readonly ItemManager _items;
|
||||
private readonly FilterComboColors _stainCombo;
|
||||
private readonly StainData _stainData;
|
||||
private readonly ItemCombo[] _itemCombo;
|
||||
private readonly Dictionary<(FullEquipType, EquipSlot), WeaponCombo> _weaponCombo;
|
||||
|
||||
public EquipmentDrawer(DataManager gameData, ItemManager items)
|
||||
{
|
||||
_items = items;
|
||||
_stainData = items.Stains;
|
||||
_stainCombo = new FilterComboColors(140,
|
||||
_stainData.Data.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))));
|
||||
_itemCombo = EquipSlotExtensions.EqdpSlots.Select(e => new ItemCombo(gameData, items, e)).ToArray();
|
||||
_weaponCombo = new Dictionary<(FullEquipType, EquipSlot), WeaponCombo>(FullEquipTypeExtensions.WeaponTypes.Count * 2);
|
||||
foreach (var type in Enum.GetValues<FullEquipType>())
|
||||
{
|
||||
if (type.ToSlot() is EquipSlot.MainHand)
|
||||
_weaponCombo.TryAdd((type, EquipSlot.MainHand), new WeaponCombo(gameData, items, type, EquipSlot.MainHand));
|
||||
else if (type.ToSlot() is EquipSlot.OffHand)
|
||||
_weaponCombo.TryAdd((type, EquipSlot.OffHand), new WeaponCombo(gameData, items, type, EquipSlot.OffHand));
|
||||
|
||||
var offhand = type.Offhand();
|
||||
if (offhand is not FullEquipType.Unknown && !_weaponCombo.ContainsKey((offhand, EquipSlot.OffHand)))
|
||||
_weaponCombo.TryAdd((offhand, EquipSlot.OffHand), new WeaponCombo(gameData, items, type, EquipSlot.OffHand));
|
||||
}
|
||||
|
||||
_weaponCombo.Add((FullEquipType.Unknown, EquipSlot.MainHand), new WeaponCombo(gameData, items, FullEquipType.Unknown, EquipSlot.MainHand));
|
||||
}
|
||||
|
||||
private string VerifyRestrictedGear(Item gear, EquipSlot slot, Gender gender, Race race)
|
||||
{
|
||||
if (slot.IsAccessory())
|
||||
return gear.Name;
|
||||
|
||||
var (changed, _) = _items.ResolveRestrictedGear(gear.Model, slot, race, gender);
|
||||
if (changed)
|
||||
return gear.Name + " (Restricted)";
|
||||
|
||||
return gear.Name;
|
||||
}
|
||||
|
||||
public bool DrawArmor(Item current, EquipSlot slot, out Item armor, Gender gender = Gender.Unknown, Race race = Race.Unknown)
|
||||
{
|
||||
Debug.Assert(slot.IsEquipment() || slot.IsAccessory(), $"Called {nameof(DrawArmor)} on {slot}.");
|
||||
var combo = _itemCombo[slot.ToIndex()];
|
||||
armor = current;
|
||||
var change = combo.Draw(VerifyRestrictedGear(armor, slot, gender, race), armor.ItemId, 320 * ImGuiHelpers.GlobalScale);
|
||||
if (armor.ModelBase.Value != 0)
|
||||
{
|
||||
ImGuiUtil.HoverTooltip("Right-click to clear.");
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
{
|
||||
change = true;
|
||||
armor = ItemManager.NothingItem(slot);
|
||||
}
|
||||
else if (change)
|
||||
{
|
||||
armor = combo.CurrentSelection.WithStain(armor.Stain);
|
||||
}
|
||||
}
|
||||
else if (change)
|
||||
{
|
||||
armor = combo.CurrentSelection.WithStain(armor.Stain);
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
public bool DrawStain(StainId current, EquipSlot slot, out Stain stain)
|
||||
{
|
||||
var found = _stainData.TryGetValue(current, out stain);
|
||||
if (!_stainCombo.Draw($"##stain{slot}", stain.RgbaColor, stain.Name, found))
|
||||
return false;
|
||||
|
||||
return _stainData.TryGetValue(_stainCombo.CurrentSelection.Key, out stain);
|
||||
}
|
||||
|
||||
public bool DrawMainhand(Weapon current, bool drawAll, out Weapon weapon)
|
||||
{
|
||||
weapon = current;
|
||||
if (!_weaponCombo.TryGetValue((drawAll ? FullEquipType.Unknown : current.Type, EquipSlot.MainHand), out var combo))
|
||||
return false;
|
||||
|
||||
if (!combo.Draw(weapon.Name, weapon.ItemId, 320 * ImGuiHelpers.GlobalScale))
|
||||
return false;
|
||||
|
||||
weapon = combo.CurrentSelection.WithStain(current.Stain);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DrawOffhand(Weapon current, FullEquipType mainType, out Weapon weapon)
|
||||
{
|
||||
weapon = current;
|
||||
var offType = mainType.Offhand();
|
||||
if (offType == FullEquipType.Unknown)
|
||||
return false;
|
||||
|
||||
if (!_weaponCombo.TryGetValue((offType, EquipSlot.OffHand), out var combo))
|
||||
return false;
|
||||
|
||||
var change = combo.Draw(weapon.Name, weapon.ItemId, 320 * ImGuiHelpers.GlobalScale);
|
||||
if (offType.ToSlot() is EquipSlot.OffHand && weapon.ModelBase.Value != 0)
|
||||
{
|
||||
ImGuiUtil.HoverTooltip("Right-click to clear.");
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
{
|
||||
change = true;
|
||||
weapon = ItemManager.NothingItem(offType);
|
||||
}
|
||||
}
|
||||
else if (change)
|
||||
{
|
||||
weapon = combo.CurrentSelection.WithStain(current.Stain);
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
public bool DrawApply(Design design, EquipSlot slot, out bool enabled)
|
||||
=> DrawCheckbox($"##apply{slot}", design.DoApplyEquip(slot), out enabled);
|
||||
|
||||
public bool DrawApplyStain(Design design, EquipSlot slot, out bool enabled)
|
||||
=> DrawCheckbox($"##applyStain{slot}", design.DoApplyStain(slot), out enabled);
|
||||
|
||||
private static bool DrawCheckbox(string label, bool value, out bool on)
|
||||
{
|
||||
var ret = ImGuiUtil.Checkbox(label, string.Empty, value, v => value = v);
|
||||
on = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool DrawVisor(Design design, out bool on)
|
||||
=> DrawCheckbox("##visorToggled", design.Visor.ForcedValue, out on);
|
||||
|
||||
public bool DrawVisor(ActiveDesign design, out bool on)
|
||||
=> DrawCheckbox("##visorToggled", design.IsVisorToggled, out on);
|
||||
|
||||
public bool DrawHat(Design design, out bool on)
|
||||
=> DrawCheckbox("##hatVisible", design.Hat.ForcedValue, out on);
|
||||
|
||||
public bool DrawHat(ActiveDesign design, out bool on)
|
||||
=> DrawCheckbox("##hatVisible", design.IsHatVisible, out on);
|
||||
|
||||
public bool DrawWeapon(Design design, out bool on)
|
||||
=> DrawCheckbox("##weaponVisible", design.Weapon.ForcedValue, out on);
|
||||
|
||||
public bool DrawWeapon(ActiveDesign design, out bool on)
|
||||
=> DrawCheckbox("##weaponVisible", design.IsWeaponVisible, out on);
|
||||
|
||||
public bool DrawWetness(Design design, out bool on)
|
||||
=> DrawCheckbox("##wetness", design.Wetness.ForcedValue, out on);
|
||||
|
||||
public bool DrawWetness(ActiveDesign design, out bool on)
|
||||
=> DrawCheckbox("##wetnessVisible", design.IsWet, out on);
|
||||
|
||||
public bool DrawApplyVisor(Design design, out bool on)
|
||||
=> DrawCheckbox("##applyVisor", design.Visor.Enabled, out on);
|
||||
|
||||
public bool DrawApplyWetness(Design design, out bool on)
|
||||
=> DrawCheckbox("##applyWetness", design.Wetness.Enabled, out on);
|
||||
|
||||
public bool DrawApplyHatState(Design design, out bool on)
|
||||
=> DrawCheckbox("##applyHatState", design.Hat.Enabled, out on);
|
||||
|
||||
public bool DrawApplyWeaponState(Design design, out bool on)
|
||||
=> DrawCheckbox("##applyWeaponState", design.Weapon.Enabled, out on);
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Data;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using ImGuiNET;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Item = Glamourer.Designs.Item;
|
||||
|
||||
namespace Glamourer.Gui.Equipment;
|
||||
|
||||
public sealed class ItemCombo : FilterComboCache<Item>
|
||||
{
|
||||
public readonly string Label;
|
||||
private uint _currentItem;
|
||||
|
||||
public ItemCombo(DataManager gameData, ItemManager items, EquipSlot slot)
|
||||
: base(() => GetItems(items, slot))
|
||||
{
|
||||
Label = GetLabel(gameData, slot);
|
||||
_currentItem = ItemManager.NothingId(slot);
|
||||
}
|
||||
|
||||
protected override void DrawList(float width, float itemHeight)
|
||||
{
|
||||
base.DrawList(width, itemHeight);
|
||||
if (NewSelection != null && Items.Count > NewSelection.Value)
|
||||
CurrentSelection = Items[NewSelection.Value];
|
||||
}
|
||||
|
||||
protected override int UpdateCurrentSelected(int currentSelected)
|
||||
{
|
||||
if (CurrentSelection.ItemId != _currentItem)
|
||||
{
|
||||
CurrentSelectionIdx = Items.IndexOf(i => i.ItemId == _currentItem);
|
||||
CurrentSelection = CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : default;
|
||||
return base.UpdateCurrentSelected(CurrentSelectionIdx);
|
||||
}
|
||||
|
||||
return currentSelected;
|
||||
}
|
||||
|
||||
public bool Draw(string previewName, uint previewIdx, float width)
|
||||
{
|
||||
_currentItem = previewIdx;
|
||||
return Draw(Label, previewName, string.Empty, width, ImGui.GetTextLineHeightWithSpacing());
|
||||
}
|
||||
|
||||
protected override bool DrawSelectable(int globalIdx, bool selected)
|
||||
{
|
||||
var obj = Items[globalIdx];
|
||||
var name = ToString(obj);
|
||||
var ret = ImGui.Selectable(name, selected);
|
||||
ImGui.SameLine();
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080);
|
||||
ImGuiUtil.RightAlign($"({obj.ModelBase.Value}-{obj.Variant})");
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override bool IsVisible(int globalIndex, LowerString filter)
|
||||
=> base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelBase.ToString());
|
||||
|
||||
protected override string ToString(Item obj)
|
||||
=> obj.Name;
|
||||
|
||||
private static string GetLabel(DataManager gameData, EquipSlot slot)
|
||||
{
|
||||
var sheet = gameData.GetExcelSheet<Addon>()!;
|
||||
|
||||
return slot switch
|
||||
{
|
||||
EquipSlot.Head => sheet.GetRow(740)?.Text.ToString() ?? "Head",
|
||||
EquipSlot.Body => sheet.GetRow(741)?.Text.ToString() ?? "Body",
|
||||
EquipSlot.Hands => sheet.GetRow(742)?.Text.ToString() ?? "Hands",
|
||||
EquipSlot.Legs => sheet.GetRow(744)?.Text.ToString() ?? "Legs",
|
||||
EquipSlot.Feet => sheet.GetRow(745)?.Text.ToString() ?? "Feet",
|
||||
EquipSlot.Ears => sheet.GetRow(746)?.Text.ToString() ?? "Ears",
|
||||
EquipSlot.Neck => sheet.GetRow(747)?.Text.ToString() ?? "Neck",
|
||||
EquipSlot.Wrists => sheet.GetRow(748)?.Text.ToString() ?? "Wrists",
|
||||
EquipSlot.RFinger => sheet.GetRow(749)?.Text.ToString() ?? "Right Ring",
|
||||
EquipSlot.LFinger => sheet.GetRow(750)?.Text.ToString() ?? "Left Ring",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyList<Item> GetItems(ItemManager items, EquipSlot slot)
|
||||
{
|
||||
var nothing = ItemManager.NothingItem(slot);
|
||||
if (!items.ItemService.AwaitedService.TryGetValue(slot.ToEquipType(), out var list))
|
||||
return new[]
|
||||
{
|
||||
nothing,
|
||||
};
|
||||
|
||||
var enumerable = list.Select(i => new Item(i));
|
||||
if (slot.IsEquipment())
|
||||
enumerable = enumerable.Append(ItemManager.SmallClothesItem(slot));
|
||||
return enumerable.OrderBy(i => i.Name).Prepend(nothing).ToList();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Data;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using ImGuiNET;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Gui.Equipment;
|
||||
|
||||
public sealed class WeaponCombo : FilterComboCache<Weapon>
|
||||
{
|
||||
public readonly string Label;
|
||||
private uint _currentItem;
|
||||
|
||||
public WeaponCombo(DataManager gameData, ItemManager items, FullEquipType type, EquipSlot offhand)
|
||||
: base(offhand is EquipSlot.OffHand ? () => GetOff(items, type) : () => GetMain(items, type))
|
||||
=> Label = GetLabel(gameData, offhand);
|
||||
|
||||
protected override void DrawList(float width, float itemHeight)
|
||||
{
|
||||
base.DrawList(width, itemHeight);
|
||||
if (NewSelection != null && Items.Count > NewSelection.Value)
|
||||
CurrentSelection = Items[NewSelection.Value];
|
||||
}
|
||||
|
||||
protected override int UpdateCurrentSelected(int currentSelected)
|
||||
{
|
||||
if (CurrentSelection.ItemId != _currentItem)
|
||||
{
|
||||
CurrentSelectionIdx = Items.IndexOf(i => i.ItemId == _currentItem);
|
||||
CurrentSelection = CurrentSelectionIdx >= 0 ? Items[CurrentSelectionIdx] : default;
|
||||
return base.UpdateCurrentSelected(CurrentSelectionIdx);
|
||||
}
|
||||
|
||||
return currentSelected;
|
||||
}
|
||||
|
||||
public bool Draw(string previewName, uint previewIdx, float width)
|
||||
{
|
||||
_currentItem = previewIdx;
|
||||
return Draw(Label, previewName, string.Empty, width, ImGui.GetTextLineHeightWithSpacing());
|
||||
}
|
||||
|
||||
protected override bool DrawSelectable(int globalIdx, bool selected)
|
||||
{
|
||||
var obj = Items[globalIdx];
|
||||
var name = ToString(obj);
|
||||
var ret = ImGui.Selectable(name, selected);
|
||||
ImGui.SameLine();
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080);
|
||||
ImGuiUtil.RightAlign($"({obj.ModelBase.Value}-{obj.WeaponBase.Value}-{obj.Variant})");
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected override bool IsVisible(int globalIndex, LowerString filter)
|
||||
=> base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelBase.ToString());
|
||||
|
||||
protected override string ToString(Weapon obj)
|
||||
=> obj.Name;
|
||||
|
||||
private static string GetLabel(DataManager gameData, EquipSlot offhand)
|
||||
{
|
||||
var sheet = gameData.GetExcelSheet<Addon>()!;
|
||||
return offhand is EquipSlot.OffHand
|
||||
? sheet.GetRow(739)?.Text.ToString() ?? "Off Hand"
|
||||
: sheet.GetRow(738)?.Text.ToString() ?? "Main Hand";
|
||||
}
|
||||
|
||||
private static IReadOnlyList<Weapon> GetMain(ItemManager items, FullEquipType type)
|
||||
{
|
||||
var list = new List<Weapon>();
|
||||
if (type is FullEquipType.Unknown)
|
||||
foreach (var t in Enum.GetValues<FullEquipType>().Where(t => t.ToSlot() == EquipSlot.MainHand))
|
||||
list.AddRange(items.ItemService.AwaitedService[t].Select(w => new Weapon(w, false)));
|
||||
else if (type.ToSlot() is EquipSlot.MainHand)
|
||||
list.AddRange(items.ItemService.AwaitedService[type].Select(w => new Weapon(w, false)));
|
||||
list.Sort((w1, w2) => string.CompareOrdinal(w1.Name, w2.Name));
|
||||
return list;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<Weapon> GetOff(ItemManager items, FullEquipType type)
|
||||
{
|
||||
if (type.ToSlot() == EquipSlot.OffHand)
|
||||
{
|
||||
var nothing = ItemManager.NothingItem(type);
|
||||
if (!items.ItemService.AwaitedService.TryGetValue(type, out var list))
|
||||
return new[]
|
||||
{
|
||||
nothing,
|
||||
};
|
||||
|
||||
return list.Select(w => new Weapon(w, true)).OrderBy(w => w.Name).Prepend(nothing).ToList();
|
||||
}
|
||||
else if (items.ItemService.AwaitedService.TryGetValue(type, out var list))
|
||||
{
|
||||
return list.Select(w => new Weapon(w, true)).OrderBy(w => w.Name).ToList();
|
||||
}
|
||||
|
||||
return Array.Empty<Weapon>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Windowing;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public class GlamourerWindowSystem : IDisposable
|
||||
{
|
||||
private readonly WindowSystem _windowSystem = new("Glamourer");
|
||||
private readonly UiBuilder _uiBuilder;
|
||||
private readonly Interface _ui;
|
||||
|
||||
public GlamourerWindowSystem(UiBuilder uiBuilder, Interface ui)
|
||||
{
|
||||
_uiBuilder = uiBuilder;
|
||||
_ui = ui;
|
||||
_windowSystem.AddWindow(ui);
|
||||
_uiBuilder.Draw += _windowSystem.Draw;
|
||||
_uiBuilder.OpenConfigUi += _ui.Toggle;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_uiBuilder.Draw -= _windowSystem.Draw;
|
||||
_uiBuilder.OpenConfigUi -= _ui.Toggle;
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
=> _ui.Toggle();
|
||||
}
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
using ImGui = ImGuiNET.ImGui;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public partial class Interface
|
||||
{
|
||||
private class ActorTab
|
||||
{
|
||||
private readonly Interface _main;
|
||||
private readonly ActiveDesign.Manager _activeDesigns;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly TargetManager _targets;
|
||||
private readonly ActorService _actors;
|
||||
private readonly ItemManager _items;
|
||||
|
||||
public ActorTab(Interface main, ActiveDesign.Manager activeDesigns, ObjectManager objects, TargetManager targets, ActorService actors,
|
||||
ItemManager items)
|
||||
{
|
||||
_main = main;
|
||||
_activeDesigns = activeDesigns;
|
||||
_objects = objects;
|
||||
_targets = targets;
|
||||
_actors = actors;
|
||||
_items = items;
|
||||
}
|
||||
|
||||
private ActorIdentifier _identifier = ActorIdentifier.Invalid;
|
||||
private ActorData _currentData = ActorData.Invalid;
|
||||
private ActiveDesign? _currentSave;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var tab = ImRaii.TabItem("Actors");
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
DrawActorSelector();
|
||||
if (!_objects.TryGetValue(_identifier, out _currentData))
|
||||
_currentData = ActorData.Invalid;
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
DrawPanel();
|
||||
}
|
||||
|
||||
private unsafe void DrawPanel()
|
||||
{
|
||||
if (_identifier == ActorIdentifier.Invalid)
|
||||
return;
|
||||
|
||||
using var group = ImRaii.Group();
|
||||
DrawPanelHeader();
|
||||
using var child = ImRaii.Child("##ActorPanel", -Vector2.One, true);
|
||||
if (!child || _currentSave == null)
|
||||
return;
|
||||
|
||||
if (_currentData.Valid)
|
||||
_currentSave.Initialize(_items, _currentData.Objects[0]);
|
||||
|
||||
RevertButton();
|
||||
ActorDebug.Draw(_currentSave.ModelData);
|
||||
return;
|
||||
if (_main._customizationDrawer.Draw(_currentSave.ModelData.Customize, _identifier.Type == IdentifierType.Special))
|
||||
_activeDesigns.ChangeCustomize(_currentSave, _main._customizationDrawer.Changed, _main._customizationDrawer.Customize.Data,
|
||||
false);
|
||||
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var current = _currentSave.Armor(slot);
|
||||
if (_main._equipmentDrawer.DrawStain(current.Stain, slot, out var stain))
|
||||
_activeDesigns.ChangeStain(_currentSave, slot, stain.RowIndex, false);
|
||||
ImGui.SameLine();
|
||||
if (_main._equipmentDrawer.DrawArmor(current, slot, out var armor, _currentSave.ModelData.Customize.Gender,
|
||||
_currentSave.ModelData.Customize.Race))
|
||||
_activeDesigns.ChangeEquipment(_currentSave, slot, armor, false);
|
||||
}
|
||||
|
||||
var currentMain = _currentSave.WeaponMain;
|
||||
if (_main._equipmentDrawer.DrawStain(currentMain.Stain, EquipSlot.MainHand, out var stainMain))
|
||||
_activeDesigns.ChangeStain(_currentSave, EquipSlot.MainHand, stainMain.RowIndex, false);
|
||||
ImGui.SameLine();
|
||||
_main._equipmentDrawer.DrawMainhand(currentMain, true, out var main);
|
||||
if (currentMain.Type.Offhand() != FullEquipType.Unknown)
|
||||
{
|
||||
var currentOff = _currentSave.WeaponOff;
|
||||
if (_main._equipmentDrawer.DrawStain(currentOff.Stain, EquipSlot.OffHand, out var stainOff))
|
||||
_activeDesigns.ChangeStain(_currentSave, EquipSlot.OffHand, stainOff.RowIndex, false);
|
||||
ImGui.SameLine();
|
||||
_main._equipmentDrawer.DrawOffhand(currentOff, main.Type, out var off);
|
||||
}
|
||||
|
||||
if (_main._equipmentDrawer.DrawVisor(_currentSave, out var value))
|
||||
_activeDesigns.ChangeVisor(_currentSave, value, false);
|
||||
}
|
||||
|
||||
private const uint RedHeaderColor = 0xFF1818C0;
|
||||
private const uint GreenHeaderColor = 0xFF18C018;
|
||||
|
||||
private unsafe void RevertButton()
|
||||
{
|
||||
if (ImGui.Button("Revert"))
|
||||
_activeDesigns.RevertDesign(_currentSave!);
|
||||
//foreach (var actor in _currentData.Objects)
|
||||
// _currentSave!.ApplyToActor(actor);
|
||||
//
|
||||
//if (_currentData.Objects.Count > 0)
|
||||
// _currentSave = _manipulations.GetOrCreateSave(_currentData.Objects[0]);
|
||||
//
|
||||
//_currentSave!.Reset();
|
||||
if (_currentData.Objects.Count > 0)
|
||||
ImGui.TextUnformatted(_currentData.Objects[0].Pointer->GameObject.DataID.ToString());
|
||||
//VisorBox();
|
||||
}
|
||||
|
||||
//private unsafe void VisorBox()
|
||||
//{
|
||||
// var (flags, mask) = (_currentSave!.Data.Flags & (ApplicationFlags.SetVisor | ApplicationFlags.Visor)) switch
|
||||
// {
|
||||
// ApplicationFlags.SetVisor => (0u, 3u),
|
||||
// ApplicationFlags.Visor => (1u, 3u),
|
||||
// ApplicationFlags.SetVisor | ApplicationFlags.Visor => (3u, 3u),
|
||||
// _ => (2u, 3u),
|
||||
// };
|
||||
// var tmp = flags;
|
||||
// if (ImGui.CheckboxFlags("Visor Toggled", ref tmp, mask))
|
||||
// {
|
||||
// _currentSave.Data.Flags = flags switch
|
||||
// {
|
||||
// 0 => (_currentSave.Data.Flags | ApplicationFlags.Visor) & ~ApplicationFlags.SetVisor,
|
||||
// 1 => _currentSave.Data.Flags | ApplicationFlags.SetVisor,
|
||||
// 2 => _currentSave.Data.Flags | ApplicationFlags.SetVisor,
|
||||
// _ => _currentSave.Data.Flags & ~(ApplicationFlags.SetVisor | ApplicationFlags.Visor),
|
||||
// };
|
||||
// if (_currentSave.Data.Flags.HasFlag(ApplicationFlags.SetVisor))
|
||||
// {
|
||||
// var on = _currentSave.Data.Flags.HasFlag(ApplicationFlags.Visor);
|
||||
// foreach (var actor in _currentData.Objects.Where(a => a.IsHuman && a.DrawObject))
|
||||
// RedrawManager.SetVisor(actor.DrawObject.Pointer, on);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
private void DrawPanelHeader()
|
||||
{
|
||||
var color = _currentData.Valid ? GreenHeaderColor : RedHeaderColor;
|
||||
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
|
||||
using var c = ImRaii.PushColor(ImGuiCol.Text, color)
|
||||
.Push(ImGuiCol.Button, buttonColor)
|
||||
.Push(ImGuiCol.ButtonHovered, buttonColor)
|
||||
.Push(ImGuiCol.ButtonActive, buttonColor);
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FrameRounding, 0);
|
||||
ImGui.Button($"{_currentData.Label}##playerHeader", -Vector2.UnitX);
|
||||
}
|
||||
|
||||
//private void DrawActorPanel()
|
||||
//{
|
||||
// using var group = ImRaii.Group();
|
||||
// if (!_data.Identifier.IsValid)
|
||||
// return;
|
||||
//
|
||||
// if (DrawCustomization(_currentSave.Customize, _currentSave.Equipment, !_data.Modifiable))
|
||||
// //Glamourer.RedrawManager.Set(_data.Actor.Address, _character);
|
||||
// Glamourer.Penumbra.RedrawObject(_data.Actor.Character, RedrawType.Redraw, true);
|
||||
//
|
||||
// if (ImGui.Button("Set Machinist Goggles"))
|
||||
// Glamourer.RedrawManager.ChangeEquip(_data.Actor, EquipSlot.Head, new CharacterArmor(265, 1, 0));
|
||||
//
|
||||
// if (ImGui.Button("Set Weapon"))
|
||||
// Glamourer.RedrawManager.LoadWeapon(_data.Actor.Address, new CharacterWeapon(0x00C9, 0x004E, 0x0001, 0x00),
|
||||
// new CharacterWeapon(0x0065, 0x003D, 0x0001, 0x00));
|
||||
//
|
||||
// if (ImGui.Button("Set Customize"))
|
||||
// {
|
||||
// unsafe
|
||||
// {
|
||||
// var data = _data.Actor.Customize.Data->Clone();
|
||||
// Glamourer.RedrawManager.UpdateCustomize(_data.Actor.DrawObject, new Customize(&data)
|
||||
// {
|
||||
// SkinColor = 154,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//private void DrawMonsterPanel()
|
||||
//{
|
||||
// using var group = ImRaii.Group();
|
||||
// var currentModel = (uint)_data.Actor.ModelId;
|
||||
// var models = GameData.Models(Dalamud.GameData);
|
||||
// var currentData = models.Models.TryGetValue(currentModel, out var c) ? c.FirstName : $"#{currentModel}";
|
||||
// using var combo = ImRaii.Combo("Model Id", currentData);
|
||||
// if (!combo)
|
||||
// return;
|
||||
//
|
||||
// foreach (var (id, data) in models.Models)
|
||||
// {
|
||||
// if (ImGui.Selectable(data.FirstName, id == currentModel) && id != currentModel)
|
||||
// {
|
||||
// _data.Actor.SetModelId((int)id);
|
||||
// Glamourer.Penumbra.RedrawObject(_data.Actor.Character, RedrawType.Redraw, true);
|
||||
// }
|
||||
//
|
||||
// ImGuiUtil.HoverTooltip(data.AllNames);
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
private LowerString _actorFilter = LowerString.Empty;
|
||||
|
||||
private void DrawActorSelector()
|
||||
{
|
||||
using var group = ImRaii.Group();
|
||||
var oldSpacing = ImGui.GetStyle().ItemSpacing;
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FrameRounding, 0);
|
||||
ImGui.SetNextItemWidth(_actorSelectorWidth);
|
||||
LowerString.InputWithHint("##actorFilter", "Filter...", ref _actorFilter, 64);
|
||||
|
||||
DrawSelector(oldSpacing);
|
||||
DrawSelectionButtons();
|
||||
}
|
||||
|
||||
private void DrawSelector(Vector2 oldSpacing)
|
||||
{
|
||||
using var child = ImRaii.Child("##actorSelector", new Vector2(_actorSelectorWidth, -ImGui.GetFrameHeight()), true);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
_objects.Update();
|
||||
if (!_activeDesigns.TryGetValue(_identifier, out _currentSave))
|
||||
_currentSave = null;
|
||||
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, oldSpacing);
|
||||
var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeight());
|
||||
var remainder = ImGuiClip.FilteredClippedDraw(_objects, skips, CheckFilter, DrawSelectable);
|
||||
ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeight());
|
||||
if (_currentSave == null)
|
||||
{
|
||||
_identifier = ActorIdentifier.Invalid;
|
||||
_currentData = ActorData.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckFilter(KeyValuePair<ActorIdentifier, ActorData> pair)
|
||||
=> _actorFilter.IsEmpty || pair.Value.Label.Contains(_actorFilter.Lower, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private void DrawSelectable(KeyValuePair<ActorIdentifier, ActorData> pair)
|
||||
{
|
||||
var equal = pair.Key.Equals(_identifier);
|
||||
if (ImGui.Selectable(pair.Value.Label, equal) || equal)
|
||||
{
|
||||
_identifier = pair.Key.CreatePermanent();
|
||||
_currentData = pair.Value;
|
||||
if (!_activeDesigns.TryGetValue(_identifier, out _currentSave))
|
||||
_currentSave = _currentData.Valid ? _activeDesigns.GetOrCreateSave(_currentData.Objects[0]) : null;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSelectionButtons()
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FrameRounding, 0);
|
||||
var buttonWidth = new Vector2(_actorSelectorWidth / 2, 0);
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UserCircle.ToIconString(), buttonWidth
|
||||
, "Select the local player character.", !_objects.Player, true))
|
||||
_identifier = _objects.Player.GetIdentifier(_actors.AwaitedService);
|
||||
|
||||
ImGui.SameLine();
|
||||
Actor targetActor = _targets.Target?.Address;
|
||||
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.HandPointer.ToIconString(), buttonWidth,
|
||||
"Select the current target, if it is in the list.", _objects.IsInGPose || !targetActor, true))
|
||||
_identifier = targetActor.GetIdentifier(_actors.AwaitedService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Util;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public partial class Interface
|
||||
{
|
||||
private class DebugDataTab
|
||||
{
|
||||
private readonly CustomizationService _service;
|
||||
|
||||
public DebugDataTab(CustomizationService service)
|
||||
=> _service = service;
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (!_service.Valid)
|
||||
return;
|
||||
|
||||
using var tab = ImRaii.TabItem("Debug");
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
foreach (var clan in _service.AwaitedService.Clans)
|
||||
{
|
||||
foreach (var gender in _service.AwaitedService.Genders)
|
||||
DrawCustomizationInfo(_service.AwaitedService.GetList(clan, gender));
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawCustomizationInfo(CustomizationSet set)
|
||||
{
|
||||
if (!ImGui.CollapsingHeader($"{CustomizeExtensions.ClanName(_service.AwaitedService, set.Clan, set.Gender)} {set.Gender}"))
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table("data", 5);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn(index.ToString());
|
||||
ImGuiUtil.DrawTableColumn(set.Option(index));
|
||||
ImGuiUtil.DrawTableColumn(set.IsAvailable(index) ? "Available" : "Unavailable");
|
||||
ImGuiUtil.DrawTableColumn(set.Type(index).ToString());
|
||||
ImGuiUtil.DrawTableColumn(set.Count(index).ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using Glamourer.State;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public partial class Interface
|
||||
{
|
||||
private class DebugStateTab
|
||||
{
|
||||
private readonly ActiveDesign.Manager _activeDesigns;
|
||||
|
||||
private LowerString _manipulationFilter = LowerString.Empty;
|
||||
private ActorIdentifier _selection = ActorIdentifier.Invalid;
|
||||
private ActiveDesign? _save = null;
|
||||
private bool _delete = false;
|
||||
|
||||
public DebugStateTab(ActiveDesign.Manager activeDesigns)
|
||||
=> _activeDesigns = activeDesigns;
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public void Draw()
|
||||
{
|
||||
using var tab = ImRaii.TabItem("Current Manipulations");
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
DrawManipulationSelector();
|
||||
if (_save == null)
|
||||
return;
|
||||
|
||||
ImGui.SameLine();
|
||||
DrawActorPanel();
|
||||
if (_delete)
|
||||
{
|
||||
_delete = false;
|
||||
_activeDesigns.DeleteSave(_selection);
|
||||
_selection = ActorIdentifier.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSelector(Vector2 oldSpacing)
|
||||
{
|
||||
using var child = ImRaii.Child("##actorSelector", new Vector2(_actorSelectorWidth, -1), true);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, oldSpacing);
|
||||
var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeight());
|
||||
var remainder = ImGuiClip.FilteredClippedDraw(_activeDesigns, skips, CheckFilter, DrawSelectable);
|
||||
ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeight());
|
||||
}
|
||||
|
||||
private void DrawManipulationSelector()
|
||||
{
|
||||
using var group = ImRaii.Group();
|
||||
var oldSpacing = ImGui.GetStyle().ItemSpacing;
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FrameRounding, 0);
|
||||
ImGui.SetNextItemWidth(_actorSelectorWidth);
|
||||
LowerString.InputWithHint("##actorFilter", "Filter...", ref _manipulationFilter, 64);
|
||||
|
||||
_save = null;
|
||||
DrawSelector(oldSpacing);
|
||||
}
|
||||
|
||||
private bool CheckFilter(KeyValuePair<ActorIdentifier, ActiveDesign> data)
|
||||
{
|
||||
if (data.Key.Equals(_selection))
|
||||
_save = data.Value;
|
||||
return _manipulationFilter.Length == 0 || _manipulationFilter.IsContained(data.Key.ToString()!);
|
||||
}
|
||||
|
||||
private void DrawSelectable(KeyValuePair<ActorIdentifier, ActiveDesign> data)
|
||||
{
|
||||
var equal = data.Key.Equals(_selection);
|
||||
if (ImGui.Selectable(data.Key.ToString(), equal))
|
||||
{
|
||||
_selection = data.Key;
|
||||
_save = data.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawActorPanel()
|
||||
{
|
||||
using var group = ImRaii.Group();
|
||||
if (ImGui.Button("Delete"))
|
||||
_delete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui.Designs;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.State;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public partial class Interface
|
||||
{
|
||||
private class DesignTab : IDisposable
|
||||
{
|
||||
public readonly DesignFileSystemSelector Selector;
|
||||
private readonly Interface _main;
|
||||
private readonly DesignFileSystem _fileSystem;
|
||||
private readonly DesignManager _designManager;
|
||||
private readonly ActiveDesign.Manager _activeDesignManager;
|
||||
private readonly ObjectManager _objects;
|
||||
|
||||
public DesignTab(Interface main, DesignManager designManager, DesignFileSystem fileSystem, KeyState keyState,
|
||||
ActiveDesign.Manager activeDesignManager, ObjectManager objects)
|
||||
{
|
||||
_main = main;
|
||||
_designManager = designManager;
|
||||
_fileSystem = fileSystem;
|
||||
_activeDesignManager = activeDesignManager;
|
||||
_objects = objects;
|
||||
Selector = new DesignFileSystemSelector(designManager, fileSystem, keyState);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> Selector.Dispose();
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var tab = ImRaii.TabItem("Designs");
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
Selector.Draw(GetDesignSelectorSize());
|
||||
ImGui.SameLine();
|
||||
DrawDesignPanel();
|
||||
}
|
||||
|
||||
public float GetDesignSelectorSize()
|
||||
=> 200f * ImGuiHelpers.GlobalScale;
|
||||
|
||||
private void ApplySelfButton()
|
||||
{
|
||||
var self = _objects.Player;
|
||||
if (!ImGuiUtil.DrawDisabledButton("Apply to Self", Vector2.Zero, string.Empty, !self.Valid))
|
||||
return;
|
||||
|
||||
var design = _activeDesignManager.GetOrCreateSave(self);
|
||||
_activeDesignManager.ApplyDesign(design, Selector.Selected!, false);
|
||||
}
|
||||
|
||||
public void DrawDesignPanel()
|
||||
{
|
||||
if (Selector.Selected == null)
|
||||
return;
|
||||
|
||||
using var group = ImRaii.Group();
|
||||
ApplySelfButton();
|
||||
|
||||
using var child = ImRaii.Child("##DesignPanel", new Vector2(-0.001f), true, ImGuiWindowFlags.HorizontalScrollbar);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
ActorDebug.Draw(Selector.Selected.ModelData);
|
||||
_main._customizationDrawer.Draw(Selector.Selected.ModelData.Customize, CustomizeFlagExtensions.All, true);
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var current = Selector.Selected.Armor(slot);
|
||||
_main._equipmentDrawer.DrawStain(current.Stain, slot, out var stain);
|
||||
ImGui.SameLine();
|
||||
_main._equipmentDrawer.DrawArmor(current, slot, out var armor);
|
||||
}
|
||||
|
||||
var currentMain = Selector.Selected.WeaponMain;
|
||||
_main._equipmentDrawer.DrawStain(currentMain.Stain, EquipSlot.MainHand, out var stainMain);
|
||||
ImGui.SameLine();
|
||||
_main._equipmentDrawer.DrawMainhand(currentMain, true, out var main);
|
||||
if (currentMain.Type.Offhand() != FullEquipType.Unknown)
|
||||
{
|
||||
var currentOff = Selector.Selected.WeaponOff;
|
||||
_main._equipmentDrawer.DrawStain(currentOff.Stain, EquipSlot.OffHand, out var stainOff);
|
||||
ImGui.SameLine();
|
||||
_main._equipmentDrawer.DrawOffhand(currentOff, main.Type, out var off);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
using System;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public partial class Interface
|
||||
{
|
||||
private void Checkmark(string label, string tooltip, bool value, Action<bool> setter)
|
||||
{
|
||||
if (ImGuiUtil.Checkbox(label, tooltip, value, setter))
|
||||
_config.Save();
|
||||
}
|
||||
|
||||
private void ChangeAndSave<T>(T value, T currentValue, Action<T> setter) where T : IEquatable<T>
|
||||
{
|
||||
if (value.Equals(currentValue))
|
||||
return;
|
||||
|
||||
setter(value);
|
||||
_config.Save();
|
||||
}
|
||||
|
||||
private void DrawColorPicker(string name, string tooltip, uint value, uint defaultValue, Action<uint> setter)
|
||||
{
|
||||
const ImGuiColorEditFlags flags = ImGuiColorEditFlags.AlphaPreviewHalf | ImGuiColorEditFlags.NoInputs;
|
||||
|
||||
var tmp = ImGui.ColorConvertU32ToFloat4(value);
|
||||
if (ImGui.ColorEdit4($"##{name}", ref tmp, flags))
|
||||
ChangeAndSave(ImGui.ColorConvertFloat4ToU32(tmp), value, setter);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button($"Default##{name}"))
|
||||
ChangeAndSave(defaultValue, value, setter);
|
||||
ImGuiUtil.HoverTooltip(
|
||||
$"Reset to default: #{defaultValue & 0xFF:X2}{(defaultValue >> 8) & 0xFF:X2}{(defaultValue >> 16) & 0xFF:X2}{defaultValue >> 24:X2}");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(name);
|
||||
ImGuiUtil.HoverTooltip(tooltip);
|
||||
}
|
||||
|
||||
private static void DrawRestorePenumbraButton()
|
||||
{
|
||||
//const string buttonLabel = "Re-Register Penumbra";
|
||||
// TODO
|
||||
//if (ImGui.Button(buttonLabel))
|
||||
// Glamourer.Penumbra.Reattach(true);
|
||||
|
||||
//ImGuiUtil.HoverTooltip(
|
||||
// "If Penumbra did not register the functions for some reason, pressing this button might help restore functionality.");
|
||||
}
|
||||
|
||||
private void DrawSettingsTab()
|
||||
{
|
||||
using var tab = ImRaii.TabItem("Settings");
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
ImGui.Dummy(_spacing);
|
||||
|
||||
Checkmark("Folders First", "Sort Folders before all designs instead of lexicographically.", _config.FoldersFirst,
|
||||
v => _config.FoldersFirst = v);
|
||||
Checkmark("Color Designs", "Color the names of designs in the selector using the colors from below for the given cases.",
|
||||
_config.ColorDesigns,
|
||||
v => _config.ColorDesigns = v);
|
||||
Checkmark("Show Locks", "Write-protected Designs show a lock besides their name in the selector.", _config.ShowLocks,
|
||||
v => _config.ShowLocks = v);
|
||||
DrawRestorePenumbraButton();
|
||||
|
||||
Checkmark("Apply Fixed Designs",
|
||||
"Automatically apply fixed designs to characters and redraw them when anything changes.",
|
||||
_config.ApplyFixedDesigns,
|
||||
v => { _config.ApplyFixedDesigns = v; });
|
||||
|
||||
ImGui.Dummy(_spacing);
|
||||
|
||||
DrawColorPicker("Customization Color", "The color for designs that only apply their character customization.",
|
||||
_config.CustomizationColor, ConfigurationOld.DefaultCustomizationColor, c => _config.CustomizationColor = c);
|
||||
DrawColorPicker("Equipment Color", "The color for designs that only apply some or all of their equipment slots and stains.",
|
||||
_config.EquipmentColor, ConfigurationOld.DefaultEquipmentColor, c => _config.EquipmentColor = c);
|
||||
DrawColorPicker("State Color", "The color for designs that only apply some state modification.",
|
||||
_config.StateColor, ConfigurationOld.DefaultStateColor, c => _config.StateColor = c);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public partial class Interface
|
||||
{
|
||||
private static Vector2 _spacing = Vector2.Zero;
|
||||
private static float _actorSelectorWidth;
|
||||
|
||||
private static void UpdateState()
|
||||
{
|
||||
_spacing = _spacing with { Y = ImGui.GetTextLineHeightWithSpacing() / 2 };
|
||||
_actorSelectorWidth = 200 * ImGuiHelpers.GlobalScale;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public partial class Interface : Window, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
|
||||
private readonly EquipmentDrawer _equipmentDrawer;
|
||||
private readonly CustomizationDrawer _customizationDrawer;
|
||||
private readonly ConfigurationOld _config;
|
||||
private readonly ActorTab _actorTab;
|
||||
private readonly DesignTab _designTab;
|
||||
private readonly DebugStateTab _debugStateTab;
|
||||
private readonly DebugDataTab _debugDataTab;
|
||||
|
||||
public Interface(DalamudPluginInterface pi, ItemManager items, ActiveDesign.Manager activeDesigns, DesignManager designManager,
|
||||
DesignFileSystem fileSystem, ObjectManager objects, CustomizationService customization, ConfigurationOld config, DataManager gameData, TargetManager targets, ActorService actors, KeyState keyState)
|
||||
: base(GetLabel())
|
||||
{
|
||||
_pi = pi;
|
||||
_config = config;
|
||||
_equipmentDrawer = new EquipmentDrawer(gameData, items);
|
||||
_customizationDrawer = new CustomizationDrawer(pi, customization, items);
|
||||
pi.UiBuilder.DisableGposeUiHide = true;
|
||||
SizeConstraints = new WindowSizeConstraints()
|
||||
{
|
||||
MinimumSize = new Vector2(675, 675),
|
||||
MaximumSize = ImGui.GetIO().DisplaySize,
|
||||
};
|
||||
_actorTab = new ActorTab(this, activeDesigns, objects, targets, actors, items);
|
||||
_debugStateTab = new DebugStateTab(activeDesigns);
|
||||
_debugDataTab = new DebugDataTab(customization);
|
||||
_designTab = new DesignTab(this, designManager, fileSystem, keyState, activeDesigns, objects);
|
||||
}
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
using var tabBar = ImRaii.TabBar("##Tabs");
|
||||
if (!tabBar)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
UpdateState();
|
||||
|
||||
_actorTab.Draw();
|
||||
_designTab.Draw();
|
||||
DrawSettingsTab();
|
||||
_debugStateTab.Draw();
|
||||
_debugDataTab.Draw();
|
||||
// DrawSaves();
|
||||
// DrawFixedDesignsTab();
|
||||
// DrawRevertablesTab();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PluginLog.Error($"Unexpected Error during Draw:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pi.UiBuilder.OpenConfigUi -= Toggle;
|
||||
_customizationDrawer.Dispose();
|
||||
_designTab.Dispose();
|
||||
}
|
||||
|
||||
private static string GetLabel()
|
||||
=> Glamourer.Version.Length == 0
|
||||
? "Glamourer###GlamourerConfigWindow"
|
||||
: $"Glamourer v{Glamourer.Version}###GlamourerConfigWindow";
|
||||
}
|
||||
|
|
@ -1,290 +0,0 @@
|
|||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
public partial class Interface
|
||||
{
|
||||
//private readonly CharacterSave _currentSave = new();
|
||||
//private string _newDesignName = string.Empty;
|
||||
//private bool _keyboardFocus;
|
||||
//private bool _holdShift;
|
||||
//private bool _holdCtrl;
|
||||
//private const string DesignNamePopupLabel = "Save Design As...";
|
||||
|
||||
//
|
||||
//private void DrawPlayerHeader()
|
||||
//{
|
||||
// var color = _player == null ? RedHeaderColor : GreenHeaderColor;
|
||||
// var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
|
||||
// using var c = ImRaii.PushColor(ImGuiCol.Text, color)
|
||||
// .Push(ImGuiCol.Button, buttonColor)
|
||||
// .Push(ImGuiCol.ButtonHovered, buttonColor)
|
||||
// .Push(ImGuiCol.ButtonActive, buttonColor);
|
||||
// using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
// .Push(ImGuiStyleVar.FrameRounding, 0);
|
||||
// ImGui.Button($"{_currentLabel}##playerHeader", -Vector2.UnitX * 0.0001f);
|
||||
//}
|
||||
//
|
||||
//private static void DrawCopyClipboardButton(CharacterSave save)
|
||||
//{
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// if (ImGui.Button(FontAwesomeIcon.Clipboard.ToIconString()))
|
||||
// ImGui.SetClipboardText(save.ToBase64());
|
||||
// ImGui.PopFont();
|
||||
// ImGuiUtil.HoverTooltip("Copy customization code to clipboard.");
|
||||
//}
|
||||
//
|
||||
//private static void ConditionalApply(CharacterSave save, Character player)
|
||||
//{
|
||||
// if (ImGui.GetIO().KeyShift)
|
||||
// save.ApplyOnlyCustomizations(player);
|
||||
// else if (ImGui.GetIO().KeyCtrl)
|
||||
// save.ApplyOnlyEquipment(player);
|
||||
// else
|
||||
// save.Apply(player);
|
||||
//}
|
||||
//
|
||||
//private static CharacterSave ConditionalCopy(CharacterSave save, bool shift, bool ctrl)
|
||||
//{
|
||||
// var copy = save.Copy();
|
||||
// if (shift)
|
||||
// {
|
||||
// copy.Load(new CharacterEquipment());
|
||||
// copy.SetHatState = false;
|
||||
// copy.SetVisorState = false;
|
||||
// copy.SetWeaponState = false;
|
||||
// copy.WriteEquipment = CharacterEquipMask.None;
|
||||
// }
|
||||
// else if (ctrl)
|
||||
// {
|
||||
// copy.Load(CharacterCustomization.Default);
|
||||
// copy.SetHatState = false;
|
||||
// copy.SetVisorState = false;
|
||||
// copy.SetWeaponState = false;
|
||||
// copy.WriteCustomizations = false;
|
||||
// }
|
||||
//
|
||||
// return copy;
|
||||
//}
|
||||
//
|
||||
//private bool DrawApplyClipboardButton()
|
||||
//{
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// var applyButton = ImGui.Button(FontAwesomeIcon.Paste.ToIconString()) && _player != null;
|
||||
// ImGui.PopFont();
|
||||
// ImGuiUtil.HoverTooltip(
|
||||
// "Apply customization code from clipboard.\nHold Shift to apply only customizations.\nHold Control to apply only equipment.");
|
||||
//
|
||||
// if (!applyButton)
|
||||
// return false;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// var text = ImGui.GetClipboardText();
|
||||
// if (!text.Any())
|
||||
// return false;
|
||||
//
|
||||
// var save = CharacterSave.FromString(text);
|
||||
// ConditionalApply(save, _player!);
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// PluginLog.Information($"{e}");
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
//}
|
||||
//
|
||||
//private void DrawSaveDesignButton()
|
||||
//{
|
||||
// ImGui.PushFont(UiBuilder.IconFont);
|
||||
// if (ImGui.Button(FontAwesomeIcon.Save.ToIconString()))
|
||||
// OpenDesignNamePopup(DesignNameUse.SaveCurrent);
|
||||
//
|
||||
// ImGui.PopFont();
|
||||
// ImGuiUtil.HoverTooltip("Save the current design.\nHold Shift to save only customizations.\nHold Control to save only equipment.");
|
||||
//
|
||||
// DrawDesignNamePopup(DesignNameUse.SaveCurrent);
|
||||
//}
|
||||
//
|
||||
//private void DrawTargetPlayerButton()
|
||||
//{
|
||||
// if (ImGui.Button("Target Player"))
|
||||
// Dalamud.Targets.SetTarget(_player);
|
||||
//}
|
||||
//
|
||||
//private void DrawApplyToPlayerButton(CharacterSave save)
|
||||
//{
|
||||
// if (!ImGui.Button("Apply to Self"))
|
||||
// return;
|
||||
//
|
||||
// var player = _inGPose
|
||||
// ? (Character?)Dalamud.Objects[GPoseObjectId]
|
||||
// : Dalamud.ClientState.LocalPlayer;
|
||||
// var fallback = _inGPose ? Dalamud.ClientState.LocalPlayer : null;
|
||||
// if (player == null)
|
||||
// return;
|
||||
//
|
||||
// ConditionalApply(save, player);
|
||||
// if (_inGPose)
|
||||
// ConditionalApply(save, fallback!);
|
||||
// Glamourer.Penumbra.UpdateCharacters(player, fallback);
|
||||
//}
|
||||
//
|
||||
//
|
||||
//private static Character? TransformToCustomizable(Character? actor)
|
||||
//{
|
||||
// if (actor == null)
|
||||
// return null;
|
||||
//
|
||||
// if (actor.ModelType() == 0)
|
||||
// return actor;
|
||||
//
|
||||
// actor.SetModelType(0);
|
||||
// CharacterCustomization.Default.Write(actor.Address);
|
||||
// return actor;
|
||||
//}
|
||||
//
|
||||
//private void DrawApplyToTargetButton(CharacterSave save)
|
||||
//{
|
||||
// if (!ImGui.Button("Apply to Target"))
|
||||
// return;
|
||||
//
|
||||
// var player = TransformToCustomizable(CharacterFactory.Convert(Dalamud.Targets.Target));
|
||||
// if (player == null)
|
||||
// return;
|
||||
//
|
||||
// var fallBackCharacter = _gPoseActors.TryGetValue(player.Name.ToString(), out var f) ? f : null;
|
||||
// ConditionalApply(save, player);
|
||||
// if (fallBackCharacter != null)
|
||||
// ConditionalApply(save, fallBackCharacter!);
|
||||
// Glamourer.Penumbra.UpdateCharacters(player, fallBackCharacter);
|
||||
//}
|
||||
//
|
||||
//private void DrawRevertButton()
|
||||
//{
|
||||
// if (!ImGuiUtil.DrawDisabledButton("Revert", Vector2.Zero, string.Empty, _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
|
||||
// {
|
||||
// var (folder, name) = _designs.FileSystem.CreateAllFolders(_newDesignName);
|
||||
// if (!name.Any())
|
||||
// return;
|
||||
//
|
||||
// var newDesign = new Design(folder, name) { Data = save };
|
||||
// folder.AddChild(newDesign);
|
||||
// _designs.Designs[newDesign.FullName()] = save;
|
||||
// _designs.SaveToFile();
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// PluginLog.Error($"Could not save new design {_newDesignName}:\n{e}");
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//private void DrawMonsterPanel()
|
||||
//{
|
||||
// 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 = _player!.ModelType();
|
||||
// using var combo = ImRaii.Combo("Model Id", currentModel.ToString());
|
||||
// if (!combo)
|
||||
// return;
|
||||
//
|
||||
// foreach (var (id, _) in _models.Skip(1))
|
||||
// {
|
||||
// if (!ImGui.Selectable($"{id:D6}##models", id == currentModel) || id == currentModel)
|
||||
// continue;
|
||||
//
|
||||
// _player!.SetModelType((int)id);
|
||||
// Glamourer.Penumbra.UpdateCharacters(_player!);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//private void DrawPlayerPanel()
|
||||
//{
|
||||
// DrawCopyClipboardButton(_currentSave);
|
||||
// ImGui.SameLine();
|
||||
// var changes = !_currentSave.WriteProtected && DrawApplyClipboardButton();
|
||||
// ImGui.SameLine();
|
||||
// DrawSaveDesignButton();
|
||||
// ImGui.SameLine();
|
||||
// DrawApplyToPlayerButton(_currentSave);
|
||||
// if (!_inGPose)
|
||||
// {
|
||||
// ImGui.SameLine();
|
||||
// DrawApplyToTargetButton(_currentSave);
|
||||
// if (_player != null && !_currentSave.WriteProtected)
|
||||
// {
|
||||
// ImGui.SameLine();
|
||||
// DrawTargetPlayerButton();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var data = _currentSave;
|
||||
// if (!_currentSave.WriteProtected)
|
||||
// {
|
||||
// ImGui.SameLine();
|
||||
// DrawRevertButton();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 0.8f);
|
||||
// data = data.Copy();
|
||||
// }
|
||||
//
|
||||
// if (DrawCustomization(ref data.Customizations) && _player != null)
|
||||
// {
|
||||
// Glamourer.RevertableDesigns.Add(_player);
|
||||
// _currentSave.Customizations.Write(_player.Address);
|
||||
// changes = true;
|
||||
// }
|
||||
//
|
||||
// changes |= DrawEquip(data.Equipment);
|
||||
// changes |= DrawMiscellaneous(data, _player);
|
||||
//
|
||||
// if (_player != null && changes)
|
||||
// Glamourer.Penumbra.UpdateCharacters(_player);
|
||||
// if (_currentSave.WriteProtected)
|
||||
// ImGui.PopStyleVar();
|
||||
//}
|
||||
//
|
||||
//private void DrawActorPanel()
|
||||
//{
|
||||
// using var group = ImRaii.Group();
|
||||
// DrawPlayerHeader();
|
||||
// using var child = ImRaii.Child("##playerData", -Vector2.One, true);
|
||||
// if (!child)
|
||||
// return;
|
||||
//
|
||||
// if (_player == null || _player.ModelType() == 0)
|
||||
// DrawPlayerPanel();
|
||||
// else
|
||||
// DrawMonsterPanel();
|
||||
//}
|
||||
}
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
using Dalamud.Interface;
|
||||
using Glamourer.Structs;
|
||||
using ImGuiNET;
|
||||
using Lumina.Text;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
//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 change = stainCombo.Draw(string.Empty, out var newStain) && !newStain.RowIndex.Equals(stainIdx);
|
||||
// if (!change && (byte)stainIdx != 0)
|
||||
// {
|
||||
// ImGuiUtil.HoverTooltip("Right-click to clear.");
|
||||
// if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
// {
|
||||
// change = true;
|
||||
// newStain = Stain.None;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!change)
|
||||
// return false;
|
||||
//
|
||||
// if (_player == null)
|
||||
// return _inDesignMode && (_selection?.Data.WriteStain(slot, newStain.RowIndex) ?? false);
|
||||
//
|
||||
// Glamourer.RevertableDesigns.Add(_player);
|
||||
// newStain.Write(_player.Address, slot);
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// private bool DrawItemSelector(ComboWithFilter<Item> equipCombo, Lumina.Excel.GeneratedSheets.Item item, EquipSlot slot = EquipSlot.Unknown)
|
||||
// {
|
||||
// var currentName = item.Name.ToString();
|
||||
// var change = equipCombo.Draw(currentName, out var newItem, _itemComboWidth) && newItem.Base.RowId != item.RowId;
|
||||
// if (!change && !ReferenceEquals(item, SmallClothes))
|
||||
// {
|
||||
// ImGuiUtil.HoverTooltip("Right-click to clear.");
|
||||
// if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
|
||||
// {
|
||||
// change = true;
|
||||
// newItem = Item.Nothing(slot);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (!change)
|
||||
// return false;
|
||||
//
|
||||
// newItem = new Item(newItem.Base, newItem.Name, slot);
|
||||
// if (_player == null)
|
||||
// return _inDesignMode && (_selection?.Data.WriteItem(newItem) ?? false);
|
||||
//
|
||||
// Glamourer.RevertableDesigns.Add(_player);
|
||||
// newItem.Write(_player.Address);
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// private static bool DrawCheckbox(CharacterEquipMask flag, ref CharacterEquipMask mask)
|
||||
// {
|
||||
// var tmp = (uint)mask;
|
||||
// var ret = false;
|
||||
// if (ImGui.CheckboxFlags($"##flag_{(uint)flag}", ref tmp, (uint)flag) && tmp != (uint)mask)
|
||||
// {
|
||||
// mask = (CharacterEquipMask)tmp;
|
||||
// ret = true;
|
||||
// }
|
||||
//
|
||||
// if (ImGui.IsItemHovered())
|
||||
// ImGui.SetTooltip("Enable writing this slot in this save.");
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// private static readonly Lumina.Excel.GeneratedSheets.Item SmallClothes = new()
|
||||
// {
|
||||
// Name = new SeString("Nothing"),
|
||||
// RowId = 0,
|
||||
// };
|
||||
//
|
||||
// private static readonly Lumina.Excel.GeneratedSheets.Item SmallClothesNpc = new()
|
||||
// {
|
||||
// Name = new SeString("Smallclothes (NPC)"),
|
||||
// RowId = 1,
|
||||
// };
|
||||
//
|
||||
// private static readonly Lumina.Excel.GeneratedSheets.Item Unknown = new()
|
||||
// {
|
||||
// Name = new SeString("Unknown"),
|
||||
// RowId = 2,
|
||||
// };
|
||||
//
|
||||
// private Lumina.Excel.GeneratedSheets.Item Identify(SetId set, WeaponType weapon, ushort variant, EquipSlot slot)
|
||||
// {
|
||||
// return (uint)set switch
|
||||
// {
|
||||
// 0 => SmallClothes,
|
||||
// 9903 => SmallClothesNpc,
|
||||
// _ => _identifier.Identify(set, weapon, variant, slot.ToSlot()).FirstOrDefault() ?? Unknown,
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// private bool DrawEquipSlot(EquipSlot slot, CharacterArmor equip)
|
||||
// {
|
||||
// var (equipCombo, stainCombo) = _combos[slot];
|
||||
//
|
||||
// var ret = DrawStainSelector(stainCombo, slot, equip.Stain);
|
||||
// ImGui.SameLine();
|
||||
// var item = Identify(equip.Set, new WeaponType(), equip.Variant, slot);
|
||||
// ret |= DrawItemSelector(equipCombo, item, slot);
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// private bool DrawEquipSlotWithCheck(EquipSlot slot, CharacterArmor equip, CharacterEquipMask flag, ref CharacterEquipMask mask)
|
||||
// {
|
||||
// var ret = DrawCheckbox(flag, ref mask);
|
||||
// ImGui.SameLine();
|
||||
// ret |= DrawEquipSlot(slot, equip);
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// private bool DrawWeapon(EquipSlot slot, CharacterWeapon weapon)
|
||||
// {
|
||||
// var (equipCombo, stainCombo) = _combos[slot];
|
||||
//
|
||||
// var ret = DrawStainSelector(stainCombo, slot, weapon.Stain);
|
||||
// ImGui.SameLine();
|
||||
// var item = Identify(weapon.Set, weapon.Type, weapon.Variant, slot);
|
||||
// ret |= DrawItemSelector(equipCombo, item, slot);
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// private bool DrawWeaponWithCheck(EquipSlot slot, CharacterWeapon weapon, CharacterEquipMask flag, ref CharacterEquipMask mask)
|
||||
// {
|
||||
// var ret = DrawCheckbox(flag, ref mask);
|
||||
// ImGui.SameLine();
|
||||
// ret |= DrawWeapon(slot, weapon);
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// private bool DrawEquip(CharacterEquipment equip)
|
||||
// {
|
||||
// var ret = false;
|
||||
// if (ImGui.CollapsingHeader("Character Equipment"))
|
||||
// {
|
||||
// ret |= DrawWeapon(EquipSlot.MainHand, equip.MainHand);
|
||||
// ret |= DrawWeapon(EquipSlot.OffHand, equip.OffHand);
|
||||
// ret |= DrawEquipSlot(EquipSlot.Head, equip.Head);
|
||||
// ret |= DrawEquipSlot(EquipSlot.Body, equip.Body);
|
||||
// ret |= DrawEquipSlot(EquipSlot.Hands, equip.Hands);
|
||||
// ret |= DrawEquipSlot(EquipSlot.Legs, equip.Legs);
|
||||
// ret |= DrawEquipSlot(EquipSlot.Feet, equip.Feet);
|
||||
// ret |= DrawEquipSlot(EquipSlot.Ears, equip.Ears);
|
||||
// ret |= DrawEquipSlot(EquipSlot.Neck, equip.Neck);
|
||||
// ret |= DrawEquipSlot(EquipSlot.Wrists, equip.Wrists);
|
||||
// ret |= DrawEquipSlot(EquipSlot.RFinger, equip.RFinger);
|
||||
// ret |= DrawEquipSlot(EquipSlot.LFinger, equip.LFinger);
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// private bool DrawEquip(CharacterEquipment equip, ref CharacterEquipMask mask)
|
||||
// {
|
||||
// var ret = false;
|
||||
// if (ImGui.CollapsingHeader("Character Equipment"))
|
||||
// {
|
||||
// ret |= DrawWeaponWithCheck(EquipSlot.MainHand, equip.MainHand, CharacterEquipMask.MainHand, ref mask);
|
||||
// ret |= DrawWeaponWithCheck(EquipSlot.OffHand, equip.OffHand, CharacterEquipMask.OffHand, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.Head, equip.Head, CharacterEquipMask.Head, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.Body, equip.Body, CharacterEquipMask.Body, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.Hands, equip.Hands, CharacterEquipMask.Hands, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.Legs, equip.Legs, CharacterEquipMask.Legs, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.Feet, equip.Feet, CharacterEquipMask.Feet, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.Ears, equip.Ears, CharacterEquipMask.Ears, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.Neck, equip.Neck, CharacterEquipMask.Neck, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.Wrists, equip.Wrists, CharacterEquipMask.Wrists, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.RFinger, equip.RFinger, CharacterEquipMask.RFinger, ref mask);
|
||||
// ret |= DrawEquipSlotWithCheck(EquipSlot.LFinger, equip.LFinger, CharacterEquipMask.LFinger, ref mask);
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Structs;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
//internal partial class Interface
|
||||
//{
|
||||
// private const string FixDragDropLabel = "##FixDragDrop";
|
||||
//
|
||||
// private List<string>? _fullPathCache;
|
||||
// private string _newFixCharacterName = string.Empty;
|
||||
// private string _newFixDesignPath = string.Empty;
|
||||
// private JobGroup? _newFixDesignGroup;
|
||||
// private Design? _newFixDesign;
|
||||
// private int _fixDragDropIdx = -1;
|
||||
//
|
||||
// private static unsafe bool IsDropping()
|
||||
// => ImGui.AcceptDragDropPayload(FixDragDropLabel).NativePtr != null;
|
||||
//
|
||||
// private void DrawFixedDesignsTab()
|
||||
// {
|
||||
// _newFixDesignGroup ??= Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
|
||||
//
|
||||
// using var tabItem = ImRaii.TabItem("Fixed Designs");
|
||||
// if (!tabItem)
|
||||
// {
|
||||
// _fullPathCache = null;
|
||||
// _newFixDesign = null;
|
||||
// _newFixDesignPath = string.Empty;
|
||||
// _newFixDesignGroup = Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// _fullPathCache ??= Glamourer.FixedDesignManager.FixedDesigns.Data.Select(d => d.Design.FullName()).ToList();
|
||||
//
|
||||
// using var table = ImRaii.Table("##FixedTable", 4);
|
||||
// var buttonWidth = 23.5f * ImGuiHelpers.GlobalScale;
|
||||
//
|
||||
// ImGui.TableSetupColumn("##DeleteColumn", ImGuiTableColumnFlags.WidthFixed, 2 * buttonWidth);
|
||||
// ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthFixed, 200 * ImGuiHelpers.GlobalScale);
|
||||
// ImGui.TableSetupColumn("Jobs", ImGuiTableColumnFlags.WidthFixed, 175 * ImGuiHelpers.GlobalScale);
|
||||
// ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthStretch);
|
||||
// ImGui.TableHeadersRow();
|
||||
// var xPos = 0f;
|
||||
//
|
||||
// using var style = new ImRaii.Style();
|
||||
// using var font = new ImRaii.Font();
|
||||
// for (var i = 0; i < _fullPathCache.Count; ++i)
|
||||
// {
|
||||
// var path = _fullPathCache[i];
|
||||
// var name = Glamourer.FixedDesignManager.FixedDesigns.Data[i];
|
||||
//
|
||||
// ImGui.TableNextRow();
|
||||
// ImGui.TableNextColumn();
|
||||
// style.Push(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing / 2);
|
||||
// font.Push(UiBuilder.IconFont);
|
||||
// if (ImGui.Button($"{FontAwesomeIcon.Trash.ToIconChar()}##{i}"))
|
||||
// {
|
||||
// _fullPathCache.RemoveAt(i--);
|
||||
// Glamourer.FixedDesignManager.FixedDesigns.Remove(name);
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// var tmp = name.Enabled;
|
||||
// ImGui.SameLine();
|
||||
// xPos = ImGui.GetCursorPosX();
|
||||
// if (ImGui.Checkbox($"##Enabled{i}", ref tmp))
|
||||
// if (tmp && Glamourer.FixedDesignManager.FixedDesigns.EnableDesign(name)
|
||||
// || !tmp && Glamourer.FixedDesignManager.FixedDesigns.DisableDesign(name))
|
||||
// {
|
||||
// Glamourer.Config.FixedDesigns[i].Enabled = tmp;
|
||||
// Glamourer.Config.Save();
|
||||
// }
|
||||
//
|
||||
// style.Pop();
|
||||
// font.Pop();
|
||||
// ImGui.TableNextColumn();
|
||||
// ImGui.Selectable($"{name.Name}##Fix{i}");
|
||||
// if (ImGui.BeginDragDropSource())
|
||||
// {
|
||||
// _fixDragDropIdx = i;
|
||||
// ImGui.SetDragDropPayload("##FixDragDrop", IntPtr.Zero, 0);
|
||||
// ImGui.Text($"Dragging {name.Name} ({path})...");
|
||||
// ImGui.EndDragDropSource();
|
||||
// }
|
||||
//
|
||||
// if (ImGui.BeginDragDropTarget())
|
||||
// {
|
||||
// if (IsDropping() && _fixDragDropIdx >= 0)
|
||||
// {
|
||||
// var d = Glamourer.FixedDesignManager.FixedDesigns.Data[_fixDragDropIdx];
|
||||
// Glamourer.FixedDesignManager.FixedDesigns.Move(d, i);
|
||||
// var p = _fullPathCache[_fixDragDropIdx];
|
||||
// _fullPathCache.RemoveAt(_fixDragDropIdx);
|
||||
// _fullPathCache.Insert(i, p);
|
||||
// _fixDragDropIdx = -1;
|
||||
// }
|
||||
//
|
||||
// ImGui.EndDragDropTarget();
|
||||
// }
|
||||
//
|
||||
// ImGui.TableNextColumn();
|
||||
// ImGui.Text(Glamourer.FixedDesignManager.FixedDesigns.Data[i].Jobs.Name);
|
||||
// ImGui.TableNextColumn();
|
||||
// ImGui.Text(path);
|
||||
// }
|
||||
//
|
||||
// ImGui.TableNextRow();
|
||||
// ImGui.TableNextColumn();
|
||||
// font.Push(UiBuilder.IconFont);
|
||||
//
|
||||
// ImGui.SetCursorPosX(xPos);
|
||||
// if (_newFixDesign == null || _newFixCharacterName == string.Empty)
|
||||
// {
|
||||
// style.Push(ImGuiStyleVar.Alpha, 0.5f);
|
||||
// ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix");
|
||||
// style.Pop();
|
||||
// }
|
||||
// else if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix"))
|
||||
// {
|
||||
// _fullPathCache.Add(_newFixDesignPath);
|
||||
// Glamourer.FixedDesignManager.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, _newFixDesignGroup.Value, false);
|
||||
// _newFixCharacterName = string.Empty;
|
||||
// _newFixDesignPath = string.Empty;
|
||||
// _newFixDesign = null;
|
||||
// _newFixDesignGroup = Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
|
||||
// }
|
||||
//
|
||||
// font.Pop();
|
||||
// ImGui.TableNextColumn();
|
||||
// ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
||||
// ImGui.InputTextWithHint("##NewFix", "Enter new Character", ref _newFixCharacterName, 32);
|
||||
// ImGui.TableNextColumn();
|
||||
// ImGui.SetNextItemWidth(-1);
|
||||
// using var combo = ImRaii.Combo("##NewFixDesignGroup", _newFixDesignGroup.Value.Name);
|
||||
// if (combo)
|
||||
// foreach (var (id, group) in Glamourer.FixedDesignManager.FixedDesigns.JobGroups)
|
||||
// {
|
||||
// ImGui.SetNextItemWidth(-1);
|
||||
// if (ImGui.Selectable($"{group.Name}##NewFixDesignGroup", group.Name == _newFixDesignGroup.Value.Name))
|
||||
// _newFixDesignGroup = group;
|
||||
// }
|
||||
//
|
||||
// ImGui.TableNextColumn();
|
||||
// ImGui.SetNextItemWidth(-1);
|
||||
// using var combo2 = ImRaii.Combo("##NewFixPath", _newFixDesignPath);
|
||||
// if (!combo2)
|
||||
// return;
|
||||
//
|
||||
// foreach (var design in _plugin.Designs.FileSystem.Root.AllLeaves(SortMode.Lexicographical).Cast<Design>())
|
||||
// {
|
||||
// var fullName = design.FullName();
|
||||
// ImGui.SetNextItemWidth(-1);
|
||||
// if (!ImGui.Selectable($"{fullName}##NewFixDesign", fullName == _newFixDesignPath))
|
||||
// continue;
|
||||
//
|
||||
// _newFixDesignPath = fullName;
|
||||
// _newFixDesign = design;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Logging;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Structs;
|
||||
using ImGuiNET;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
//internal partial class Interface
|
||||
//{
|
||||
//
|
||||
|
||||
//
|
||||
|
||||
//
|
||||
//
|
||||
// private enum DesignNameUse
|
||||
// {
|
||||
// SaveCurrent,
|
||||
// NewDesign,
|
||||
// DuplicateDesign,
|
||||
// NewFolder,
|
||||
// FromClipboard,
|
||||
// }
|
||||
//
|
||||
// private void DrawDesignNamePopup(DesignNameUse use)
|
||||
// {
|
||||
// if (ImGui.BeginPopup($"{DesignNamePopupLabel}{use}"))
|
||||
// {
|
||||
// if (ImGui.InputText("##designName", ref _newDesignName, 64, ImGuiInputTextFlags.EnterReturnsTrue)
|
||||
// && _newDesignName.Any())
|
||||
// {
|
||||
// switch (use)
|
||||
// {
|
||||
// case DesignNameUse.SaveCurrent:
|
||||
// SaveNewDesign(ConditionalCopy(_currentSave, _holdShift, _holdCtrl));
|
||||
// break;
|
||||
// case DesignNameUse.NewDesign:
|
||||
// var empty = new CharacterSave();
|
||||
// empty.Load(CharacterCustomization.Default);
|
||||
// empty.WriteCustomizations = false;
|
||||
// SaveNewDesign(empty);
|
||||
// break;
|
||||
// case DesignNameUse.DuplicateDesign:
|
||||
// SaveNewDesign(ConditionalCopy(_selection!.Data, _holdShift, _holdCtrl));
|
||||
// break;
|
||||
// case DesignNameUse.NewFolder:
|
||||
// _designs.FileSystem
|
||||
// .CreateAllFolders($"{_newDesignName}/a"); // Filename is just ignored, but all folders are created.
|
||||
// break;
|
||||
// case DesignNameUse.FromClipboard:
|
||||
// try
|
||||
// {
|
||||
// var text = ImGui.GetClipboardText();
|
||||
// var save = CharacterSave.FromString(text);
|
||||
// SaveNewDesign(save);
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// PluginLog.Information($"Could not save new Design from Clipboard:\n{e}");
|
||||
// }
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// _newDesignName = string.Empty;
|
||||
// ImGui.CloseCurrentPopup();
|
||||
// }
|
||||
//
|
||||
// if (_keyboardFocus)
|
||||
// {
|
||||
// ImGui.SetKeyboardFocusHere();
|
||||
// _keyboardFocus = false;
|
||||
// }
|
||||
//
|
||||
// ImGui.EndPopup();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void OpenDesignNamePopup(DesignNameUse use)
|
||||
// {
|
||||
// _newDesignName = string.Empty;
|
||||
// _keyboardFocus = true;
|
||||
// _holdCtrl = ImGui.GetIO().KeyCtrl;
|
||||
// _holdShift = ImGui.GetIO().KeyShift;
|
||||
// ImGui.OpenPopup($"{DesignNamePopupLabel}{use}");
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using ImGuiNET;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
//internal partial class Interface
|
||||
//{
|
||||
// private const float ColorButtonWidth = 22.5f;
|
||||
// private const float ColorComboWidth = 140f;
|
||||
// private const float ItemComboWidth = 350f;
|
||||
//
|
||||
// private static readonly Vector4 GreyVector = new(0.5f, 0.5f, 0.5f, 1);
|
||||
//
|
||||
// private static ComboWithFilter<Stain> CreateDefaultStainCombo(IReadOnlyList<Stain> stains)
|
||||
// => new("##StainCombo", ColorComboWidth, ColorButtonWidth, stains,
|
||||
// s => s.Name.ToString())
|
||||
// {
|
||||
// Flags = ImGuiComboFlags.NoArrowButton | ImGuiComboFlags.HeightLarge,
|
||||
// PreList = () =>
|
||||
// {
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, Vector2.Zero);
|
||||
// ImGui.PushStyleVar(ImGuiStyleVar.FrameRounding, 0);
|
||||
// },
|
||||
// PostList = () => { ImGui.PopStyleVar(3); },
|
||||
// CreateSelectable = s =>
|
||||
// {
|
||||
// var push = PushColor(s);
|
||||
// var ret = ImGui.Button($"{s.Name}##Stain{(byte)s.RowIndex}",
|
||||
// Vector2.UnitX * (ColorComboWidth - ImGui.GetStyle().ScrollbarSize));
|
||||
// ImGui.PopStyleColor(push);
|
||||
// return ret;
|
||||
// },
|
||||
// ItemsAtOnce = 12,
|
||||
// };
|
||||
//
|
||||
// private ComboWithFilter<Item> CreateItemCombo(EquipSlot slot, IReadOnlyList<Item> items)
|
||||
// => new($"{_equipSlotNames[slot]}##Equip", ItemComboWidth, ItemComboWidth, items, i => i.Name)
|
||||
// {
|
||||
// Flags = ImGuiComboFlags.HeightLarge,
|
||||
// CreateSelectable = i =>
|
||||
// {
|
||||
// var ret = ImGui.Selectable(i.Name);
|
||||
// var setId = $"({(int)i.MainModel.id})";
|
||||
// var size = ImGui.CalcTextSize(setId).X;
|
||||
// ImGui.SameLine(ImGui.GetWindowContentRegionWidth() - size - ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
// ImGui.TextColored(GreyVector, setId);
|
||||
// return ret;
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// private (ComboWithFilter<Item>, ComboWithFilter<Stain>) CreateCombos(EquipSlot slot, IReadOnlyList<Item> items,
|
||||
// ComboWithFilter<Stain> defaultStain)
|
||||
// => (CreateItemCombo(slot, items), new ComboWithFilter<Stain>($"##{slot}Stain", defaultStain));
|
||||
//
|
||||
|
||||
//
|
||||
// private static Dictionary<EquipSlot, string> GetEquipSlotNames()
|
||||
// {
|
||||
// var sheet = Dalamud.GameData.GetExcelSheet<Addon>()!;
|
||||
// var ret = new Dictionary<EquipSlot, string>(12)
|
||||
// {
|
||||
// [EquipSlot.MainHand] = sheet.GetRow(738)?.Text.ToString() ?? "Main Hand",
|
||||
// [EquipSlot.OffHand] = sheet.GetRow(739)?.Text.ToString() ?? "Off Hand",
|
||||
// [EquipSlot.Head] = sheet.GetRow(740)?.Text.ToString() ?? "Head",
|
||||
// [EquipSlot.Body] = sheet.GetRow(741)?.Text.ToString() ?? "Body",
|
||||
// [EquipSlot.Hands] = sheet.GetRow(742)?.Text.ToString() ?? "Hands",
|
||||
// [EquipSlot.Legs] = sheet.GetRow(744)?.Text.ToString() ?? "Legs",
|
||||
// [EquipSlot.Feet] = sheet.GetRow(745)?.Text.ToString() ?? "Feet",
|
||||
// [EquipSlot.Ears] = sheet.GetRow(746)?.Text.ToString() ?? "Ears",
|
||||
// [EquipSlot.Neck] = sheet.GetRow(747)?.Text.ToString() ?? "Neck",
|
||||
// [EquipSlot.Wrists] = sheet.GetRow(748)?.Text.ToString() ?? "Wrists",
|
||||
// [EquipSlot.RFinger] = sheet.GetRow(749)?.Text.ToString() ?? "Right Ring",
|
||||
// [EquipSlot.LFinger] = sheet.GetRow(750)?.Text.ToString() ?? "Left Ring",
|
||||
// };
|
||||
// return ret;
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
using System;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Glamourer.Gui;
|
||||
|
||||
//internal partial class Interface
|
||||
//{
|
||||
//
|
||||
// private static bool DrawMiscellaneous(CharacterSave save, Character? player)
|
||||
// {
|
||||
// var ret = false;
|
||||
// if (!ImGui.CollapsingHeader("Miscellaneous"))
|
||||
// return ret;
|
||||
//
|
||||
// ret |= DrawCheckMark("Hat Visible", save.HatState, v =>
|
||||
// {
|
||||
// save.HatState = v;
|
||||
// player?.SetHatVisible(v);
|
||||
// });
|
||||
//
|
||||
// ret |= DrawCheckMark("Weapon Visible", save.WeaponState, v =>
|
||||
// {
|
||||
// save.WeaponState = v;
|
||||
// player?.SetWeaponHidden(!v);
|
||||
// });
|
||||
//
|
||||
// ret |= DrawCheckMark("Visor Toggled", save.VisorState, v =>
|
||||
// {
|
||||
// save.VisorState = v;
|
||||
// player?.SetVisorToggled(v);
|
||||
// });
|
||||
//
|
||||
// ret |= DrawCheckMark("Is Wet", save.IsWet, v =>
|
||||
// {
|
||||
// save.IsWet = v;
|
||||
// player?.SetWetness(v);
|
||||
// });
|
||||
//
|
||||
// var alpha = save.Alpha;
|
||||
// if (ImGui.DragFloat("Alpha", ref alpha, 0.01f, 0f, 1f, "%.2f") && alpha != save.Alpha)
|
||||
// {
|
||||
// alpha = (float)Math.Round(alpha > 1 ? 1 : alpha < 0 ? 0 : alpha, 2);
|
||||
// save.Alpha = alpha;
|
||||
// ret = true;
|
||||
// if (player != null)
|
||||
// player.Alpha() = alpha;
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
|
||||
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 _ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
|
||||
// {
|
||||
// ImGui.EndChild();
|
||||
// }
|
||||
//
|
||||
// DrawSelectionButtons();
|
||||
// ImGui.EndGroup();
|
||||
// }
|
||||
//
|
||||
// private void DrawRevertablePanel()
|
||||
// {
|
||||
// using var group = ImRaii.Group();
|
||||
// {
|
||||
// var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
|
||||
// using var color = ImRaii.PushColor(ImGuiCol.Text, GreenHeaderColor)
|
||||
// .Push(ImGuiCol.Button, buttonColor)
|
||||
// .Push(ImGuiCol.ButtonHovered, buttonColor)
|
||||
// .Push(ImGuiCol.ButtonActive, buttonColor);
|
||||
// using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
// .Push(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 tabItem = ImRaii.TabItem("Revertables");
|
||||
// if (!tabItem)
|
||||
// return;
|
||||
//
|
||||
// DrawRevertablesSelector();
|
||||
//
|
||||
// if (_currentRevertableName == null)
|
||||
// return;
|
||||
//
|
||||
// ImGui.SameLine();
|
||||
// DrawRevertablePanel();
|
||||
// }
|
||||
//}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
using System;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Glamourer.Customization;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.String;
|
||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
|
||||
{
|
||||
public static readonly Actor Null = new() { Pointer = null };
|
||||
|
||||
public FFXIVClientStructs.FFXIV.Client.Game.Character.Character* Pointer;
|
||||
|
||||
public IntPtr Address
|
||||
=> (IntPtr)Pointer;
|
||||
|
||||
public static implicit operator Actor(IntPtr? pointer)
|
||||
=> new() { Pointer = (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)(pointer ?? IntPtr.Zero) };
|
||||
|
||||
public static implicit operator IntPtr(Actor actor)
|
||||
=> actor.Pointer == null ? IntPtr.Zero : (IntPtr)actor.Pointer;
|
||||
|
||||
public ActorIdentifier GetIdentifier(ActorManager actors)
|
||||
=> actors.FromObject((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Pointer, out _, true, true, false);
|
||||
|
||||
public bool Identifier(ActorManager actors, out ActorIdentifier ident)
|
||||
{
|
||||
if (Valid)
|
||||
{
|
||||
ident = GetIdentifier(actors);
|
||||
return true;
|
||||
}
|
||||
|
||||
ident = ActorIdentifier.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> Pointer != null ? Utf8Name.ToString() : "Invalid";
|
||||
|
||||
public bool IsAvailable
|
||||
=> Pointer->GameObject.GetIsTargetable();
|
||||
|
||||
public bool IsHuman
|
||||
=> Pointer != null && Pointer->ModelCharaId == 0;
|
||||
|
||||
public ObjectKind ObjectKind
|
||||
{
|
||||
get => (ObjectKind)Pointer->GameObject.ObjectKind;
|
||||
set => Pointer->GameObject.ObjectKind = (byte)value;
|
||||
}
|
||||
|
||||
public ByteString Utf8Name
|
||||
=> new(Pointer->GameObject.Name);
|
||||
|
||||
public byte Job
|
||||
=> Pointer->ClassJob;
|
||||
|
||||
public DrawObject DrawObject
|
||||
=> (IntPtr)Pointer->GameObject.DrawObject;
|
||||
|
||||
public bool Valid
|
||||
=> Pointer != null;
|
||||
|
||||
public int Index
|
||||
=> Valid ? Pointer->GameObject.ObjectIndex : -1;
|
||||
|
||||
public uint ModelId
|
||||
{
|
||||
get => (uint)Pointer->ModelCharaId;
|
||||
set => Pointer->ModelCharaId = (int)value;
|
||||
}
|
||||
|
||||
public ushort UsedMountId
|
||||
=> !IsHuman ? (ushort)0 : *(ushort*)((byte*)Pointer + 0x668);
|
||||
|
||||
public ushort CompanionId
|
||||
=> ObjectKind == ObjectKind.Companion ? *(ushort*)((byte*)Pointer + 0x1AAC) : (ushort)0;
|
||||
|
||||
public Customize Customize
|
||||
=> new(*(CustomizeData*)&Pointer->DrawData.CustomizeData);
|
||||
|
||||
public CharacterEquip Equip
|
||||
=> new((CharacterArmor*)&Pointer->DrawData.Head);
|
||||
|
||||
public CharacterWeapon MainHand
|
||||
{
|
||||
get => *(CharacterWeapon*)&Pointer->DrawData.MainHandModel;
|
||||
set => *(CharacterWeapon*)&Pointer->DrawData.MainHandModel = value;
|
||||
}
|
||||
|
||||
public CharacterWeapon OffHand
|
||||
{
|
||||
get => *(CharacterWeapon*)&Pointer->DrawData.OffHandModel;
|
||||
set => *(CharacterWeapon*)&Pointer->DrawData.OffHandModel = value;
|
||||
}
|
||||
|
||||
public unsafe bool VisorEnabled
|
||||
{
|
||||
get => (*(byte*)(Address + Offsets.Character.VisorToggled) & Offsets.Character.Flags.IsVisorToggled) != 0;
|
||||
set => *(byte*)(Address + Offsets.Character.VisorToggled) = (byte)(value
|
||||
? *(byte*)(Address + Offsets.Character.VisorToggled) | Offsets.Character.Flags.IsVisorToggled
|
||||
: *(byte*)(Address + Offsets.Character.VisorToggled) & ~Offsets.Character.Flags.IsVisorToggled);
|
||||
}
|
||||
|
||||
public unsafe bool WeaponEnabled
|
||||
{
|
||||
get => (*(byte*)(Address + Offsets.Character.WeaponHidden1) & Offsets.Character.Flags.IsWeaponHidden1) == 0;
|
||||
set
|
||||
{
|
||||
ref var w1 = ref *(byte*)(Address + Offsets.Character.WeaponHidden1);
|
||||
ref var w2 = ref *(byte*)(Address + Offsets.Character.WeaponHidden2);
|
||||
if (value)
|
||||
{
|
||||
w1 = (byte)(w1 & ~Offsets.Character.Flags.IsWeaponHidden1);
|
||||
w2 = (byte)(w2 & ~Offsets.Character.Flags.IsWeaponHidden2);
|
||||
}
|
||||
else
|
||||
{
|
||||
w1 = (byte)(w1 | Offsets.Character.Flags.IsWeaponHidden1);
|
||||
w2 = (byte)(w2 | Offsets.Character.Flags.IsWeaponHidden2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWet { get; set; }
|
||||
|
||||
|
||||
public void SetModelId(int value)
|
||||
{
|
||||
if (Pointer != null)
|
||||
Pointer->ModelCharaId = value;
|
||||
}
|
||||
|
||||
public static implicit operator bool(Actor actor)
|
||||
=> actor.Pointer != null;
|
||||
|
||||
public static bool operator true(Actor actor)
|
||||
=> actor.Pointer != null;
|
||||
|
||||
public static bool operator false(Actor actor)
|
||||
=> actor.Pointer == null;
|
||||
|
||||
public static bool operator !(Actor actor)
|
||||
=> actor.Pointer == null;
|
||||
|
||||
public bool Equals(Actor other)
|
||||
=> Pointer == other.Pointer;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is Actor other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ((ulong)Pointer).GetHashCode();
|
||||
|
||||
public static bool operator ==(Actor lhs, Actor rhs)
|
||||
=> lhs.Pointer == rhs.Pointer;
|
||||
|
||||
public static bool operator !=(Actor lhs, Actor rhs)
|
||||
=> lhs.Pointer != rhs.Pointer;
|
||||
|
||||
public string AddressString()
|
||||
=> $"0x{Address:X}";
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public unsafe class ChangeCustomizeService
|
||||
{
|
||||
public ChangeCustomizeService()
|
||||
=> SignatureHelper.Initialise(this);
|
||||
|
||||
public delegate bool ChangeCustomizeDelegate(Human* human, byte* data, byte skipEquipment);
|
||||
|
||||
[Signature(Sigs.ChangeCustomize)]
|
||||
private readonly ChangeCustomizeDelegate _changeCustomize = null!;
|
||||
|
||||
public bool UpdateCustomize(Actor actor, CustomizeData customize)
|
||||
{
|
||||
if (customize.Data == null || !actor.Valid || !actor.DrawObject.Valid)
|
||||
return false;
|
||||
|
||||
return _changeCustomize(actor.DrawObject.Pointer, customize.Data, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
using System;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Glamourer.Customization;
|
||||
using Penumbra.GameData.Structs;
|
||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public unsafe partial struct DrawObject : IEquatable<DrawObject>, IDesignable
|
||||
{
|
||||
public Human* Pointer;
|
||||
|
||||
public IntPtr Address
|
||||
=> (IntPtr)Pointer;
|
||||
|
||||
public static implicit operator DrawObject(IntPtr? pointer)
|
||||
=> new() { Pointer = (Human*)(pointer ?? IntPtr.Zero) };
|
||||
|
||||
public static implicit operator IntPtr(DrawObject drawObject)
|
||||
=> drawObject.Pointer == null ? IntPtr.Zero : (IntPtr)drawObject.Pointer;
|
||||
|
||||
public bool Valid
|
||||
=> Pointer != null;
|
||||
|
||||
public uint ModelId
|
||||
=> 0;
|
||||
|
||||
public bool IsWet
|
||||
=> false;
|
||||
|
||||
public uint Type
|
||||
=> (*(delegate* unmanaged<Human*, uint>**)Pointer)[50](Pointer);
|
||||
|
||||
public Customize Customize
|
||||
=> *(Customize*)Pointer->CustomizeData;
|
||||
|
||||
public CharacterEquip Equip
|
||||
=> new((CharacterArmor*)Pointer->EquipSlotData);
|
||||
|
||||
public CharacterWeapon MainHand
|
||||
{
|
||||
get
|
||||
{
|
||||
var child = (byte*)Pointer->CharacterBase.DrawObject.Object.ChildObject;
|
||||
if (child == null)
|
||||
return CharacterWeapon.Empty;
|
||||
|
||||
return *(CharacterWeapon*)(child + 0x8F0);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe CharacterWeapon OffHand
|
||||
{
|
||||
get
|
||||
{
|
||||
var child = Pointer->CharacterBase.DrawObject.Object.ChildObject;
|
||||
if (child == null)
|
||||
return CharacterWeapon.Empty;
|
||||
|
||||
var sibling = (byte*)child->NextSiblingObject;
|
||||
if (sibling == null)
|
||||
return CharacterWeapon.Empty;
|
||||
|
||||
return *(CharacterWeapon*)(sibling + 0x8F0);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe bool VisorEnabled
|
||||
=> (*(byte*)(Address + 0x90) & 0x40) != 0;
|
||||
|
||||
public unsafe bool WeaponEnabled
|
||||
=> false;
|
||||
|
||||
public static implicit operator bool(DrawObject actor)
|
||||
=> actor.Pointer != null;
|
||||
|
||||
public static bool operator true(DrawObject actor)
|
||||
=> actor.Pointer != null;
|
||||
|
||||
public static bool operator false(DrawObject actor)
|
||||
=> actor.Pointer == null;
|
||||
|
||||
public static bool operator !(DrawObject actor)
|
||||
=> actor.Pointer == null;
|
||||
|
||||
public bool Equals(DrawObject other)
|
||||
=> Pointer == other.Pointer;
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is DrawObject other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> unchecked((int)(long)Pointer);
|
||||
|
||||
public static bool operator ==(DrawObject lhs, DrawObject rhs)
|
||||
=> lhs.Pointer == rhs.Pointer;
|
||||
|
||||
public static bool operator !=(DrawObject lhs, DrawObject rhs)
|
||||
=> lhs.Pointer != rhs.Pointer;
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
using Glamourer.Customization;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public interface IDesignable
|
||||
{
|
||||
public bool Valid { get; }
|
||||
public uint ModelId { get; }
|
||||
public Customize Customize { get; }
|
||||
public CharacterEquip Equip { get; }
|
||||
public CharacterWeapon MainHand { get; }
|
||||
public CharacterWeapon OffHand { get; }
|
||||
public bool VisorEnabled { get; }
|
||||
public bool WeaponEnabled { get; }
|
||||
public bool IsWet { get; }
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using Glamourer.Structs;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public class JobService : IDisposable
|
||||
{
|
||||
public readonly IReadOnlyDictionary<byte, Job> Jobs;
|
||||
public readonly IReadOnlyDictionary<ushort, JobGroup> JobGroups;
|
||||
|
||||
public event Action<Actor, Job>? JobChanged;
|
||||
|
||||
public JobService(DataManager gameData)
|
||||
{
|
||||
SignatureHelper.Initialise(this);
|
||||
Jobs = GameData.Jobs(gameData);
|
||||
JobGroups = GameData.JobGroups(gameData);
|
||||
_changeJobHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_changeJobHook.Dispose();
|
||||
}
|
||||
|
||||
private delegate void ChangeJobDelegate(nint data, uint job);
|
||||
|
||||
[Signature(Sigs.ChangeJob, DetourName = nameof(ChangeJobDetour))]
|
||||
private readonly Hook<ChangeJobDelegate> _changeJobHook = null!;
|
||||
|
||||
private void ChangeJobDetour(nint data, uint jobIndex)
|
||||
{
|
||||
_changeJobHook.Original(data, jobIndex);
|
||||
var actor = (Actor)(data - Offsets.Character.ClassJobContainer);
|
||||
var job = Jobs[(byte)jobIndex];
|
||||
Glamourer.Log.Excessive($"{actor} changed job to {job}");
|
||||
JobChanged?.Invoke(actor, job);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Glamourer.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public readonly struct ActorData
|
||||
{
|
||||
public readonly List<Actor> Objects;
|
||||
public readonly string Label;
|
||||
|
||||
public bool Valid
|
||||
=> Objects.Count > 0;
|
||||
|
||||
public ActorData(Actor actor, string label)
|
||||
{
|
||||
Objects = new List<Actor> { actor };
|
||||
Label = label;
|
||||
}
|
||||
|
||||
public static readonly ActorData Invalid = new(false);
|
||||
|
||||
private ActorData(bool _)
|
||||
{
|
||||
Objects = new List<Actor>(0);
|
||||
Label = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
|
||||
{
|
||||
private readonly Framework _framework;
|
||||
private readonly ClientState _clientState;
|
||||
private readonly ObjectTable _objects;
|
||||
private readonly ActorService _actors;
|
||||
|
||||
public ObjectManager(Framework framework, ClientState clientState, ObjectTable objects, ActorService actors)
|
||||
{
|
||||
_framework = framework;
|
||||
_clientState = clientState;
|
||||
_objects = objects;
|
||||
_actors = actors;
|
||||
}
|
||||
|
||||
|
||||
public DateTime LastUpdate { get; private set; }
|
||||
|
||||
public bool IsInGPose { get; private set; }
|
||||
public ushort World { get; private set; }
|
||||
|
||||
private readonly Dictionary<ActorIdentifier, ActorData> _identifiers = new(200);
|
||||
|
||||
private void HandleIdentifier(ActorIdentifier identifier, Actor character)
|
||||
{
|
||||
if (!character.DrawObject || !identifier.IsValid)
|
||||
return;
|
||||
|
||||
if (!_identifiers.TryGetValue(identifier, out var data))
|
||||
{
|
||||
data = new ActorData(character, identifier.ToString());
|
||||
_identifiers[identifier] = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Objects.Add(character);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var lastUpdate = _framework.LastUpdate;
|
||||
if (lastUpdate <= LastUpdate)
|
||||
return;
|
||||
|
||||
LastUpdate = lastUpdate;
|
||||
World = (ushort)(_clientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
|
||||
_identifiers.Clear();
|
||||
|
||||
for (var i = 0; i < (int)ScreenActor.CutsceneStart; ++i)
|
||||
{
|
||||
Actor character = _objects.GetObjectAddress(i);
|
||||
if (character.Identifier(_actors.AwaitedService, out var identifier))
|
||||
HandleIdentifier(identifier, character);
|
||||
}
|
||||
|
||||
for (var i = (int)ScreenActor.CutsceneStart; i < (int)ScreenActor.CutsceneEnd; ++i)
|
||||
{
|
||||
Actor character = _objects.GetObjectAddress(i);
|
||||
if (!character.Identifier(_actors.AwaitedService, out var identifier))
|
||||
break;
|
||||
|
||||
HandleIdentifier(identifier, character);
|
||||
}
|
||||
|
||||
void AddSpecial(ScreenActor idx, string label)
|
||||
{
|
||||
Actor actor = _objects.GetObjectAddress((int)idx);
|
||||
if (actor.Identifier(_actors.AwaitedService, out var ident))
|
||||
{
|
||||
var data = new ActorData(actor, label);
|
||||
_identifiers.Add(ident, data);
|
||||
}
|
||||
}
|
||||
|
||||
AddSpecial(ScreenActor.CharacterScreen, "Character Screen Actor");
|
||||
AddSpecial(ScreenActor.ExamineScreen, "Examine Screen Actor");
|
||||
AddSpecial(ScreenActor.FittingRoom, "Fitting Room Actor");
|
||||
AddSpecial(ScreenActor.DyePreview, "Dye Preview Actor");
|
||||
AddSpecial(ScreenActor.Portrait, "Portrait Actor");
|
||||
AddSpecial(ScreenActor.Card6, "Card Actor 6");
|
||||
AddSpecial(ScreenActor.Card7, "Card Actor 7");
|
||||
AddSpecial(ScreenActor.Card8, "Card Actor 8");
|
||||
|
||||
for (var i = (int)ScreenActor.ScreenEnd; i < _objects.Length; ++i)
|
||||
{
|
||||
Actor character = _objects.GetObjectAddress(i);
|
||||
if (character.Identifier(_actors.AwaitedService, out var identifier))
|
||||
HandleIdentifier(identifier, character);
|
||||
}
|
||||
|
||||
var gPose = GPosePlayer;
|
||||
IsInGPose = gPose && gPose.Utf8Name.Length > 0;
|
||||
}
|
||||
|
||||
public Actor GPosePlayer
|
||||
=> _objects.GetObjectAddress((int)ScreenActor.GPosePlayer);
|
||||
|
||||
public Actor Player
|
||||
=> _objects.GetObjectAddress(0);
|
||||
|
||||
public IEnumerator<KeyValuePair<ActorIdentifier, ActorData>> GetEnumerator()
|
||||
=> _identifiers.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public int Count
|
||||
=> _identifiers.Count;
|
||||
|
||||
public bool ContainsKey(ActorIdentifier key)
|
||||
=> _identifiers.ContainsKey(key);
|
||||
|
||||
public bool TryGetValue(ActorIdentifier key, out ActorData value)
|
||||
=> _identifiers.TryGetValue(key, out value);
|
||||
|
||||
public ActorData this[ActorIdentifier key]
|
||||
=> _identifiers[key];
|
||||
|
||||
public IEnumerable<ActorIdentifier> Keys
|
||||
=> _identifiers.Keys;
|
||||
|
||||
public IEnumerable<ActorData> Values
|
||||
=> _identifiers.Values;
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public unsafe partial class RedrawManager : IDisposable
|
||||
{
|
||||
private readonly ItemManager _items;
|
||||
private readonly ActorService _actors;
|
||||
private readonly FixedDesignManager _fixedDesignManager;
|
||||
private readonly ActiveDesign.Manager _stateManager;
|
||||
private readonly PenumbraAttach _penumbra;
|
||||
private readonly WeaponService _weapons;
|
||||
|
||||
public RedrawManager(FixedDesignManager fixedDesignManager, ActiveDesign.Manager stateManager, ItemManager items, ActorService actors,
|
||||
PenumbraAttach penumbra, WeaponService weapons)
|
||||
{
|
||||
SignatureHelper.Initialise(this);
|
||||
_fixedDesignManager = fixedDesignManager;
|
||||
_stateManager = stateManager;
|
||||
_items = items;
|
||||
_actors = actors;
|
||||
_penumbra = penumbra;
|
||||
_weapons = weapons;
|
||||
|
||||
_penumbra.CreatingCharacterBase += OnCharacterRedraw;
|
||||
_penumbra.CreatedCharacterBase += OnCharacterRedrawFinished;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnCharacterRedraw(Actor actor, uint* modelId, Customize customize, CharacterEquip equip)
|
||||
{
|
||||
// Do not apply anything if the game object model id does not correspond to the draw object model id.
|
||||
// This is the case if the actor is transformed to a different creature.
|
||||
if (actor.ModelId != *modelId)
|
||||
return;
|
||||
|
||||
// Check if we have a current design in use, or if not if the actor has a fixed design.
|
||||
var identifier = actor.GetIdentifier(_actors.AwaitedService);
|
||||
if (!_stateManager.TryGetValue(identifier, out var save))
|
||||
return;
|
||||
|
||||
// Compare game object customize data against draw object customize data for transformations.
|
||||
// Apply customization if they correspond and there is customization to apply.
|
||||
var gameObjectCustomize = new Customize(*(CustomizeData*)&actor.Pointer->DrawData.CustomizeData);
|
||||
if (gameObjectCustomize.Equals(customize))
|
||||
customize.Load(save.ModelData.Customize);
|
||||
|
||||
// Compare game object equip data against draw object equip data for transformations.
|
||||
// Apply each piece of equip that should be applied if they correspond.
|
||||
var gameObjectEquip = new CharacterEquip((CharacterArmor*)&actor.Pointer->DrawData.Head);
|
||||
if (gameObjectEquip.Equals(equip))
|
||||
{
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
(_, equip[slot]) =
|
||||
_items.ResolveRestrictedGear(save.ModelData.Armor(slot), slot, customize.Race, customize.Gender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCharacterRedraw(IntPtr gameObject, string collection, IntPtr modelId, IntPtr customize, IntPtr equipData)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnCharacterRedraw(gameObject, (uint*)modelId, new Customize(*(CustomizeData*)customize),
|
||||
new CharacterEquip((CharacterArmor*)equipData));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PluginLog.Error($"Error on new draw object creation:\n{e}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnCharacterRedrawFinished(IntPtr gameObject, string collection, IntPtr drawObject)
|
||||
{
|
||||
//SetVisor((Human*)drawObject, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public unsafe class UpdateSlotService : IDisposable
|
||||
{
|
||||
public UpdateSlotService()
|
||||
{
|
||||
SignatureHelper.Initialise(this);
|
||||
_flagSlotForUpdateHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> _flagSlotForUpdateHook.Dispose();
|
||||
|
||||
private delegate ulong FlagSlotForUpdateDelegateIntern(nint drawObject, uint slot, CharacterArmor* data);
|
||||
public delegate void FlagSlotForUpdateDelegate(DrawObject drawObject, EquipSlot slot, ref CharacterArmor item);
|
||||
|
||||
// This gets called when one of the ten equip items of an existing draw object gets changed.
|
||||
[Signature(Sigs.FlagSlotForUpdate, DetourName = nameof(FlagSlotForUpdateDetour))]
|
||||
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagSlotForUpdateHook = null!;
|
||||
|
||||
public event FlagSlotForUpdateDelegate? EquipUpdate;
|
||||
|
||||
public ulong FlagSlotForUpdateInterop(DrawObject drawObject, EquipSlot slot, CharacterArmor armor)
|
||||
=> _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
||||
|
||||
public void UpdateSlot(DrawObject drawObject, EquipSlot slot, CharacterArmor data)
|
||||
{
|
||||
InvokeFlagSlotEvent(drawObject, slot, ref data);
|
||||
FlagSlotForUpdateInterop(drawObject, slot, data);
|
||||
}
|
||||
|
||||
public void UpdateStain(DrawObject drawObject, EquipSlot slot, StainId stain)
|
||||
{
|
||||
var armor = drawObject.Equip[slot] with { Stain = stain };
|
||||
UpdateSlot(drawObject, slot, armor);
|
||||
}
|
||||
|
||||
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||
{
|
||||
var slot = slotIdx.ToEquipSlot();
|
||||
InvokeFlagSlotEvent(drawObject, slot, ref *data);
|
||||
return _flagSlotForUpdateHook.Original(drawObject, slotIdx, data);
|
||||
}
|
||||
|
||||
private void InvokeFlagSlotEvent(DrawObject drawObject, EquipSlot slot, ref CharacterArmor armor)
|
||||
{
|
||||
if (EquipUpdate == null)
|
||||
{
|
||||
Glamourer.Log.Excessive(
|
||||
$"{slot} updated on 0x{drawObject.Address:X} to {armor.Set.Value}-{armor.Variant} with stain {armor.Stain.Value}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var iv = armor;
|
||||
foreach (var del in EquipUpdate.GetInvocationList().OfType<FlagSlotForUpdateDelegate>())
|
||||
{
|
||||
try
|
||||
{
|
||||
del(drawObject, slot, ref armor);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not invoke {nameof(EquipUpdate)} Subscriber:\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
Glamourer.Log.Excessive(
|
||||
$"{slot} updated on 0x{drawObject.Address:X} to {armor.Set.Value}-{armor.Variant} with stain {armor.Stain.Value}, initial armor was {iv.Set.Value}-{iv.Variant} with stain {iv.Stain.Value}.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public class VisorService : IDisposable
|
||||
{
|
||||
public VisorService()
|
||||
{
|
||||
SignatureHelper.Initialise(this);
|
||||
_setupVisorHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> _setupVisorHook.Dispose();
|
||||
|
||||
public static unsafe bool GetVisorState(nint humanPtr)
|
||||
{
|
||||
if (humanPtr == IntPtr.Zero)
|
||||
return false;
|
||||
|
||||
var data = (Human*)humanPtr;
|
||||
var flags = &data->CharacterBase.UnkFlags_01;
|
||||
return (*flags & Offsets.DrawObjectVisorStateFlag) != 0;
|
||||
}
|
||||
|
||||
public unsafe void SetVisorState(nint humanPtr, bool on)
|
||||
{
|
||||
if (humanPtr == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
var data = (Human*)humanPtr;
|
||||
_setupVisorHook.Original(humanPtr, (ushort) data->HeadSetID, on);
|
||||
}
|
||||
|
||||
private delegate void UpdateVisorDelegateInternal(nint humanPtr, ushort modelId, bool on);
|
||||
public delegate void UpdateVisorDelegate(DrawObject human, SetId modelId, ref bool on);
|
||||
|
||||
[Signature(Penumbra.GameData.Sigs.SetupVisor, DetourName = nameof(SetupVisorDetour))]
|
||||
private readonly Hook<UpdateVisorDelegateInternal> _setupVisorHook = null!;
|
||||
|
||||
public event UpdateVisorDelegate? VisorUpdate;
|
||||
|
||||
private void SetupVisorDetour(nint humanPtr, ushort modelId, bool on)
|
||||
{
|
||||
InvokeVisorEvent(humanPtr, modelId, ref on);
|
||||
_setupVisorHook.Original(humanPtr, modelId, on);
|
||||
}
|
||||
|
||||
private void InvokeVisorEvent(DrawObject drawObject, SetId modelId, ref bool on)
|
||||
{
|
||||
if (VisorUpdate == null)
|
||||
{
|
||||
Glamourer.Log.Excessive($"Visor setup on 0x{drawObject.Address:X} with {modelId.Value}, setting to {on}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var initialValue = on;
|
||||
foreach (var del in VisorUpdate.GetInvocationList().OfType<UpdateVisorDelegate>())
|
||||
{
|
||||
try
|
||||
{
|
||||
del(drawObject, modelId, ref on);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not invoke {nameof(VisorUpdate)} Subscriber:\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
Glamourer.Log.Excessive(
|
||||
$"Visor setup on 0x{drawObject.Address:X} with {modelId.Value}, setting to {on}, initial call was {initialValue}.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Interop;
|
||||
|
||||
public unsafe class WeaponService : IDisposable
|
||||
{
|
||||
public WeaponService()
|
||||
{
|
||||
SignatureHelper.Initialise(this);
|
||||
_loadWeaponHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_loadWeaponHook.Dispose();
|
||||
}
|
||||
|
||||
public static readonly int CharacterWeaponOffset = (int)Marshal.OffsetOf<Character>("DrawData");
|
||||
|
||||
public delegate void LoadWeaponDelegate(nint offsetCharacter, uint slot, ulong weapon, byte redrawOnEquality, byte unk2,
|
||||
byte skipGameObject,
|
||||
byte unk4);
|
||||
|
||||
// Weapons for a specific character are reloaded with this function.
|
||||
// The first argument is a pointer to the game object but shifted a bit inside.
|
||||
// slot is 0 for main hand, 1 for offhand, 2 for unknown (always called with empty data.
|
||||
// weapon argument is the new weapon data.
|
||||
// redrawOnEquality controls whether the game does anything if the new weapon is identical to the old one.
|
||||
// skipGameObject seems to control whether the new weapons are written to the game object or just influence the draw object. (1 = skip, 0 = change)
|
||||
// unk4 seemed to be the same as unk1.
|
||||
[Signature(Penumbra.GameData.Sigs.WeaponReload, DetourName = nameof(LoadWeaponDetour))]
|
||||
private readonly Hook<LoadWeaponDelegate> _loadWeaponHook = null!;
|
||||
|
||||
private void LoadWeaponDetour(nint characterOffset, uint slot, ulong weapon, byte redrawOnEquality, byte unk2, byte skipGameObject,
|
||||
byte unk4)
|
||||
{
|
||||
//var oldWeapon = weapon;
|
||||
//var character = (Actor)(characterOffset - CharacterWeaponOffset);
|
||||
//try
|
||||
//{
|
||||
// var identifier = character.GetIdentifier(_actors.AwaitedService);
|
||||
// if (_fixedDesignManager.TryGetDesign(identifier, out var save))
|
||||
// {
|
||||
// PluginLog.Information($"Loaded weapon from fixed design for {identifier}.");
|
||||
// weapon = slot switch
|
||||
// {
|
||||
// 0 => save.WeaponMain.Model.Value,
|
||||
// 1 => save.WeaponOff.Model.Value,
|
||||
// _ => weapon,
|
||||
// };
|
||||
// }
|
||||
// else if (redrawOnEquality == 1 && _stateManager.TryGetValue(identifier, out var save2))
|
||||
// {
|
||||
// PluginLog.Information($"Loaded weapon from current design for {identifier}.");
|
||||
// //switch (slot)
|
||||
// //{
|
||||
// // case 0:
|
||||
// // save2.MainHand = new CharacterWeapon(weapon);
|
||||
// // break;
|
||||
// // case 1:
|
||||
// // save2.Data.OffHand = new CharacterWeapon(weapon);
|
||||
// // break;
|
||||
// //}
|
||||
// }
|
||||
//}
|
||||
//catch (Exception e)
|
||||
//{
|
||||
// PluginLog.Error($"Error on loading new weapon:\n{e}");
|
||||
//}
|
||||
|
||||
// First call the regular function.
|
||||
_loadWeaponHook.Original(characterOffset, slot, weapon, redrawOnEquality, unk2, skipGameObject, unk4);
|
||||
Glamourer.Log.Excessive($"Weapon reloaded for {(Actor)(characterOffset - CharacterWeaponOffset)} with attributes {slot} {weapon:X14}, {redrawOnEquality}, {unk2}, {skipGameObject}, {unk4}");
|
||||
// // If something changed the weapon, call it again with the actual change, not forcing redraws and skipping applying it to the game object.
|
||||
// if (oldWeapon != weapon)
|
||||
// _loadWeaponHook.Original(characterOffset, slot, weapon, 0 /* redraw */, unk2, 1 /* skip */, unk4);
|
||||
// // If we're not actively changing the offhand and the game object has no offhand, redraw an empty offhand to fix animation problems.
|
||||
// else if (slot != 1 && character.OffHand.Value == 0)
|
||||
// _loadWeaponHook.Original(characterOffset, 1, 0, 1 /* redraw */, unk2, 1 /* skip */, unk4);
|
||||
}
|
||||
|
||||
// Load a specific weapon for a character by its data and slot.
|
||||
public void LoadWeapon(Actor character, EquipSlot slot, CharacterWeapon weapon)
|
||||
{
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
LoadWeaponDetour(character.Address + CharacterWeaponOffset, 0, weapon.Value, 0, 0, 1, 0);
|
||||
return;
|
||||
case EquipSlot.OffHand:
|
||||
LoadWeaponDetour(character.Address + CharacterWeaponOffset, 1, weapon.Value, 0, 0, 1, 0);
|
||||
return;
|
||||
case EquipSlot.BothHand:
|
||||
LoadWeaponDetour(character.Address + CharacterWeaponOffset, 0, weapon.Value, 0, 0, 1, 0);
|
||||
LoadWeaponDetour(character.Address + CharacterWeaponOffset, 1, CharacterWeapon.Empty.Value, 0, 0, 1, 0);
|
||||
return;
|
||||
// function can also be called with '2', but does not seem to ever be.
|
||||
}
|
||||
}
|
||||
|
||||
// Load specific Main- and Offhand weapons.
|
||||
public void LoadWeapon(Actor character, CharacterWeapon main, CharacterWeapon off)
|
||||
{
|
||||
LoadWeaponDetour(character.Address + CharacterWeaponOffset, 0, main.Value, 1, 0, 1, 0);
|
||||
LoadWeaponDetour(character.Address + CharacterWeaponOffset, 1, off.Value, 1, 0, 1, 0);
|
||||
}
|
||||
|
||||
public void LoadStain(Actor character, EquipSlot slot, StainId stain)
|
||||
{
|
||||
var weapon = slot == EquipSlot.OffHand ? character.OffHand : character.MainHand;
|
||||
weapon.Stain = stain;
|
||||
LoadWeapon(character, slot, weapon);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
|
@ -1,30 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public class BackupService
|
||||
{
|
||||
public BackupService(Logger logger, FilenameService fileNames)
|
||||
{
|
||||
var files = GlamourerFiles(fileNames);
|
||||
Backup.CreateBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), files);
|
||||
}
|
||||
|
||||
/// <summary> Collect all relevant files for glamourer configuration. </summary>
|
||||
private static IReadOnlyList<FileInfo> GlamourerFiles(FilenameService fileNames)
|
||||
{
|
||||
var list = new List<FileInfo>(16)
|
||||
{
|
||||
new(fileNames.ConfigFile),
|
||||
new(fileNames.DesignFileSystem),
|
||||
new(fileNames.MigrationDesignFile),
|
||||
};
|
||||
|
||||
list.AddRange(fileNames.Designs());
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
using System;
|
||||
using Dalamud.Game.Command;
|
||||
using Glamourer.Gui;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public class CommandService : IDisposable
|
||||
{
|
||||
private const string HelpString = "[Copy|Apply|Save],[Name or PlaceHolder],<Name for Save>";
|
||||
private const string MainCommandString = "/glamourer";
|
||||
private const string ApplyCommandString = "/glamour";
|
||||
|
||||
private readonly CommandManager _commands;
|
||||
private readonly Interface _interface;
|
||||
|
||||
public CommandService(CommandManager commands, Interface ui)
|
||||
{
|
||||
_commands = commands;
|
||||
_interface = ui;
|
||||
|
||||
_commands.AddHandler(MainCommandString, new CommandInfo(OnGlamourer) { HelpMessage = "Open or close the Glamourer window." });
|
||||
_commands.AddHandler(ApplyCommandString, new CommandInfo(OnGlamour) { HelpMessage = $"Use Glamourer Functions: {HelpString}" });
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_commands.RemoveHandler(MainCommandString);
|
||||
_commands.RemoveHandler(ApplyCommandString);
|
||||
}
|
||||
|
||||
private void OnGlamourer(string command, string arguments)
|
||||
=> _interface.Toggle();
|
||||
|
||||
private void OnGlamour(string command, string arguments)
|
||||
{ }
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Designs;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public class FilenameService
|
||||
{
|
||||
public readonly string ConfigDirectory;
|
||||
public readonly string ConfigFile;
|
||||
public readonly string DesignFileSystem;
|
||||
public readonly string MigrationDesignFile;
|
||||
public readonly string DesignDirectory;
|
||||
|
||||
public FilenameService(DalamudPluginInterface pi)
|
||||
{
|
||||
ConfigDirectory = pi.ConfigDirectory.FullName;
|
||||
ConfigFile = pi.ConfigFile.FullName;
|
||||
DesignFileSystem = Path.Combine(ConfigDirectory, "sort_order.json");
|
||||
MigrationDesignFile = Path.Combine(ConfigDirectory, "Designs.json");
|
||||
DesignDirectory = Path.Combine(ConfigDirectory, "designs");
|
||||
}
|
||||
|
||||
public IEnumerable<FileInfo> Designs()
|
||||
{
|
||||
if (!Directory.Exists(DesignDirectory))
|
||||
yield break;
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(DesignDirectory, "*.json", SearchOption.TopDirectoryOnly))
|
||||
yield return new FileInfo(file);
|
||||
}
|
||||
|
||||
public string DesignFile(Design design)
|
||||
=> DesignFile(design.Identifier.ToString());
|
||||
|
||||
public string DesignFile(string identifier)
|
||||
=> Path.Combine(DesignDirectory, $"{identifier}.json");
|
||||
}
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Utility;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Lumina.Text;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Race = Penumbra.GameData.Enums.Race;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public class ItemManager : IDisposable
|
||||
{
|
||||
public const string Nothing = "Nothing";
|
||||
public const string SmallClothesNpc = "Smallclothes (NPC)";
|
||||
public const ushort SmallClothesNpcModel = 9903;
|
||||
|
||||
private readonly ConfigurationOld _config;
|
||||
public readonly IdentifierService IdentifierService;
|
||||
public readonly ExcelSheet<Item> ItemSheet;
|
||||
public readonly StainData Stains;
|
||||
public readonly ItemService ItemService;
|
||||
public readonly RestrictedGear RestrictedGear;
|
||||
|
||||
public ItemManager(DalamudPluginInterface pi, DataManager gameData, IdentifierService identifierService, ItemService itemService, ConfigurationOld config)
|
||||
{
|
||||
_config = config;
|
||||
ItemSheet = gameData.GetExcelSheet<Item>()!;
|
||||
IdentifierService = identifierService;
|
||||
Stains = new StainData(pi, gameData, gameData.Language);
|
||||
ItemService = itemService;
|
||||
RestrictedGear = new RestrictedGear(pi, gameData.Language, gameData);
|
||||
DefaultSword = ItemSheet.GetRow(1601)!; // Weathered Shortsword
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stains.Dispose();
|
||||
RestrictedGear.Dispose();
|
||||
}
|
||||
|
||||
public (bool, CharacterArmor) ResolveRestrictedGear(CharacterArmor armor, EquipSlot slot, Race race, Gender gender)
|
||||
{
|
||||
if (_config.UseRestrictedGearProtection)
|
||||
return RestrictedGear.ResolveRestricted(armor, slot, race, gender);
|
||||
|
||||
return (false, armor);
|
||||
}
|
||||
|
||||
public readonly Item DefaultSword;
|
||||
|
||||
public static uint NothingId(EquipSlot slot)
|
||||
=> uint.MaxValue - 128 - (uint)slot.ToSlot();
|
||||
|
||||
public static uint SmallclothesId(EquipSlot slot)
|
||||
=> uint.MaxValue - 256 - (uint)slot.ToSlot();
|
||||
|
||||
public static uint NothingId(FullEquipType type)
|
||||
=> uint.MaxValue - 384 - (uint)type;
|
||||
|
||||
public static Designs.Item NothingItem(EquipSlot slot)
|
||||
{
|
||||
Debug.Assert(slot.IsEquipment() || slot.IsAccessory(), $"Called {nameof(NothingItem)} on {slot}.");
|
||||
return new Designs.Item(Nothing, NothingId(slot), CharacterArmor.Empty);
|
||||
}
|
||||
|
||||
public static Designs.Weapon NothingItem(FullEquipType type)
|
||||
{
|
||||
Debug.Assert(type.ToSlot() == EquipSlot.OffHand, $"Called {nameof(NothingItem)} on {type}.");
|
||||
return new Designs.Weapon(Nothing, NothingId(type), CharacterWeapon.Empty, type);
|
||||
}
|
||||
|
||||
public static Designs.Item SmallClothesItem(EquipSlot slot)
|
||||
{
|
||||
Debug.Assert(slot.IsEquipment(), $"Called {nameof(SmallClothesItem)} on {slot}.");
|
||||
return new Designs.Item(SmallClothesNpc, SmallclothesId(slot), new CharacterArmor(SmallClothesNpcModel, 1, 0));
|
||||
}
|
||||
|
||||
public (bool Valid, SetId Id, byte Variant, string ItemName) Resolve(EquipSlot slot, uint itemId, Item? item = null)
|
||||
{
|
||||
slot = slot.ToSlot();
|
||||
if (itemId == NothingId(slot))
|
||||
return (true, 0, 0, Nothing);
|
||||
if (itemId == SmallclothesId(slot))
|
||||
return (true, SmallClothesNpcModel, 1, SmallClothesNpc);
|
||||
|
||||
if (item == null || item.RowId != itemId)
|
||||
item = ItemSheet.GetRow(itemId);
|
||||
|
||||
if (item == null)
|
||||
return (false, 0, 0, string.Intern($"Unknown #{itemId}"));
|
||||
if (item.ToEquipType().ToSlot() != slot)
|
||||
return (false, 0, 0, string.Intern($"Invalid ({item.Name.ToDalamudString()})"));
|
||||
|
||||
return (true, (SetId)item.ModelMain, (byte)(item.ModelMain >> 16), string.Intern(item.Name.ToDalamudString().TextValue));
|
||||
}
|
||||
|
||||
public (bool Valid, SetId Id, WeaponType Weapon, byte Variant, string ItemName, FullEquipType Type) Resolve(uint itemId, Item? item = null)
|
||||
{
|
||||
if (item == null || item.RowId != itemId)
|
||||
item = ItemSheet.GetRow(itemId);
|
||||
|
||||
if (item == null)
|
||||
return (false, 0, 0, 0, string.Intern($"Unknown #{itemId}"), FullEquipType.Unknown);
|
||||
|
||||
var type = item.ToEquipType();
|
||||
if (type.ToSlot() != EquipSlot.MainHand)
|
||||
return (false, 0, 0, 0, string.Intern($"Invalid ({item.Name.ToDalamudString()})"), type);
|
||||
|
||||
return (true, (SetId)item.ModelMain, (WeaponType)(item.ModelMain >> 16), (byte)(item.ModelMain >> 32),
|
||||
string.Intern(item.Name.ToDalamudString().TextValue), type);
|
||||
}
|
||||
|
||||
public (bool Valid, SetId Id, WeaponType Weapon, byte Variant, string ItemName, FullEquipType Type) Resolve(uint itemId,
|
||||
FullEquipType mainType, Item? item = null)
|
||||
{
|
||||
var offType = mainType.Offhand();
|
||||
if (itemId == NothingId(offType))
|
||||
return (true, 0, 0, 0, Nothing, offType);
|
||||
|
||||
if (item == null || item.RowId != itemId)
|
||||
item = ItemSheet.GetRow(itemId);
|
||||
|
||||
if (item == null)
|
||||
return (false, 0, 0, 0, string.Intern($"Unknown #{itemId}"), FullEquipType.Unknown);
|
||||
|
||||
|
||||
var type = item.ToEquipType();
|
||||
if (offType != type)
|
||||
return (false, 0, 0, 0, string.Intern($"Invalid ({item.Name.ToDalamudString()})"), type);
|
||||
|
||||
var (m, w, v) = offType.ToSlot() == EquipSlot.MainHand
|
||||
? ((SetId)item.ModelSub, (WeaponType)(item.ModelSub >> 16), (byte)(item.ModelSub >> 32))
|
||||
: ((SetId)item.ModelMain, (WeaponType)(item.ModelMain >> 16), (byte)(item.ModelMain >> 32));
|
||||
|
||||
return (true, m, w, v, string.Intern(item.Name.ToDalamudString().TextValue), type);
|
||||
}
|
||||
|
||||
public (bool Valid, uint ItemId, string ItemName) Identify(EquipSlot slot, SetId id, byte variant)
|
||||
{
|
||||
slot = slot.ToSlot();
|
||||
if (!slot.IsEquipmentPiece())
|
||||
return (false, 0, string.Intern($"Unknown ({id.Value}-{variant})"));
|
||||
|
||||
switch (id.Value)
|
||||
{
|
||||
case 0: return (true, NothingId(slot), Nothing);
|
||||
case SmallClothesNpcModel: return (true, SmallclothesId(slot), SmallClothesNpc);
|
||||
default:
|
||||
var item = IdentifierService.AwaitedService.Identify(id, variant, slot).FirstOrDefault();
|
||||
return item == null
|
||||
? (false, 0, string.Intern($"Unknown ({id.Value}-{variant})"))
|
||||
: (true, item.RowId, string.Intern(item.Name.ToDalamudString().TextValue));
|
||||
}
|
||||
}
|
||||
|
||||
public (bool Valid, uint ItemId, string ItemName, FullEquipType Type) Identify(EquipSlot slot, SetId id, WeaponType type, byte variant,
|
||||
FullEquipType mainhandType = FullEquipType.Unknown)
|
||||
{
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
{
|
||||
var item = IdentifierService.AwaitedService.Identify(id, type, variant, slot).FirstOrDefault();
|
||||
return item != null
|
||||
? (true, item.RowId, string.Intern(item.Name.ToDalamudString().TextValue), item.ToEquipType())
|
||||
: (false, 0, string.Intern($"Unknown ({id.Value}-{type.Value}-{variant})"), mainhandType);
|
||||
}
|
||||
case EquipSlot.OffHand:
|
||||
{
|
||||
var weaponType = mainhandType.Offhand();
|
||||
if (id.Value == 0)
|
||||
return (true, NothingId(weaponType), Nothing, weaponType);
|
||||
|
||||
var item = IdentifierService.AwaitedService.Identify(id, type, variant, slot).FirstOrDefault();
|
||||
return item != null
|
||||
? (true, item.RowId, string.Intern(item.Name.ToDalamudString().TextValue), item.ToEquipType())
|
||||
: (false, 0, string.Intern($"Unknown ({id.Value}-{type.Value}-{variant})"),
|
||||
weaponType);
|
||||
}
|
||||
default: return (false, 0, string.Intern($"Unknown ({id.Value}-{type.Value}-{variant})"), FullEquipType.Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Any file type that we want to save via SaveService.
|
||||
/// </summary>
|
||||
public interface ISavable : ISavable<FilenameService>
|
||||
{ }
|
||||
|
||||
public sealed class SaveService : SaveServiceBase<FilenameService>
|
||||
{
|
||||
public SaveService(Logger log, FrameworkManager framework, FilenameService fileNames)
|
||||
: base(log, framework, fileNames)
|
||||
{ }
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
using Dalamud.Plugin;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.State;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Log;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public static class ServiceManager
|
||||
{
|
||||
public static ServiceProvider CreateProvider(DalamudPluginInterface pi, Logger log)
|
||||
{
|
||||
var services = new ServiceCollection()
|
||||
.AddSingleton(log)
|
||||
.AddDalamud(pi)
|
||||
.AddMeta()
|
||||
.AddConfig()
|
||||
.AddPenumbra()
|
||||
.AddInterop()
|
||||
.AddGameData()
|
||||
.AddDesigns()
|
||||
.AddInterface()
|
||||
.AddApi();
|
||||
|
||||
return services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });
|
||||
}
|
||||
|
||||
private static IServiceCollection AddDalamud(this IServiceCollection services, DalamudPluginInterface pi)
|
||||
{
|
||||
new DalamudServices(pi).AddServices(services);
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddMeta(this IServiceCollection services)
|
||||
=> services.AddSingleton<FilenameService>()
|
||||
.AddSingleton<SaveService>()
|
||||
.AddSingleton<FrameworkManager>()
|
||||
.AddSingleton<ChatService>();
|
||||
|
||||
private static IServiceCollection AddConfig(this IServiceCollection services)
|
||||
=> services.AddSingleton<ConfigurationOld>()
|
||||
.AddSingleton<BackupService>();
|
||||
|
||||
private static IServiceCollection AddPenumbra(this IServiceCollection services)
|
||||
=> services.AddSingleton<PenumbraAttach>();
|
||||
|
||||
private static IServiceCollection AddGameData(this IServiceCollection services)
|
||||
=> services.AddSingleton<IdentifierService>()
|
||||
.AddSingleton<ActorService>()
|
||||
.AddSingleton<ItemService>()
|
||||
.AddSingleton<ItemManager>()
|
||||
.AddSingleton<CustomizationService>();
|
||||
|
||||
private static IServiceCollection AddInterop(this IServiceCollection services)
|
||||
=> services.AddSingleton<ChangeCustomizeService>()
|
||||
.AddSingleton<JobService>()
|
||||
.AddSingleton<UpdateSlotService>()
|
||||
.AddSingleton<VisorService>()
|
||||
.AddSingleton<WeaponService>()
|
||||
.AddSingleton<ObjectManager>();
|
||||
|
||||
private static IServiceCollection AddDesigns(this IServiceCollection services)
|
||||
=> services.AddSingleton<DesignManager>()
|
||||
.AddSingleton<DesignFileSystem>()
|
||||
.AddSingleton<ActiveDesign.Manager>()
|
||||
.AddSingleton<FixedDesignManager>()
|
||||
.AddSingleton<RedrawManager>();
|
||||
|
||||
private static IServiceCollection AddInterface(this IServiceCollection services)
|
||||
=> services.AddSingleton<Interface>()
|
||||
.AddSingleton<GlamourerWindowSystem>();
|
||||
|
||||
private static IServiceCollection AddApi(this IServiceCollection services)
|
||||
=> services.AddSingleton<CommandService>()
|
||||
.AddSingleton<Glamourer.GlamourerIpc>();
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
using Dalamud.Data;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.GameData.Actors;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Game;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Customization;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public abstract class AsyncServiceWrapper<T>
|
||||
{
|
||||
public string Name { get; }
|
||||
public T? Service { get; private set; }
|
||||
|
||||
public T AwaitedService
|
||||
{
|
||||
get
|
||||
{
|
||||
_task?.Wait();
|
||||
return Service!;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Valid
|
||||
=> Service != null && !_isDisposed;
|
||||
|
||||
public event Action? FinishedCreation;
|
||||
private Task? _task;
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
protected AsyncServiceWrapper(string name, Func<T> factory)
|
||||
{
|
||||
Name = name;
|
||||
_task = Task.Run(() =>
|
||||
{
|
||||
var service = factory();
|
||||
if (_isDisposed)
|
||||
{
|
||||
if (service is IDisposable d)
|
||||
d.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
Service = service;
|
||||
Glamourer.Log.Verbose($"[{Name}] Created.");
|
||||
_task = null;
|
||||
}
|
||||
});
|
||||
_task.ContinueWith((t, x) =>
|
||||
{
|
||||
if (!_isDisposed)
|
||||
FinishedCreation?.Invoke();
|
||||
}, null);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
_isDisposed = true;
|
||||
_task = null;
|
||||
if (Service is IDisposable d)
|
||||
d.Dispose();
|
||||
Glamourer.Log.Verbose($"[{Name}] Disposed.");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class IdentifierService : AsyncServiceWrapper<IObjectIdentifier>
|
||||
{
|
||||
public IdentifierService(DalamudPluginInterface pi, DataManager data)
|
||||
: base(nameof(IdentifierService), () => Penumbra.GameData.GameData.GetIdentifier(pi, data))
|
||||
{ }
|
||||
}
|
||||
|
||||
public sealed class ItemService : AsyncServiceWrapper<ItemData>
|
||||
{
|
||||
public ItemService(DalamudPluginInterface pi, DataManager gameData)
|
||||
: base(nameof(ItemService), () => new ItemData(pi, gameData, gameData.Language))
|
||||
{ }
|
||||
}
|
||||
|
||||
public sealed class ActorService : AsyncServiceWrapper<ActorManager>
|
||||
{
|
||||
public ActorService(DalamudPluginInterface pi, ObjectTable objects, ClientState clientState, Framework framework, DataManager gameData,
|
||||
GameGui gui, PenumbraAttach penumbra)
|
||||
: base(nameof(ActorService),
|
||||
() => new ActorManager(pi, objects, clientState, framework, gameData, gui, idx => (short)penumbra.CutsceneParent(idx)))
|
||||
{ }
|
||||
}
|
||||
|
||||
public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationManager>
|
||||
{
|
||||
public CustomizationService(DalamudPluginInterface pi, DataManager gameData)
|
||||
: base(nameof(CustomizationService), () => CustomizationManager.Create(pi, gameData))
|
||||
{ }
|
||||
}
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Glamourer.Interop;
|
||||
using Penumbra.GameData.Actors;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Glamourer.Api;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||
|
||||
namespace Glamourer.State;
|
||||
|
||||
public sealed partial class ActiveDesign
|
||||
{
|
||||
[Flags]
|
||||
public enum ChangeType
|
||||
{
|
||||
Default = 0x00,
|
||||
Changed = 0x01,
|
||||
Fixed = 0x02,
|
||||
}
|
||||
|
||||
public class Manager : IReadOnlyDictionary<ActorIdentifier, ActiveDesign>
|
||||
{
|
||||
private readonly ActorService _actors;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly PenumbraAttach _penumbra;
|
||||
private readonly ItemManager _items;
|
||||
private readonly VisorService _visor;
|
||||
private readonly ChangeCustomizeService _customize;
|
||||
private readonly UpdateSlotService _updateSlot;
|
||||
private readonly WeaponService _weaponService;
|
||||
|
||||
private readonly Dictionary<ActorIdentifier, ActiveDesign> _characterSaves = new();
|
||||
|
||||
public Manager(ActorService actors, ObjectManager objects, PenumbraAttach penumbra, ItemManager items, VisorService visor,
|
||||
ChangeCustomizeService customize, UpdateSlotService updateSlot, WeaponService weaponService)
|
||||
{
|
||||
_actors = actors;
|
||||
_objects = objects;
|
||||
_penumbra = penumbra;
|
||||
_items = items;
|
||||
_visor = visor;
|
||||
_customize = customize;
|
||||
_updateSlot = updateSlot;
|
||||
_weaponService = weaponService;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<ActorIdentifier, ActiveDesign>> GetEnumerator()
|
||||
=> _characterSaves.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public int Count
|
||||
=> _characterSaves.Count;
|
||||
|
||||
public bool ContainsKey(ActorIdentifier key)
|
||||
=> _characterSaves.ContainsKey(key);
|
||||
|
||||
public bool TryGetValue(ActorIdentifier key, [NotNullWhen(true)] out ActiveDesign? value)
|
||||
=> _characterSaves.TryGetValue(key, out value);
|
||||
|
||||
public ActiveDesign this[ActorIdentifier key]
|
||||
=> _characterSaves[key];
|
||||
|
||||
public IEnumerable<ActorIdentifier> Keys
|
||||
=> _characterSaves.Keys;
|
||||
|
||||
public IEnumerable<ActiveDesign> Values
|
||||
=> _characterSaves.Values;
|
||||
|
||||
public void DeleteSave(ActorIdentifier identifier)
|
||||
=> _characterSaves.Remove(identifier);
|
||||
|
||||
public unsafe ActiveDesign GetOrCreateSave(Actor actor)
|
||||
{
|
||||
var id = _actors.AwaitedService.FromObject((GameObject*)actor.Pointer, out _, false, false, false);
|
||||
if (_characterSaves.TryGetValue(id, out var save))
|
||||
{
|
||||
save.Initialize(_items, actor);
|
||||
return save;
|
||||
}
|
||||
|
||||
id = id.CreatePermanent();
|
||||
save = new ActiveDesign(_items, id, actor);
|
||||
save.Initialize(_items, actor);
|
||||
_characterSaves.Add(id, save);
|
||||
return save;
|
||||
}
|
||||
|
||||
public void SetWetness(ActiveDesign design, bool wet, bool fromFixed)
|
||||
=> design.IsWet = wet;
|
||||
|
||||
public void SetHatVisible(ActiveDesign design, bool visible, bool fromFixed)
|
||||
=> design.IsHatVisible = visible;
|
||||
|
||||
public void SetVisor(ActiveDesign design, bool toggled, bool fromFixed)
|
||||
=> design.IsVisorToggled = toggled;
|
||||
|
||||
public void SetWeaponVisible(ActiveDesign design, bool visible, bool fromFixed)
|
||||
=> design.IsWeaponVisible = visible;
|
||||
|
||||
public unsafe void ApplyDesign(ActiveDesign to, Design from, bool fromFixed)
|
||||
{
|
||||
if (to.ModelId != from.ModelId)
|
||||
return;
|
||||
|
||||
if (from.DoApplyEquip(EquipSlot.MainHand))
|
||||
ChangeMainHand(to, from.MainHandId, fromFixed);
|
||||
if (from.DoApplyStain(EquipSlot.MainHand))
|
||||
ChangeStain(to, EquipSlot.MainHand, from.WeaponMain.Stain, fromFixed);
|
||||
|
||||
if (from.DoApplyEquip(EquipSlot.OffHand))
|
||||
ChangeOffHand(to, from.OffHandId, fromFixed);
|
||||
if (from.DoApplyStain(EquipSlot.OffHand))
|
||||
ChangeStain(to, EquipSlot.OffHand, from.WeaponOff.Stain, fromFixed);
|
||||
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var armor = from.Armor(slot);
|
||||
if (from.DoApplyEquip(slot))
|
||||
ChangeEquipment(to, slot, armor, fromFixed);
|
||||
if (from.DoApplyStain(slot))
|
||||
ChangeStain(to, slot, armor.Stain, fromFixed);
|
||||
}
|
||||
|
||||
ChangeCustomize(to, from.ApplyCustomize, from.ModelData.Customize.Data, fromFixed);
|
||||
|
||||
if (from.Wetness.Enabled)
|
||||
SetWetness(to, from.Wetness.ForcedValue, fromFixed);
|
||||
if (from.Hat.Enabled)
|
||||
SetHatVisible(to, from.Hat.ForcedValue, fromFixed);
|
||||
if (from.Visor.Enabled)
|
||||
SetVisor(to, from.Visor.ForcedValue, fromFixed);
|
||||
if (from.Weapon.Enabled)
|
||||
SetWeaponVisible(to, from.Weapon.ForcedValue, fromFixed);
|
||||
}
|
||||
|
||||
public void RevertDesign(ActiveDesign design)
|
||||
{
|
||||
RevertCustomize(design, design.ChangedCustomize);
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
RevertEquipment(design, slot, design.ChangedEquip.HasFlag(slot.ToFlag()), design.ChangedEquip.HasFlag(slot.ToStainFlag()));
|
||||
|
||||
RevertMainHand(design);
|
||||
RevertOffHand(design);
|
||||
}
|
||||
|
||||
public void ChangeMainHand(ActiveDesign design, uint itemId, bool fromFixed)
|
||||
=> design.SetMainhand(_items, itemId);
|
||||
|
||||
public void ChangeOffHand(ActiveDesign design, uint itemId, bool fromFixed)
|
||||
=> design.SetOffhand(_items, itemId);
|
||||
|
||||
public void RevertMainHand(ActiveDesign design)
|
||||
{ }
|
||||
|
||||
public void RevertOffHand(ActiveDesign design)
|
||||
{ }
|
||||
|
||||
public void RevertCustomize(ActiveDesign design, CustomizeFlag flags)
|
||||
=> ChangeCustomize(design, flags, design._initialData.Customize.Data, false);
|
||||
|
||||
public void ChangeCustomize(ActiveDesign design, CustomizeFlag flags, CustomizeData newValue, bool fromFixed)
|
||||
{
|
||||
var customize = new Customize(newValue);
|
||||
var anyChanges = false;
|
||||
foreach (var option in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
var flag = option.ToFlag();
|
||||
var apply = flags.HasFlag(flag);
|
||||
anyChanges |= apply && design.SetCustomize(option, customize[option]);
|
||||
if (design.GetCustomize(option).Value != design._initialData.Customize[option].Value)
|
||||
design.ChangedCustomize |= flag;
|
||||
else
|
||||
design.ChangedCustomize &= ~flag;
|
||||
|
||||
if (fromFixed)
|
||||
design.FixedCustomize |= flag;
|
||||
else
|
||||
design.FixedCustomize &= ~flag;
|
||||
}
|
||||
|
||||
if (!anyChanges)
|
||||
return;
|
||||
|
||||
_objects.Update();
|
||||
if (!_objects.TryGetValue(design.Identifier, out var data))
|
||||
return;
|
||||
|
||||
var redraw = flags.RequiresRedraw();
|
||||
foreach (var obj in data.Objects)
|
||||
{
|
||||
if (redraw)
|
||||
_penumbra.RedrawObject(obj, RedrawType.Redraw);
|
||||
else
|
||||
_customize.UpdateCustomize(obj, design.ModelData.Customize.Data);
|
||||
}
|
||||
}
|
||||
|
||||
public void RevertEquipment(ActiveDesign design, EquipSlot slot, bool equip, bool stain)
|
||||
{
|
||||
var item = design._initialData.Armor(slot);
|
||||
if (equip)
|
||||
{
|
||||
var flag = slot.ToFlag();
|
||||
design.UpdateArmor(_items, slot, item, true);
|
||||
design.ChangedEquip &= ~flag;
|
||||
design.FixedEquip &= ~flag;
|
||||
}
|
||||
|
||||
if (stain)
|
||||
{
|
||||
var flag = slot.ToStainFlag();
|
||||
design.SetStain(slot, item.Stain);
|
||||
design.ChangedEquip &= ~flag;
|
||||
design.FixedEquip &= ~flag;
|
||||
}
|
||||
|
||||
_objects.Update();
|
||||
if (!_objects.TryGetValue(design.Identifier, out var data))
|
||||
return;
|
||||
|
||||
foreach (var obj in data.Objects)
|
||||
_updateSlot.UpdateSlot(obj.DrawObject, slot, item);
|
||||
}
|
||||
|
||||
public void ChangeEquipment(ActiveDesign design, EquipSlot slot, Item item, bool fromFixed)
|
||||
{
|
||||
var flag = slot.ToFlag();
|
||||
design.SetArmor(slot, item);
|
||||
var current = design.Armor(slot);
|
||||
var initial = design._initialData.Armor(slot);
|
||||
if (current.ModelBase.Value != initial.Set.Value || current.Variant != initial.Variant)
|
||||
design.ChangedEquip |= flag;
|
||||
else
|
||||
design.ChangedEquip &= ~flag;
|
||||
if (fromFixed)
|
||||
design.FixedEquip |= flag;
|
||||
else
|
||||
design.FixedEquip &= ~flag;
|
||||
|
||||
_objects.Update();
|
||||
if (!_objects.TryGetValue(design.Identifier, out var data))
|
||||
return;
|
||||
|
||||
foreach (var obj in data.Objects)
|
||||
_updateSlot.UpdateSlot(obj.DrawObject, slot, item.Model);
|
||||
}
|
||||
|
||||
public void ChangeStain(ActiveDesign design, EquipSlot slot, StainId stain, bool fromFixed)
|
||||
{
|
||||
var flag = slot.ToStainFlag();
|
||||
design.SetStain(slot, stain);
|
||||
var (current, initial, weapon) = slot switch
|
||||
{
|
||||
EquipSlot.MainHand => (design.WeaponMain.Stain, design._initialData.MainHand.Stain, true),
|
||||
EquipSlot.OffHand => (design.WeaponOff.Stain, design._initialData.OffHand.Stain, true),
|
||||
_ => (design.Armor(slot).Stain, design._initialData.Armor(slot).Stain, false),
|
||||
};
|
||||
if (current.Value != initial.Value)
|
||||
design.ChangedEquip |= flag;
|
||||
else
|
||||
design.ChangedEquip &= ~flag;
|
||||
if (fromFixed)
|
||||
design.FixedEquip |= flag;
|
||||
else
|
||||
design.FixedEquip &= ~flag;
|
||||
|
||||
_objects.Update();
|
||||
if (!_objects.TryGetValue(design.Identifier, out var data))
|
||||
return;
|
||||
|
||||
foreach (var obj in data.Objects)
|
||||
{
|
||||
if (weapon)
|
||||
_weaponService.LoadStain(obj, EquipSlot.MainHand, stain);
|
||||
else
|
||||
_updateSlot.UpdateStain(obj.DrawObject, slot, stain);
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeVisor(ActiveDesign design, bool on, bool fromFixed)
|
||||
{
|
||||
var current = design.IsVisorToggled;
|
||||
if (current == on)
|
||||
return;
|
||||
|
||||
design.IsVisorToggled = on;
|
||||
_objects.Update();
|
||||
if (!_objects.TryGetValue(design.Identifier, out var data))
|
||||
return;
|
||||
|
||||
foreach (var obj in data.Objects)
|
||||
_visor.SetVisorState(obj.DrawObject, on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.State;
|
||||
|
||||
|
||||
|
||||
public sealed partial class ActiveDesign : DesignData
|
||||
{
|
||||
public readonly ActorIdentifier Identifier;
|
||||
|
||||
private ModelData _initialData = new();
|
||||
|
||||
public CustomizeFlag ChangedCustomize { get; private set; } = 0;
|
||||
public CustomizeFlag FixedCustomize { get; private set; } = 0;
|
||||
|
||||
public EquipFlag ChangedEquip { get; private set; } = 0;
|
||||
public EquipFlag FixedEquip { get; private set; } = 0;
|
||||
|
||||
|
||||
public bool IsHatVisible { get; private set; } = false;
|
||||
public bool IsWeaponVisible { get; private set; } = false;
|
||||
public bool IsVisorToggled { get; private set; } = false;
|
||||
public bool IsWet { get; private set; } = false;
|
||||
|
||||
private ActiveDesign(ItemManager items, ActorIdentifier identifier)
|
||||
: base(items)
|
||||
=> Identifier = identifier;
|
||||
|
||||
public ActiveDesign(ItemManager items, ActorIdentifier identifier, Actor actor)
|
||||
: base(items)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Initialize(items, actor);
|
||||
}
|
||||
|
||||
//public void ApplyToActor(Actor actor)
|
||||
//{
|
||||
// if (!actor)
|
||||
// return;
|
||||
//
|
||||
// void Redraw()
|
||||
// => Glamourer.Penumbra.RedrawObject(actor.Character, RedrawType.Redraw);
|
||||
//
|
||||
// if (_drawData.ModelId != actor.ModelId)
|
||||
// {
|
||||
// Redraw();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// var customize1 = _drawData.Customize;
|
||||
// var customize2 = actor.Customize;
|
||||
// if (RedrawManager.NeedsRedraw(customize1, customize2))
|
||||
// {
|
||||
// Redraw();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// Glamourer.RedrawManager.UpdateCustomize(actor, customize2);
|
||||
// foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
// Glamourer.RedrawManager.ChangeEquip(actor, slot, actor.Equip[slot]);
|
||||
// Glamourer.RedrawManager.LoadWeapon(actor, actor.MainHand, actor.OffHand);
|
||||
// if (actor.IsHuman && actor.DrawObject)
|
||||
// RedrawManager.SetVisor(actor.DrawObject.Pointer, actor.VisorEnabled);
|
||||
//}
|
||||
//
|
||||
public void Initialize(ItemManager items, Actor actor)
|
||||
{
|
||||
if (!actor)
|
||||
return;
|
||||
|
||||
if (!_initialData.Customize.Equals(actor.Customize))
|
||||
{
|
||||
_initialData.Customize.Load(actor.Customize);
|
||||
ModelData.Customize.Load(actor.Customize);
|
||||
}
|
||||
|
||||
var currentEquip = actor.Equip;
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var current = currentEquip[slot];
|
||||
if (_initialData.Armor(slot) != current)
|
||||
{
|
||||
_initialData.SetPiece(slot, current.Set, current.Variant, current.Stain, out _);
|
||||
UpdateArmor(items, slot, current, true);
|
||||
SetStain(slot, current.Stain);
|
||||
}
|
||||
}
|
||||
|
||||
if (_initialData.MainHand != actor.MainHand)
|
||||
{
|
||||
_initialData.MainHand = actor.MainHand;
|
||||
UpdateMainhand(items, actor.MainHand);
|
||||
SetStain(EquipSlot.MainHand, actor.MainHand.Stain);
|
||||
}
|
||||
|
||||
if (_initialData.OffHand != actor.OffHand)
|
||||
{
|
||||
_initialData.OffHand = actor.OffHand;
|
||||
UpdateOffhand(items, actor.OffHand);
|
||||
SetStain(EquipSlot.OffHand, actor.OffHand.Stain);
|
||||
}
|
||||
|
||||
var visor = VisorService.GetVisorState(actor.DrawObject);
|
||||
if (IsVisorToggled != visor)
|
||||
IsVisorToggled = visor;
|
||||
}
|
||||
|
||||
public string CreateOldBase64()
|
||||
=> DesignBase64Migration.CreateOldBase64(in ModelData, EquipFlagExtensions.All, CustomizeFlagExtensions.All, IsWet, IsHatVisible, true,
|
||||
IsVisorToggled,
|
||||
true, IsWeaponVisible, true, false, 1f);
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.InstanceContent;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Structs;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Glamourer.State;
|
||||
|
||||
public class FixedDesignManager
|
||||
{
|
||||
public class FixedDesign
|
||||
{
|
||||
public Design Design = null!;
|
||||
public byte? JobCondition;
|
||||
public ushort? TerritoryCondition;
|
||||
|
||||
public bool Applies(byte job, ushort territoryType)
|
||||
=> (!JobCondition.HasValue || JobCondition.Value == job)
|
||||
&& (!TerritoryCondition.HasValue || TerritoryCondition.Value == territoryType);
|
||||
}
|
||||
|
||||
public IReadOnlyList<FixedDesign> GetDesigns(ActorIdentifier actor)
|
||||
{
|
||||
return Array.Empty<FixedDesign>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Glamourer.Interop;
|
||||
|
||||
namespace Glamourer.Util;
|
||||
|
||||
public static class CharacterExtensions
|
||||
{
|
||||
public static unsafe bool IsWet(this Character a)
|
||||
=> ((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*) a.Address)->IsGPoseWet;
|
||||
|
||||
public static unsafe bool SetWetness(this Character a, bool value)
|
||||
{
|
||||
var current = a.IsWet();
|
||||
if (current == value)
|
||||
return false;
|
||||
|
||||
((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->IsGPoseWet = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool IsHatVisible(this Character a)
|
||||
=> !((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->DrawData.IsHatHidden;
|
||||
|
||||
public static unsafe bool SetHatVisible(this Character a, bool visible)
|
||||
{
|
||||
var current = a.IsHatVisible();
|
||||
if (current == visible)
|
||||
return false;
|
||||
|
||||
((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->DrawData.IsHatHidden = !visible;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool IsVisorToggled(this Character a)
|
||||
=> ((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->DrawData.IsVisorToggled;
|
||||
|
||||
public static unsafe bool SetVisorToggled(this Character a, bool toggled)
|
||||
{
|
||||
var current = a.IsVisorToggled();
|
||||
if (current == toggled)
|
||||
return false;
|
||||
|
||||
((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->DrawData.IsVisorToggled = toggled;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool IsWeaponHidden(this Character a)
|
||||
=> ((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->DrawData.IsWeaponHidden
|
||||
&& ((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->DrawData.IsMainHandHidden;
|
||||
|
||||
public static unsafe bool SetWeaponHidden(this Character a, bool value)
|
||||
{
|
||||
var hidden = a.IsWeaponHidden();
|
||||
if (hidden == value)
|
||||
return false;
|
||||
|
||||
var drawData = &((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->DrawData;
|
||||
|
||||
|
||||
drawData->IsWeaponHidden = value;
|
||||
drawData->IsMainHandHidden = value;
|
||||
// TODO
|
||||
if (value)
|
||||
*(&drawData->MainHandState + ((long) &drawData->OffHandModel - (long) &drawData->MainHandModel)) |= 0x02;
|
||||
else
|
||||
*(&drawData->MainHandState + ((long) &drawData->OffHandModel - (long) &drawData->MainHandModel)) &= 0xFD;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe ref float Alpha(this Character a)
|
||||
=> ref ((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)a.Address)->Alpha;
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Util;
|
||||
|
||||
public static class CustomizeExtensions
|
||||
{
|
||||
// In languages other than english the actual clan name may depend on gender.
|
||||
public static string ClanName(ICustomizationManager customization, SubRace race, Gender gender)
|
||||
{
|
||||
if (gender == Gender.FemaleNpc)
|
||||
gender = Gender.Female;
|
||||
if (gender == Gender.MaleNpc)
|
||||
gender = Gender.Male;
|
||||
return (gender, race) switch
|
||||
{
|
||||
(Gender.Male, SubRace.Midlander) => customization.GetName(CustomName.MidlanderM),
|
||||
(Gender.Male, SubRace.Highlander) => customization.GetName(CustomName.HighlanderM),
|
||||
(Gender.Male, SubRace.Wildwood) => customization.GetName(CustomName.WildwoodM),
|
||||
(Gender.Male, SubRace.Duskwight) => customization.GetName(CustomName.DuskwightM),
|
||||
(Gender.Male, SubRace.Plainsfolk) => customization.GetName(CustomName.PlainsfolkM),
|
||||
(Gender.Male, SubRace.Dunesfolk) => customization.GetName(CustomName.DunesfolkM),
|
||||
(Gender.Male, SubRace.SeekerOfTheSun) => customization.GetName(CustomName.SeekerOfTheSunM),
|
||||
(Gender.Male, SubRace.KeeperOfTheMoon) => customization.GetName(CustomName.KeeperOfTheMoonM),
|
||||
(Gender.Male, SubRace.Seawolf) => customization.GetName(CustomName.SeawolfM),
|
||||
(Gender.Male, SubRace.Hellsguard) => customization.GetName(CustomName.HellsguardM),
|
||||
(Gender.Male, SubRace.Raen) => customization.GetName(CustomName.RaenM),
|
||||
(Gender.Male, SubRace.Xaela) => customization.GetName(CustomName.XaelaM),
|
||||
(Gender.Male, SubRace.Helion) => customization.GetName(CustomName.HelionM),
|
||||
(Gender.Male, SubRace.Lost) => customization.GetName(CustomName.LostM),
|
||||
(Gender.Male, SubRace.Rava) => customization.GetName(CustomName.RavaM),
|
||||
(Gender.Male, SubRace.Veena) => customization.GetName(CustomName.VeenaM),
|
||||
(Gender.Female, SubRace.Midlander) => customization.GetName(CustomName.MidlanderF),
|
||||
(Gender.Female, SubRace.Highlander) => customization.GetName(CustomName.HighlanderF),
|
||||
(Gender.Female, SubRace.Wildwood) => customization.GetName(CustomName.WildwoodF),
|
||||
(Gender.Female, SubRace.Duskwight) => customization.GetName(CustomName.DuskwightF),
|
||||
(Gender.Female, SubRace.Plainsfolk) => customization.GetName(CustomName.PlainsfolkF),
|
||||
(Gender.Female, SubRace.Dunesfolk) => customization.GetName(CustomName.DunesfolkF),
|
||||
(Gender.Female, SubRace.SeekerOfTheSun) => customization.GetName(CustomName.SeekerOfTheSunF),
|
||||
(Gender.Female, SubRace.KeeperOfTheMoon) => customization.GetName(CustomName.KeeperOfTheMoonF),
|
||||
(Gender.Female, SubRace.Seawolf) => customization.GetName(CustomName.SeawolfF),
|
||||
(Gender.Female, SubRace.Hellsguard) => customization.GetName(CustomName.HellsguardF),
|
||||
(Gender.Female, SubRace.Raen) => customization.GetName(CustomName.RaenF),
|
||||
(Gender.Female, SubRace.Xaela) => customization.GetName(CustomName.XaelaF),
|
||||
(Gender.Female, SubRace.Helion) => customization.GetName(CustomName.HelionM),
|
||||
(Gender.Female, SubRace.Lost) => customization.GetName(CustomName.LostM),
|
||||
(Gender.Female, SubRace.Rava) => customization.GetName(CustomName.RavaF),
|
||||
(Gender.Female, SubRace.Veena) => customization.GetName(CustomName.VeenaF),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(race), race, null),
|
||||
};
|
||||
}
|
||||
|
||||
public static string ClanName(this Customize customize, ICustomizationManager customization)
|
||||
=> ClanName(customization, customize.Clan, customize.Gender);
|
||||
|
||||
|
||||
// Change a gender and fix up all required customizations afterwards.
|
||||
public static CustomizeFlag ChangeGender(this Customize customize, CharacterEquip equip, Gender gender, ItemManager items, ICustomizationManager customization)
|
||||
{
|
||||
if (customize.Gender == gender)
|
||||
return 0;
|
||||
|
||||
FixRestrictedGear(items, customize, equip, gender, customize.Race);
|
||||
customize.Gender = gender;
|
||||
return CustomizeFlag.Gender | FixUpAttributes(customization, customize);
|
||||
}
|
||||
|
||||
// Change a race and fix up all required customizations afterwards.
|
||||
public static CustomizeFlag ChangeRace(this Customize customize, CharacterEquip equip, SubRace clan, ItemManager items, ICustomizationManager customization)
|
||||
{
|
||||
if (customize.Clan == clan)
|
||||
return 0;
|
||||
|
||||
var race = clan.ToRace();
|
||||
var gender = race == Race.Hrothgar ? Gender.Male : customize.Gender; // TODO Female Hrothgar
|
||||
FixRestrictedGear(items, customize, equip, gender, race);
|
||||
var flags = CustomizeFlag.Race | CustomizeFlag.Clan;
|
||||
if (gender != customize.Gender)
|
||||
flags |= CustomizeFlag.Gender;
|
||||
customize.Gender = gender;
|
||||
customize.Race = race;
|
||||
customize.Clan = clan;
|
||||
return flags | FixUpAttributes(customization, customize);
|
||||
}
|
||||
|
||||
// Go through a whole customization struct and fix up all settings that need fixing.
|
||||
private static CustomizeFlag FixUpAttributes(ICustomizationManager customization, Customize customize)
|
||||
{
|
||||
var set = customization.GetList(customize.Clan, customize.Gender);
|
||||
CustomizeFlag flags = 0;
|
||||
foreach (CustomizeIndex id in Enum.GetValues(typeof(CustomizeIndex)))
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case CustomizeIndex.Race: break;
|
||||
case CustomizeIndex.Clan: break;
|
||||
case CustomizeIndex.BodyType: break;
|
||||
case CustomizeIndex.Gender: break;
|
||||
case CustomizeIndex.Highlights: break;
|
||||
case CustomizeIndex.Face: break;
|
||||
default:
|
||||
var count = set.Count(id);
|
||||
if (set.DataByValue(id, customize[id], out _, customize.Face) < 0)
|
||||
{
|
||||
customize[id] = count == 0 ? CustomizeValue.Zero : set.Data(id, 0).Value;
|
||||
flags |= id.ToFlag();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static void FixRestrictedGear(ItemManager items, Customize customize, CharacterEquip equip, Gender gender, Race race)
|
||||
{
|
||||
if (!equip || race == customize.Race && gender == customize.Gender)
|
||||
return;
|
||||
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
(_, equip[slot]) = items.ResolveRestrictedGear(equip[slot], slot, race, gender);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue