mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-13 12:14:18 +01:00
More stuff.
This commit is contained in:
parent
dad146d043
commit
1a4672a901
23 changed files with 616 additions and 1005 deletions
|
|
@ -14,23 +14,23 @@ public class CustomizationSet
|
||||||
{
|
{
|
||||||
internal CustomizationSet(SubRace clan, Gender gender)
|
internal CustomizationSet(SubRace clan, Gender gender)
|
||||||
{
|
{
|
||||||
Gender = gender;
|
Gender = gender;
|
||||||
Clan = clan;
|
Clan = clan;
|
||||||
Race = clan.ToRace();
|
Race = clan.ToRace();
|
||||||
_settingAvailable = 0;
|
SettingAvailable = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Gender Gender { get; }
|
public Gender Gender { get; }
|
||||||
public SubRace Clan { get; }
|
public SubRace Clan { get; }
|
||||||
public Race Race { get; }
|
public Race Race { get; }
|
||||||
|
|
||||||
private CustomizeFlag _settingAvailable;
|
public CustomizeFlag SettingAvailable { get; internal set; }
|
||||||
|
|
||||||
internal void SetAvailable(CustomizeIndex index)
|
internal void SetAvailable(CustomizeIndex index)
|
||||||
=> _settingAvailable |= index.ToFlag();
|
=> SettingAvailable |= index.ToFlag();
|
||||||
|
|
||||||
public bool IsAvailable(CustomizeIndex index)
|
public bool IsAvailable(CustomizeIndex index)
|
||||||
=> _settingAvailable.HasFlag(index.ToFlag());
|
=> SettingAvailable.HasFlag(index.ToFlag());
|
||||||
|
|
||||||
// Meta
|
// Meta
|
||||||
public IReadOnlyList<string> OptionName { get; internal set; } = null!;
|
public IReadOnlyList<string> OptionName { get; internal set; } = null!;
|
||||||
|
|
@ -138,10 +138,11 @@ public class CustomizationSet
|
||||||
CharaMakeParams.MenuType.ListSelector => GetInteger(out custom),
|
CharaMakeParams.MenuType.ListSelector => GetInteger(out custom),
|
||||||
CharaMakeParams.MenuType.IconSelector => index switch
|
CharaMakeParams.MenuType.IconSelector => index switch
|
||||||
{
|
{
|
||||||
CustomizeIndex.Face => Get(Faces, HrothgarFaceHack(value), out custom),
|
CustomizeIndex.Face => Get(Faces, HrothgarFaceHack(value), out custom),
|
||||||
CustomizeIndex.Hairstyle => Get((face = HrothgarFaceHack(face)).Value < HairByFace.Count ? HairByFace[face.Value] : HairStyles, value, out custom),
|
CustomizeIndex.Hairstyle => Get((face = HrothgarFaceHack(face)).Value < HairByFace.Count ? HairByFace[face.Value] : HairStyles,
|
||||||
|
value, out custom),
|
||||||
CustomizeIndex.TailShape => Get(TailEarShapes, value, out custom),
|
CustomizeIndex.TailShape => Get(TailEarShapes, value, out custom),
|
||||||
CustomizeIndex.FacePaint => Get(FacePaints, value, out custom),
|
CustomizeIndex.FacePaint => Get(FacePaints, value, out custom),
|
||||||
CustomizeIndex.LipColor => Get(LipColorsDark, value, out custom),
|
CustomizeIndex.LipColor => Get(LipColorsDark, value, out custom),
|
||||||
_ => Invalid(out custom),
|
_ => Invalid(out custom),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ public unsafe class PenumbraAttach : IDisposable
|
||||||
if (button != MouseButton.Right || type != ChangedItemType.Item)
|
if (button != MouseButton.Right || type != ChangedItemType.Item)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var item = (Lumina.Excel.GeneratedSheets.Item)type.GetObject(id)!;
|
var item = (Lumina.Excel.GeneratedSheets.Item)type.GetObject(Dalamud.GameData, id)!;
|
||||||
var writeItem = new Item(item, string.Empty);
|
var writeItem = new Item(item, string.Empty);
|
||||||
|
|
||||||
UpdateItem(ObjectManager.GPosePlayer, writeItem);
|
UpdateItem(ObjectManager.GPosePlayer, writeItem);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Glamourer.Saves;
|
using Glamourer.Saves;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
namespace Glamourer.Designs;
|
namespace Glamourer.Designs;
|
||||||
|
|
||||||
|
|
@ -52,7 +53,7 @@ public class FixedDesign
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
public bool Enabled;
|
public bool Enabled;
|
||||||
public List<Actor.IIdentifier> Actors;
|
public List<ActorIdentifier> Actors;
|
||||||
public List<(FixedCondition, Design)> Customization;
|
public List<(FixedCondition, Design)> Customization;
|
||||||
public List<(FixedCondition, Design)> Equipment;
|
public List<(FixedCondition, Design)> Equipment;
|
||||||
public List<(FixedCondition, Design)> Weapons;
|
public List<(FixedCondition, Design)> Weapons;
|
||||||
|
|
@ -60,7 +61,7 @@ public class FixedDesign
|
||||||
public FixedDesign(string name)
|
public FixedDesign(string name)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Actors = new List<Actor.IIdentifier>();
|
Actors = new List<ActorIdentifier>();
|
||||||
Customization = new List<(FixedCondition, Design)>();
|
Customization = new List<(FixedCondition, Design)>();
|
||||||
Equipment = new List<(FixedCondition, Design)>();
|
Equipment = new List<(FixedCondition, Design)>();
|
||||||
Weapons = new List<(FixedCondition, Design)>();
|
Weapons = new List<(FixedCondition, Design)>();
|
||||||
|
|
@ -125,7 +126,7 @@ public class FixedDesign
|
||||||
j.WritePropertyName(nameof(Actors));
|
j.WritePropertyName(nameof(Actors));
|
||||||
j.WriteStartArray();
|
j.WriteStartArray();
|
||||||
foreach (var actor in Actors)
|
foreach (var actor in Actors)
|
||||||
actor.ToJson(j);
|
actor.ToJson().WriteTo(j);
|
||||||
j.WriteEndArray();
|
j.WriteEndArray();
|
||||||
j.WritePropertyName(nameof(Customization));
|
j.WritePropertyName(nameof(Customization));
|
||||||
j.WriteStartArray();
|
j.WriteStartArray();
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Glamourer.Interop;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
using OtterGui.Log;
|
using OtterGui.Log;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
namespace Glamourer;
|
namespace Glamourer;
|
||||||
|
|
||||||
|
|
@ -31,6 +32,7 @@ public class Glamourer : IDalamudPlugin
|
||||||
public static Logger Log = null!;
|
public static Logger Log = null!;
|
||||||
|
|
||||||
public static IObjectIdentifier Identifier = null!;
|
public static IObjectIdentifier Identifier = null!;
|
||||||
|
public static ActorManager Actors = null!;
|
||||||
public static PenumbraAttach Penumbra = null!;
|
public static PenumbraAttach Penumbra = null!;
|
||||||
public static ICustomizationManager Customization = null!;
|
public static ICustomizationManager Customization = null!;
|
||||||
public static RestrictedGear RestrictedGear = null!;
|
public static RestrictedGear RestrictedGear = null!;
|
||||||
|
|
@ -60,8 +62,10 @@ public class Glamourer : IDalamudPlugin
|
||||||
|
|
||||||
Config = GlamourerConfig.Load();
|
Config = GlamourerConfig.Load();
|
||||||
|
|
||||||
Identifier = global::Penumbra.GameData.GameData.GetIdentifier(Dalamud.GameData);
|
Identifier = global::Penumbra.GameData.GameData.GetIdentifier(Dalamud.PluginInterface, Dalamud.GameData);
|
||||||
Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
|
Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
|
||||||
|
Actors = new ActorManager(Dalamud.PluginInterface, Dalamud.Objects, Dalamud.ClientState, Dalamud.GameData,
|
||||||
|
i => (short)Penumbra.CutsceneParent(i));
|
||||||
FixedDesigns = new FixedDesigns();
|
FixedDesigns = new FixedDesigns();
|
||||||
CurrentManipulations = new CurrentManipulations();
|
CurrentManipulations = new CurrentManipulations();
|
||||||
//Designs = new DesignManager();
|
//Designs = new DesignManager();
|
||||||
|
|
@ -93,6 +97,8 @@ public class Glamourer : IDalamudPlugin
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Dalamud.PluginInterface.RelinquishData("test1");
|
||||||
RedrawManager?.Dispose();
|
RedrawManager?.Dispose();
|
||||||
Penumbra?.Dispose();
|
Penumbra?.Dispose();
|
||||||
if (_windowSystem != null)
|
if (_windowSystem != null)
|
||||||
|
|
|
||||||
|
|
@ -1,207 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using ImGuiNET;
|
|
||||||
|
|
||||||
namespace Glamourer.Gui;
|
|
||||||
|
|
||||||
public class ComboWithFilter<T>
|
|
||||||
{
|
|
||||||
private readonly string _label;
|
|
||||||
private readonly string _filterLabel;
|
|
||||||
private readonly string _listLabel;
|
|
||||||
private string _currentFilter = string.Empty;
|
|
||||||
private string _currentFilterLower = string.Empty;
|
|
||||||
private bool _focus;
|
|
||||||
private readonly float _size;
|
|
||||||
private float _previewSize;
|
|
||||||
private readonly IReadOnlyList<T> _items;
|
|
||||||
private readonly IReadOnlyList<(string, int)> _itemNamesLower;
|
|
||||||
private readonly Func<T, string> _itemToName;
|
|
||||||
private IReadOnlyList<(string, int)> _currentItemNames;
|
|
||||||
private bool _needsClear;
|
|
||||||
|
|
||||||
public Action? PrePreview;
|
|
||||||
public Action? PostPreview;
|
|
||||||
public Func<T, bool>? CreateSelectable;
|
|
||||||
public Action? PreList;
|
|
||||||
public Action? PostList;
|
|
||||||
public float? HeightPerItem;
|
|
||||||
|
|
||||||
private float _heightPerItem;
|
|
||||||
|
|
||||||
public ImGuiComboFlags Flags { get; set; } = ImGuiComboFlags.None;
|
|
||||||
public int ItemsAtOnce { get; set; } = 12;
|
|
||||||
|
|
||||||
private void UpdateFilter(string newFilter)
|
|
||||||
{
|
|
||||||
if (newFilter == _currentFilter)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var lower = newFilter.ToLowerInvariant();
|
|
||||||
if (_currentFilterLower.Any() && lower.Contains(_currentFilterLower))
|
|
||||||
_currentItemNames = _currentItemNames.Where(p => p.Item1.Contains(lower)).ToArray();
|
|
||||||
else if (lower.Any())
|
|
||||||
_currentItemNames = _itemNamesLower.Where(p => p.Item1.Contains(lower)).ToArray();
|
|
||||||
else
|
|
||||||
_currentItemNames = _itemNamesLower;
|
|
||||||
_currentFilter = newFilter;
|
|
||||||
_currentFilterLower = lower;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComboWithFilter(string label, float size, float previewSize, IReadOnlyList<T> items, Func<T, string> itemToName)
|
|
||||||
{
|
|
||||||
_label = label;
|
|
||||||
_filterLabel = $"##_{label}_filter";
|
|
||||||
_listLabel = $"##_{label}_list";
|
|
||||||
_itemToName = itemToName;
|
|
||||||
_items = items;
|
|
||||||
_size = size;
|
|
||||||
_previewSize = previewSize;
|
|
||||||
|
|
||||||
_itemNamesLower = _items.Select((i, idx) => (_itemToName(i).ToLowerInvariant(), idx)).ToArray();
|
|
||||||
_currentItemNames = _itemNamesLower;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComboWithFilter(string label, ComboWithFilter<T> other)
|
|
||||||
{
|
|
||||||
_label = label;
|
|
||||||
_filterLabel = $"##_{label}_filter";
|
|
||||||
_listLabel = $"##_{label}_list";
|
|
||||||
_itemToName = other._itemToName;
|
|
||||||
_items = other._items;
|
|
||||||
_itemNamesLower = other._itemNamesLower;
|
|
||||||
_currentItemNames = other._currentItemNames;
|
|
||||||
_size = other._size;
|
|
||||||
_previewSize = other._previewSize;
|
|
||||||
PrePreview = other.PrePreview;
|
|
||||||
PostPreview = other.PostPreview;
|
|
||||||
CreateSelectable = other.CreateSelectable;
|
|
||||||
PreList = other.PreList;
|
|
||||||
PostList = other.PostList;
|
|
||||||
HeightPerItem = other.HeightPerItem;
|
|
||||||
Flags = other.Flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool DrawList(string currentName, out int numItems, out int nodeIdx, ref T? value)
|
|
||||||
{
|
|
||||||
numItems = ItemsAtOnce;
|
|
||||||
nodeIdx = -1;
|
|
||||||
if (!ImGui.BeginChild(_listLabel, new Vector2(_size, ItemsAtOnce * _heightPerItem)))
|
|
||||||
{
|
|
||||||
ImGui.EndChild();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!_focus)
|
|
||||||
{
|
|
||||||
ImGui.SetScrollY(0);
|
|
||||||
_focus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var scrollY = Math.Max((int)(ImGui.GetScrollY() / _heightPerItem) - 1, 0);
|
|
||||||
var restHeight = scrollY * _heightPerItem;
|
|
||||||
numItems = 0;
|
|
||||||
nodeIdx = 0;
|
|
||||||
|
|
||||||
if (restHeight > 0)
|
|
||||||
ImGui.Dummy(Vector2.UnitY * restHeight);
|
|
||||||
|
|
||||||
for (var i = scrollY; i < _currentItemNames.Count; ++i)
|
|
||||||
{
|
|
||||||
if (++numItems > ItemsAtOnce + 2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
nodeIdx = _currentItemNames[i].Item2;
|
|
||||||
var item = _items[nodeIdx]!;
|
|
||||||
bool success;
|
|
||||||
if (CreateSelectable != null)
|
|
||||||
{
|
|
||||||
success = CreateSelectable(item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var name = _itemToName(item);
|
|
||||||
success = ImGui.Selectable(name, name == currentName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
value = item;
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_currentItemNames.Count > ItemsAtOnce + 2)
|
|
||||||
ImGui.Dummy(Vector2.UnitY * (_currentItemNames.Count - ItemsAtOnce - 2 - scrollY) * _heightPerItem);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ImGui.EndChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Draw(string currentName, out T? value, float? size = null)
|
|
||||||
{
|
|
||||||
if (size.HasValue)
|
|
||||||
_previewSize = size.Value;
|
|
||||||
|
|
||||||
value = default;
|
|
||||||
ImGui.SetNextItemWidth(_previewSize);
|
|
||||||
PrePreview?.Invoke();
|
|
||||||
if (!ImGui.BeginCombo(_label, currentName, Flags))
|
|
||||||
{
|
|
||||||
if (_needsClear)
|
|
||||||
{
|
|
||||||
_needsClear = false;
|
|
||||||
_focus = false;
|
|
||||||
UpdateFilter(string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
PostPreview?.Invoke();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_needsClear = true;
|
|
||||||
PostPreview?.Invoke();
|
|
||||||
|
|
||||||
_heightPerItem = HeightPerItem ?? ImGui.GetTextLineHeightWithSpacing();
|
|
||||||
|
|
||||||
bool ret;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ImGui.SetNextItemWidth(-1);
|
|
||||||
var tmp = _currentFilter;
|
|
||||||
if (ImGui.InputTextWithHint(_filterLabel, "Filter...", ref tmp, 255))
|
|
||||||
UpdateFilter(tmp);
|
|
||||||
|
|
||||||
var isFocused = ImGui.IsItemActive();
|
|
||||||
if (!_focus)
|
|
||||||
ImGui.SetKeyboardFocusHere();
|
|
||||||
|
|
||||||
PreList?.Invoke();
|
|
||||||
ret = DrawList(currentName, out var numItems, out var nodeIdx, ref value);
|
|
||||||
PostList?.Invoke();
|
|
||||||
|
|
||||||
if (!isFocused && numItems <= 1 && nodeIdx >= 0)
|
|
||||||
{
|
|
||||||
value = _items[nodeIdx];
|
|
||||||
ret = true;
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ImGui.EndCombo();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
@ -354,7 +355,7 @@ public partial class EquipmentDrawer
|
||||||
{
|
{
|
||||||
0 => SmallClothes,
|
0 => SmallClothes,
|
||||||
9903 => SmallClothesNpc,
|
9903 => SmallClothesNpc,
|
||||||
_ => Identifier.Identify(set, weapon, variant, slot) ?? Unknown,
|
_ => Identifier.Identify(set, weapon, variant, slot).FirstOrDefault(Unknown),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
using Glamourer.State;
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
@ -11,6 +10,11 @@ using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer.Gui.Equipment;
|
namespace Glamourer.Gui.Equipment;
|
||||||
|
|
||||||
|
public enum ApplicationFlags
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public partial class EquipmentDrawer
|
public partial class EquipmentDrawer
|
||||||
{
|
{
|
||||||
private Race _race;
|
private Race _race;
|
||||||
|
|
@ -26,7 +30,7 @@ public partial class EquipmentDrawer
|
||||||
{
|
{
|
||||||
Stains = GameData.Stains(Dalamud.GameData);
|
Stains = GameData.Stains(Dalamud.GameData);
|
||||||
StainCombo = new FilterStainCombo(140);
|
StainCombo = new FilterStainCombo(140);
|
||||||
Identifier = Penumbra.GameData.GameData.GetIdentifier(Dalamud.GameData);
|
Identifier = Glamourer.Identifier;
|
||||||
ItemCombos = EquipSlotExtensions.EqdpSlots.Select(s => new ItemCombo(s)).ToArray();
|
ItemCombos = EquipSlotExtensions.EqdpSlots.Select(s => new ItemCombo(s)).ToArray();
|
||||||
MainHandCombo = new WeaponCombo(EquipSlot.MainHand);
|
MainHandCombo = new WeaponCombo(EquipSlot.MainHand);
|
||||||
OffHandCombo = new WeaponCombo(EquipSlot.OffHand);
|
OffHandCombo = new WeaponCombo(EquipSlot.OffHand);
|
||||||
|
|
|
||||||
|
|
@ -20,15 +20,20 @@ public partial class EquipmentDrawer
|
||||||
|
|
||||||
private sealed class FilterStainCombo : FilterComboBase<Stain>
|
private sealed class FilterStainCombo : FilterComboBase<Stain>
|
||||||
{
|
{
|
||||||
private readonly float _comboWidth;
|
private readonly float _comboWidth;
|
||||||
private Vector2 _buttonSize;
|
private Vector2 _buttonSize;
|
||||||
|
public ImRaii.Color Color = new();
|
||||||
|
|
||||||
public FilterStainCombo(float comboWidth)
|
public FilterStainCombo(float comboWidth)
|
||||||
: base(Stains.Values.ToArray(), false)
|
: base(Stains.Values.ToArray(), false)
|
||||||
=> _comboWidth = comboWidth;
|
=> _comboWidth = comboWidth;
|
||||||
|
|
||||||
protected override float GetFilterWidth()
|
protected override float GetFilterWidth()
|
||||||
=> _buttonSize.X + ImGui.GetStyle().ScrollbarSize;
|
{
|
||||||
|
// Hack to not color the filter frame.
|
||||||
|
Color.Pop();
|
||||||
|
return _buttonSize.X + ImGui.GetStyle().ScrollbarSize;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void DrawList(float width, float itemHeight)
|
protected override void DrawList(float width, float itemHeight)
|
||||||
{
|
{
|
||||||
|
|
@ -58,11 +63,12 @@ public partial class EquipmentDrawer
|
||||||
|
|
||||||
private void DrawStainSelector()
|
private void DrawStainSelector()
|
||||||
{
|
{
|
||||||
var foundIdx = StainCombo.Items.IndexOf(s => s.RowIndex.Equals(_currentArmor.Stain));
|
var foundIdx = StainCombo.Items.IndexOf(s => s.RowIndex.Equals(_currentArmor.Stain));
|
||||||
var stain = foundIdx >= 0 ? StainCombo.Items[foundIdx] : default;
|
var stain = foundIdx >= 0 ? StainCombo.Items[foundIdx] : default;
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.FrameBg, stain.RgbaColor, foundIdx >= 0);
|
StainCombo.Color.Push(ImGuiCol.FrameBg, stain.RgbaColor, foundIdx >= 0);
|
||||||
var change = StainCombo.Draw("##stainSelector", string.Empty, ref foundIdx, ImGui.GetFrameHeight(), ImGui.GetFrameHeight(),
|
var change = StainCombo.Draw("##stainSelector", string.Empty, ref foundIdx, ImGui.GetFrameHeight(), ImGui.GetFrameHeight(),
|
||||||
ImGuiComboFlags.NoArrowButton);
|
ImGuiComboFlags.NoArrowButton);
|
||||||
|
StainCombo.Color.Pop();
|
||||||
if (!change && (byte)_currentArmor.Stain != 0)
|
if (!change && (byte)_currentArmor.Stain != 0)
|
||||||
{
|
{
|
||||||
ImGuiUtil.HoverTooltip($"{stain.Name}\nRight-click to clear.");
|
ImGuiUtil.HoverTooltip($"{stain.Name}\nRight-click to clear.");
|
||||||
|
|
@ -82,7 +88,7 @@ public partial class EquipmentDrawer
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCheckbox(ref ApplicationFlags flags)
|
private void DrawCheckbox(ref ApplicationFlags flags)
|
||||||
=> DrawCheckbox("##checkbox", "Enable writing this slot in this save.", ref flags, _currentSlot.ToApplicationFlag());
|
=> DrawCheckbox("##checkbox", "Enable writing this slot in this save.", ref flags, 0);
|
||||||
|
|
||||||
private static void DrawCheckbox(string label, string tooltip, ref ApplicationFlags flags, ApplicationFlags flag)
|
private static void DrawCheckbox(string label, string tooltip, ref ApplicationFlags flags, ApplicationFlags flag)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using ImGui = ImGuiNET.ImGui;
|
using ImGui = ImGuiNET.ImGui;
|
||||||
|
|
||||||
|
|
@ -26,7 +27,7 @@ internal partial class Interface
|
||||||
public ActorTab(CurrentManipulations manipulations)
|
public ActorTab(CurrentManipulations manipulations)
|
||||||
=> _manipulations = manipulations;
|
=> _manipulations = manipulations;
|
||||||
|
|
||||||
private Actor.IIdentifier _identifier = Actor.IIdentifier.Invalid;
|
private ActorIdentifier _identifier = ActorIdentifier.Invalid;
|
||||||
private ObjectManager.ActorData _currentData = ObjectManager.ActorData.Invalid;
|
private ObjectManager.ActorData _currentData = ObjectManager.ActorData.Invalid;
|
||||||
private string _currentLabel = string.Empty;
|
private string _currentLabel = string.Empty;
|
||||||
private CurrentDesign? _currentSave;
|
private CurrentDesign? _currentSave;
|
||||||
|
|
@ -50,7 +51,7 @@ internal partial class Interface
|
||||||
|
|
||||||
private unsafe void DrawPanel()
|
private unsafe void DrawPanel()
|
||||||
{
|
{
|
||||||
if (_identifier == Actor.IIdentifier.Invalid)
|
if (_identifier == ActorIdentifier.Invalid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,15 +66,16 @@ internal partial class Interface
|
||||||
|
|
||||||
RevertButton();
|
RevertButton();
|
||||||
CustomizationDrawer.Draw(_currentSave.Data.Customize, _currentSave.Data.Equipment, _currentData.Objects,
|
CustomizationDrawer.Draw(_currentSave.Data.Customize, _currentSave.Data.Equipment, _currentData.Objects,
|
||||||
_identifier is Actor.SpecialIdentifier);
|
_identifier.Type == IdentifierType.Special);
|
||||||
|
|
||||||
EquipmentDrawer.Draw(_currentSave.Data.Customize, _currentSave.Data.Equipment, ref _currentSave.Data.MainHand, ref _currentSave.Data.OffHand, _currentData.Objects, _identifier is Actor.SpecialIdentifier);
|
EquipmentDrawer.Draw(_currentSave.Data.Customize, _currentSave.Data.Equipment, ref _currentSave.Data.MainHand,
|
||||||
|
ref _currentSave.Data.OffHand, _currentData.Objects, _identifier.Type == IdentifierType.Special);
|
||||||
}
|
}
|
||||||
|
|
||||||
private const uint RedHeaderColor = 0xFF1818C0;
|
private const uint RedHeaderColor = 0xFF1818C0;
|
||||||
private const uint GreenHeaderColor = 0xFF18C018;
|
private const uint GreenHeaderColor = 0xFF18C018;
|
||||||
|
|
||||||
private void RevertButton()
|
private unsafe void RevertButton()
|
||||||
{
|
{
|
||||||
if (ImGui.Button("Revert"))
|
if (ImGui.Button("Revert"))
|
||||||
{
|
{
|
||||||
|
|
@ -88,36 +90,38 @@ internal partial class Interface
|
||||||
_currentSave!.Reset();
|
_currentSave!.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
VisorBox();
|
if (_currentData.Objects.Count > 0)
|
||||||
|
ImGui.TextUnformatted(_currentData.Objects[0].Pointer->GameObject.DataID.ToString());
|
||||||
|
//VisorBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void VisorBox()
|
//private unsafe void VisorBox()
|
||||||
{
|
//{
|
||||||
var (flags, mask) = (_currentSave!.Data.Flags & (ApplicationFlags.SetVisor | ApplicationFlags.Visor)) switch
|
// var (flags, mask) = (_currentSave!.Data.Flags & (ApplicationFlags.SetVisor | ApplicationFlags.Visor)) switch
|
||||||
{
|
// {
|
||||||
ApplicationFlags.SetVisor => (0u, 3u),
|
// ApplicationFlags.SetVisor => (0u, 3u),
|
||||||
ApplicationFlags.Visor => (1u, 3u),
|
// ApplicationFlags.Visor => (1u, 3u),
|
||||||
ApplicationFlags.SetVisor | ApplicationFlags.Visor => (3u, 3u),
|
// ApplicationFlags.SetVisor | ApplicationFlags.Visor => (3u, 3u),
|
||||||
_ => (2u, 3u),
|
// _ => (2u, 3u),
|
||||||
};
|
// };
|
||||||
var tmp = flags;
|
// var tmp = flags;
|
||||||
if (ImGui.CheckboxFlags("Visor Toggled", ref tmp, mask))
|
// if (ImGui.CheckboxFlags("Visor Toggled", ref tmp, mask))
|
||||||
{
|
// {
|
||||||
_currentSave.Data.Flags = flags switch
|
// _currentSave.Data.Flags = flags switch
|
||||||
{
|
// {
|
||||||
0 => (_currentSave.Data.Flags | ApplicationFlags.Visor) & ~ApplicationFlags.SetVisor,
|
// 0 => (_currentSave.Data.Flags | ApplicationFlags.Visor) & ~ApplicationFlags.SetVisor,
|
||||||
1 => _currentSave.Data.Flags | ApplicationFlags.SetVisor,
|
// 1 => _currentSave.Data.Flags | ApplicationFlags.SetVisor,
|
||||||
2 => _currentSave.Data.Flags | ApplicationFlags.SetVisor,
|
// 2 => _currentSave.Data.Flags | ApplicationFlags.SetVisor,
|
||||||
_ => _currentSave.Data.Flags & ~(ApplicationFlags.SetVisor | ApplicationFlags.Visor),
|
// _ => _currentSave.Data.Flags & ~(ApplicationFlags.SetVisor | ApplicationFlags.Visor),
|
||||||
};
|
// };
|
||||||
if (_currentSave.Data.Flags.HasFlag(ApplicationFlags.SetVisor))
|
// if (_currentSave.Data.Flags.HasFlag(ApplicationFlags.SetVisor))
|
||||||
{
|
// {
|
||||||
var on = _currentSave.Data.Flags.HasFlag(ApplicationFlags.Visor);
|
// var on = _currentSave.Data.Flags.HasFlag(ApplicationFlags.Visor);
|
||||||
foreach (var actor in _currentData.Objects.Where(a => a.IsHuman && a.DrawObject))
|
// foreach (var actor in _currentData.Objects.Where(a => a.IsHuman && a.DrawObject))
|
||||||
RedrawManager.SetVisor(actor.DrawObject.Pointer, on);
|
// RedrawManager.SetVisor(actor.DrawObject.Pointer, on);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
private void DrawPanelHeader()
|
private void DrawPanelHeader()
|
||||||
{
|
{
|
||||||
|
|
@ -213,10 +217,10 @@ internal partial class Interface
|
||||||
ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeight());
|
ImGuiClip.DrawEndDummy(remainder, ImGui.GetTextLineHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckFilter((Actor.IIdentifier, ObjectManager.ActorData) pair)
|
private bool CheckFilter((ActorIdentifier, ObjectManager.ActorData) pair)
|
||||||
=> _actorFilter.IsEmpty || pair.Item2.Label.Contains(_actorFilter.Lower, StringComparison.OrdinalIgnoreCase);
|
=> _actorFilter.IsEmpty || pair.Item2.Label.Contains(_actorFilter.Lower, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
private void DrawSelectable((Actor.IIdentifier, ObjectManager.ActorData) pair)
|
private void DrawSelectable((ActorIdentifier, ObjectManager.ActorData) pair)
|
||||||
{
|
{
|
||||||
var equal = pair.Item1.Equals(_identifier);
|
var equal = pair.Item1.Equals(_identifier);
|
||||||
if (ImGui.Selectable(pair.Item2.Label, equal) && !equal)
|
if (ImGui.Selectable(pair.Item2.Label, equal) && !equal)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
namespace Glamourer.Gui;
|
namespace Glamourer.Gui;
|
||||||
|
|
||||||
|
|
@ -18,10 +19,10 @@ internal partial class Interface
|
||||||
{
|
{
|
||||||
private readonly CurrentManipulations _currentManipulations;
|
private readonly CurrentManipulations _currentManipulations;
|
||||||
|
|
||||||
private LowerString _manipulationFilter = LowerString.Empty;
|
private LowerString _manipulationFilter = LowerString.Empty;
|
||||||
private Actor.IIdentifier _selection = Actor.IIdentifier.Invalid;
|
private ActorIdentifier _selection = ActorIdentifier.Invalid;
|
||||||
private CurrentDesign? _save = null;
|
private CurrentDesign? _save = null;
|
||||||
private bool _delete = false;
|
private bool _delete = false;
|
||||||
|
|
||||||
public DebugStateTab(CurrentManipulations currentManipulations)
|
public DebugStateTab(CurrentManipulations currentManipulations)
|
||||||
=> _currentManipulations = currentManipulations;
|
=> _currentManipulations = currentManipulations;
|
||||||
|
|
@ -43,7 +44,7 @@ internal partial class Interface
|
||||||
{
|
{
|
||||||
_delete = false;
|
_delete = false;
|
||||||
_currentManipulations.DeleteSave(_selection);
|
_currentManipulations.DeleteSave(_selection);
|
||||||
_selection = Actor.IIdentifier.Invalid;
|
_selection = ActorIdentifier.Invalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,14 +73,14 @@ internal partial class Interface
|
||||||
DrawSelector(oldSpacing);
|
DrawSelector(oldSpacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CheckFilter(KeyValuePair<Actor.IIdentifier, CurrentDesign> data)
|
private bool CheckFilter(KeyValuePair<ActorIdentifier, CurrentDesign> data)
|
||||||
{
|
{
|
||||||
if (data.Key.Equals(_selection))
|
if (data.Key.Equals(_selection))
|
||||||
_save = data.Value;
|
_save = data.Value;
|
||||||
return _manipulationFilter.Length == 0 || _manipulationFilter.IsContained(data.Key.ToString()!);
|
return _manipulationFilter.Length == 0 || _manipulationFilter.IsContained(data.Key.ToString()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawSelectable(KeyValuePair<Actor.IIdentifier, CurrentDesign> data)
|
private void DrawSelectable(KeyValuePair<ActorIdentifier, CurrentDesign> data)
|
||||||
{
|
{
|
||||||
var equal = data.Key.Equals(_selection);
|
var equal = data.Key.Equals(_selection);
|
||||||
if (ImGui.Selectable(data.Key.ToString(), equal))
|
if (ImGui.Selectable(data.Key.ToString(), equal))
|
||||||
|
|
|
||||||
|
|
@ -1,375 +0,0 @@
|
||||||
using System;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
using Lumina.Excel.GeneratedSheets;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Penumbra.GameData.ByteString;
|
|
||||||
|
|
||||||
namespace Glamourer.Interop;
|
|
||||||
|
|
||||||
public unsafe partial struct Actor
|
|
||||||
{
|
|
||||||
public interface IIdentifier : IEquatable<IIdentifier>
|
|
||||||
{
|
|
||||||
Utf8String Name { get; }
|
|
||||||
public bool IsValid { get; }
|
|
||||||
|
|
||||||
public IIdentifier CreatePermanent();
|
|
||||||
|
|
||||||
public static readonly InvalidIdentifier Invalid = new();
|
|
||||||
|
|
||||||
public void ToJson(JsonTextWriter j);
|
|
||||||
|
|
||||||
public static IIdentifier? FromJson(JObject j)
|
|
||||||
{
|
|
||||||
switch (j["Type"]?.Value<string>() ?? string.Empty)
|
|
||||||
{
|
|
||||||
case nameof(PlayerIdentifier):
|
|
||||||
{
|
|
||||||
var name = j[nameof(Name)]?.Value<string>();
|
|
||||||
if (name.IsNullOrEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var serverId = j[nameof(PlayerIdentifier.HomeWorld)]?.Value<ushort>() ?? ushort.MaxValue;
|
|
||||||
return new PlayerIdentifier(Utf8String.FromStringUnsafe(name, false), serverId);
|
|
||||||
}
|
|
||||||
case nameof(SpecialIdentifier):
|
|
||||||
{
|
|
||||||
var index = j[nameof(SpecialIdentifier.Index)]?.Value<ushort>() ?? ushort.MaxValue;
|
|
||||||
return new SpecialIdentifier(index);
|
|
||||||
}
|
|
||||||
case nameof(OwnedIdentifier):
|
|
||||||
{
|
|
||||||
var name = j[nameof(Name)]?.Value<string>();
|
|
||||||
if (name.IsNullOrEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var ownerName = j[nameof(OwnedIdentifier.OwnerName)]?.Value<string>();
|
|
||||||
if (ownerName.IsNullOrEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var ownerHomeWorld = j[nameof(OwnedIdentifier.OwnerHomeWorld)]?.Value<ushort>() ?? ushort.MaxValue;
|
|
||||||
var dataId = j[nameof(OwnedIdentifier.DataId)]?.Value<ushort>() ?? ushort.MaxValue;
|
|
||||||
var kind = j[nameof(OwnedIdentifier.Kind)]?.Value<ObjectKind>() ?? ObjectKind.Player;
|
|
||||||
|
|
||||||
return new OwnedIdentifier(Utf8String.FromStringUnsafe(name, false), Utf8String.FromStringUnsafe(ownerName, false),
|
|
||||||
ownerHomeWorld, dataId, kind);
|
|
||||||
}
|
|
||||||
case nameof(NpcIdentifier):
|
|
||||||
{
|
|
||||||
var name = j[nameof(Name)]?.Value<string>();
|
|
||||||
if (name.IsNullOrEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var dataId = j[nameof(NpcIdentifier.DataId)]?.Value<uint>() ?? uint.MaxValue;
|
|
||||||
|
|
||||||
return new NpcIdentifier(Utf8String.FromStringUnsafe(name, false), ushort.MaxValue, dataId);
|
|
||||||
}
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InvalidIdentifier : IIdentifier
|
|
||||||
{
|
|
||||||
public Utf8String Name
|
|
||||||
=> Utf8String.Empty;
|
|
||||||
|
|
||||||
public bool IsValid
|
|
||||||
=> false;
|
|
||||||
|
|
||||||
public bool Equals(IIdentifier? other)
|
|
||||||
=> false;
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
=> 0;
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
=> "Invalid";
|
|
||||||
|
|
||||||
public IIdentifier CreatePermanent()
|
|
||||||
=> this;
|
|
||||||
|
|
||||||
public void ToJson(JsonTextWriter j)
|
|
||||||
{ }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PlayerIdentifier : IIdentifier, IEquatable<PlayerIdentifier>
|
|
||||||
{
|
|
||||||
public Utf8String Name { get; }
|
|
||||||
public readonly ushort HomeWorld;
|
|
||||||
|
|
||||||
public bool IsValid
|
|
||||||
=> true;
|
|
||||||
|
|
||||||
public PlayerIdentifier(Utf8String name, ushort homeWorld)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
HomeWorld = homeWorld;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(IIdentifier? other)
|
|
||||||
=> Equals(other as PlayerIdentifier);
|
|
||||||
|
|
||||||
public bool Equals(PlayerIdentifier? other)
|
|
||||||
=> other?.HomeWorld == HomeWorld && other.Name.Equals(Name);
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
=> HashCode.Combine(Name.Crc32, HomeWorld);
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
=> $"{Name} ({HomeWorld})";
|
|
||||||
|
|
||||||
public IIdentifier CreatePermanent()
|
|
||||||
=> new PlayerIdentifier(Name.Clone(), HomeWorld);
|
|
||||||
|
|
||||||
public void ToJson(JsonTextWriter j)
|
|
||||||
{
|
|
||||||
j.WriteStartObject();
|
|
||||||
j.WritePropertyName("Type");
|
|
||||||
j.WriteValue(GetType().Name);
|
|
||||||
j.WritePropertyName(nameof(Name));
|
|
||||||
j.WriteValue(Name);
|
|
||||||
j.WritePropertyName(nameof(HomeWorld));
|
|
||||||
j.WriteValue(HomeWorld);
|
|
||||||
j.WriteEndObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SpecialIdentifier : IIdentifier, IEquatable<SpecialIdentifier>
|
|
||||||
{
|
|
||||||
public Utf8String Name
|
|
||||||
=> Utf8String.Empty;
|
|
||||||
|
|
||||||
public readonly ushort Index;
|
|
||||||
|
|
||||||
public bool IsValid
|
|
||||||
=> true;
|
|
||||||
|
|
||||||
public SpecialIdentifier(ushort index)
|
|
||||||
=> Index = index;
|
|
||||||
|
|
||||||
public bool Equals(IIdentifier? other)
|
|
||||||
=> Equals(other as SpecialIdentifier);
|
|
||||||
|
|
||||||
public bool Equals(SpecialIdentifier? other)
|
|
||||||
=> other?.Index == Index;
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
=> Index;
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
=> $"Special Actor {Index}";
|
|
||||||
|
|
||||||
public IIdentifier CreatePermanent()
|
|
||||||
=> this;
|
|
||||||
|
|
||||||
public void ToJson(JsonTextWriter j)
|
|
||||||
{
|
|
||||||
j.WriteStartObject();
|
|
||||||
j.WritePropertyName("Type");
|
|
||||||
j.WriteValue(GetType().Name);
|
|
||||||
j.WritePropertyName(nameof(Index));
|
|
||||||
j.WriteValue(Index);
|
|
||||||
j.WriteEndObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class OwnedIdentifier : IIdentifier, IEquatable<OwnedIdentifier>
|
|
||||||
{
|
|
||||||
public Utf8String Name { get; }
|
|
||||||
public readonly Utf8String OwnerName;
|
|
||||||
public readonly uint DataId;
|
|
||||||
public readonly ushort OwnerHomeWorld;
|
|
||||||
public readonly ObjectKind Kind;
|
|
||||||
|
|
||||||
public bool IsValid
|
|
||||||
=> true;
|
|
||||||
|
|
||||||
public OwnedIdentifier(Utf8String actorName, Utf8String ownerName, ushort ownerHomeWorld, uint dataId, ObjectKind kind)
|
|
||||||
{
|
|
||||||
OwnerName = ownerName;
|
|
||||||
OwnerHomeWorld = ownerHomeWorld;
|
|
||||||
DataId = dataId;
|
|
||||||
Kind = kind;
|
|
||||||
Name = actorName;
|
|
||||||
switch (Kind)
|
|
||||||
{
|
|
||||||
case ObjectKind.MountType:
|
|
||||||
var mount = Dalamud.GameData.GetExcelSheet<Mount>()!.GetRow(dataId);
|
|
||||||
if (mount != null)
|
|
||||||
Name = Utf8String.FromSpanUnsafe(mount.Singular.RawData, false).AsciiToMixed();
|
|
||||||
break;
|
|
||||||
case ObjectKind.Companion:
|
|
||||||
var companion = Dalamud.GameData.GetExcelSheet<Companion>()!.GetRow(dataId);
|
|
||||||
if (companion != null)
|
|
||||||
Name = Utf8String.FromSpanUnsafe(companion.Singular.RawData, false).AsciiToMixed();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(IIdentifier? other)
|
|
||||||
=> Equals(other as OwnedIdentifier);
|
|
||||||
|
|
||||||
public bool Equals(OwnedIdentifier? other)
|
|
||||||
=> other?.DataId == DataId
|
|
||||||
&& other.OwnerHomeWorld == OwnerHomeWorld
|
|
||||||
&& other.Kind == Kind
|
|
||||||
&& other.OwnerName.Equals(OwnerName);
|
|
||||||
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
=> HashCode.Combine(OwnerName.Crc32, OwnerHomeWorld, DataId, Kind);
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
=> $"{OwnerName}s {Name}";
|
|
||||||
|
|
||||||
public IIdentifier CreatePermanent()
|
|
||||||
=> new OwnedIdentifier(Name.Clone(), OwnerName.Clone(), OwnerHomeWorld, DataId, Kind);
|
|
||||||
|
|
||||||
public void ToJson(JsonTextWriter j)
|
|
||||||
{
|
|
||||||
j.WriteStartObject();
|
|
||||||
j.WritePropertyName("Type");
|
|
||||||
j.WriteValue(GetType().Name);
|
|
||||||
j.WritePropertyName(nameof(Name));
|
|
||||||
j.WriteValue(Name);
|
|
||||||
j.WritePropertyName(nameof(OwnerName));
|
|
||||||
j.WriteValue(OwnerName);
|
|
||||||
j.WritePropertyName(nameof(OwnerHomeWorld));
|
|
||||||
j.WriteValue(OwnerHomeWorld);
|
|
||||||
j.WritePropertyName(nameof(Kind));
|
|
||||||
j.WriteValue(Kind);
|
|
||||||
j.WritePropertyName(nameof(DataId));
|
|
||||||
j.WriteValue(DataId);
|
|
||||||
j.WriteEndObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NpcIdentifier : IIdentifier, IEquatable<NpcIdentifier>
|
|
||||||
{
|
|
||||||
public Utf8String Name { get; }
|
|
||||||
public readonly uint DataId;
|
|
||||||
public readonly ushort ObjectIndex;
|
|
||||||
|
|
||||||
public bool IsValid
|
|
||||||
=> true;
|
|
||||||
|
|
||||||
public NpcIdentifier(Utf8String actorName, ushort objectIndex = ushort.MaxValue, uint dataId = uint.MaxValue)
|
|
||||||
{
|
|
||||||
Name = actorName;
|
|
||||||
ObjectIndex = objectIndex;
|
|
||||||
DataId = dataId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(IIdentifier? other)
|
|
||||||
=> Equals(other as NpcIdentifier);
|
|
||||||
|
|
||||||
public bool Equals(NpcIdentifier? other)
|
|
||||||
=> (other?.Name.Equals(Name) ?? false)
|
|
||||||
&& (other.DataId == uint.MaxValue || DataId == uint.MaxValue || other.DataId == DataId)
|
|
||||||
&& (other.ObjectIndex == ushort.MaxValue || ObjectIndex == ushort.MaxValue || other.ObjectIndex == ObjectIndex);
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
=> Name.Crc32;
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
=> DataId == uint.MaxValue ? ObjectIndex == ushort.MaxValue ? Name.ToString() : $"{Name} at {ObjectIndex}" :
|
|
||||||
ObjectIndex == ushort.MaxValue ? $"{Name} ({DataId})" : $"{Name} ({DataId}) at {ObjectIndex}";
|
|
||||||
|
|
||||||
public IIdentifier CreatePermanent()
|
|
||||||
=> new NpcIdentifier(Name.Clone(), ObjectIndex, DataId);
|
|
||||||
|
|
||||||
public void ToJson(JsonTextWriter j)
|
|
||||||
{
|
|
||||||
j.WriteStartObject();
|
|
||||||
j.WritePropertyName("Type");
|
|
||||||
j.WriteValue(GetType().Name);
|
|
||||||
j.WritePropertyName(nameof(Name));
|
|
||||||
j.WriteValue(Name);
|
|
||||||
j.WritePropertyName(nameof(DataId));
|
|
||||||
j.WriteValue(DataId);
|
|
||||||
j.WriteEndObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IIdentifier CreateIdentifier(Actor actor)
|
|
||||||
{
|
|
||||||
if (!actor.Valid)
|
|
||||||
return IIdentifier.Invalid;
|
|
||||||
|
|
||||||
var objectIdx = actor.Pointer->GameObject.ObjectIndex;
|
|
||||||
if (objectIdx is >= 200 and < 240)
|
|
||||||
{
|
|
||||||
var parentIdx = Glamourer.Penumbra.CutsceneParent(objectIdx);
|
|
||||||
if (parentIdx >= 0)
|
|
||||||
{
|
|
||||||
var parent = (Actor)Dalamud.Objects.GetObjectAddress(parentIdx);
|
|
||||||
if (!parent)
|
|
||||||
return IIdentifier.Invalid;
|
|
||||||
|
|
||||||
return CreateIdentifier(parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (actor.ObjectKind)
|
|
||||||
{
|
|
||||||
case ObjectKind.Player:
|
|
||||||
{
|
|
||||||
var name = actor.Utf8Name;
|
|
||||||
if (name.Length > 0 && actor.Pointer->HomeWorld is > 0 and < ushort.MaxValue)
|
|
||||||
return new PlayerIdentifier(actor.Utf8Name, actor.Pointer->HomeWorld);
|
|
||||||
|
|
||||||
return IIdentifier.Invalid;
|
|
||||||
}
|
|
||||||
case ObjectKind.BattleNpc:
|
|
||||||
{
|
|
||||||
var ownerId = actor.Pointer->GameObject.OwnerID;
|
|
||||||
if (ownerId != 0xE0000000)
|
|
||||||
{
|
|
||||||
var owner = (Actor)Dalamud.Objects.SearchById(ownerId)?.Address;
|
|
||||||
if (!owner)
|
|
||||||
return new InvalidIdentifier();
|
|
||||||
|
|
||||||
return new OwnedIdentifier(actor.Utf8Name, owner.Utf8Name, owner.Pointer->HomeWorld,
|
|
||||||
actor.Pointer->GameObject.DataID, ObjectKind.BattleNpc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new NpcIdentifier(actor.Utf8Name, actor.Pointer->GameObject.ObjectIndex,
|
|
||||||
actor.Pointer->GameObject.DataID);
|
|
||||||
}
|
|
||||||
case ObjectKind.Retainer:
|
|
||||||
case ObjectKind.EventNpc:
|
|
||||||
return new NpcIdentifier(actor.Utf8Name, actor.Pointer->GameObject.ObjectIndex,
|
|
||||||
actor.Pointer->GameObject.DataID);
|
|
||||||
case ObjectKind.MountType:
|
|
||||||
case ObjectKind.Companion:
|
|
||||||
{
|
|
||||||
var idx = actor.Pointer->GameObject.ObjectIndex;
|
|
||||||
if (idx % 2 == 0)
|
|
||||||
return new InvalidIdentifier();
|
|
||||||
|
|
||||||
var owner = (Actor)Dalamud.Objects[idx - 1]?.Address;
|
|
||||||
if (!owner)
|
|
||||||
return new InvalidIdentifier();
|
|
||||||
|
|
||||||
var dataId = actor.ObjectKind switch
|
|
||||||
{
|
|
||||||
ObjectKind.MountType => owner.UsedMountId,
|
|
||||||
ObjectKind.Companion => actor.CompanionId,
|
|
||||||
_ => actor.Pointer->GameObject.DataID,
|
|
||||||
};
|
|
||||||
|
|
||||||
var name = actor.Utf8Name;
|
|
||||||
if (name.IsEmpty && dataId == 0)
|
|
||||||
return new InvalidIdentifier();
|
|
||||||
|
|
||||||
return new OwnedIdentifier(name, owner.Utf8Name, owner.Pointer->HomeWorld,
|
|
||||||
dataId, actor.ObjectKind);
|
|
||||||
}
|
|
||||||
default: return new InvalidIdentifier();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
using Penumbra.GameData.ByteString;
|
using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
using Penumbra.String;
|
||||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||||
|
|
||||||
namespace Glamourer.Interop;
|
namespace Glamourer.Interop;
|
||||||
|
|
@ -23,10 +25,10 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
|
||||||
public static implicit operator IntPtr(Actor actor)
|
public static implicit operator IntPtr(Actor actor)
|
||||||
=> actor.Pointer == null ? IntPtr.Zero : (IntPtr)actor.Pointer;
|
=> actor.Pointer == null ? IntPtr.Zero : (IntPtr)actor.Pointer;
|
||||||
|
|
||||||
public IIdentifier GetIdentifier()
|
public ActorIdentifier GetIdentifier()
|
||||||
=> CreateIdentifier(this);
|
=> Glamourer.Actors.FromObject((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Pointer);
|
||||||
|
|
||||||
public bool Identifier(out IIdentifier ident)
|
public bool Identifier(out ActorIdentifier ident)
|
||||||
{
|
{
|
||||||
if (Valid)
|
if (Valid)
|
||||||
{
|
{
|
||||||
|
|
@ -34,7 +36,7 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ident = IIdentifier.Invalid;
|
ident = ActorIdentifier.Invalid;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +55,7 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
|
||||||
set => Pointer->GameObject.ObjectKind = (byte)value;
|
set => Pointer->GameObject.ObjectKind = (byte)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Utf8String Utf8Name
|
public ByteString Utf8Name
|
||||||
=> new(Pointer->GameObject.Name);
|
=> new(Pointer->GameObject.Name);
|
||||||
|
|
||||||
public byte Job
|
public byte Job
|
||||||
|
|
@ -123,6 +125,8 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsWet { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public void SetModelId(int value)
|
public void SetModelId(int value)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ public unsafe partial struct DrawObject : IEquatable<DrawObject>, IDesignable
|
||||||
public uint ModelId
|
public uint ModelId
|
||||||
=> 0;
|
=> 0;
|
||||||
|
|
||||||
|
public bool IsWet
|
||||||
|
=> false;
|
||||||
|
|
||||||
public uint Type
|
public uint Type
|
||||||
=> (*(delegate* unmanaged<Human*, uint>**)Pointer)[50](Pointer);
|
=> (*(delegate* unmanaged<Human*, uint>**)Pointer)[50](Pointer);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,5 @@ public interface IDesignable
|
||||||
public CharacterWeapon OffHand { get; }
|
public CharacterWeapon OffHand { get; }
|
||||||
public bool VisorEnabled { get; }
|
public bool VisorEnabled { get; }
|
||||||
public bool WeaponEnabled { get; }
|
public bool WeaponEnabled { get; }
|
||||||
|
public bool IsWet { get; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
using static Glamourer.Interop.Actor;
|
using static Glamourer.Interop.Actor;
|
||||||
|
|
||||||
namespace Glamourer.Interop;
|
namespace Glamourer.Interop;
|
||||||
|
|
@ -42,67 +43,29 @@ public static class ObjectManager
|
||||||
public static bool IsInGPose { get; private set; }
|
public static bool IsInGPose { get; private set; }
|
||||||
public static ushort World { get; private set; }
|
public static ushort World { get; private set; }
|
||||||
|
|
||||||
public static IReadOnlyDictionary<IIdentifier, ActorData> Actors
|
public static IReadOnlyDictionary<ActorIdentifier, ActorData> Actors
|
||||||
=> Identifiers;
|
=> Identifiers;
|
||||||
|
|
||||||
public static IReadOnlyList<(IIdentifier, ActorData)> List
|
public static IReadOnlyList<(ActorIdentifier, ActorData)> List
|
||||||
=> ListData;
|
=> ListData;
|
||||||
|
|
||||||
private static readonly Dictionary<IIdentifier, ActorData> Identifiers = new(200);
|
private static readonly Dictionary<ActorIdentifier, ActorData> Identifiers = new(200);
|
||||||
private static readonly List<(IIdentifier, ActorData)> ListData = new(Dalamud.Objects.Length);
|
private static readonly List<(ActorIdentifier, ActorData)> ListData = new(Dalamud.Objects.Length);
|
||||||
|
|
||||||
private static void HandleIdentifier(IIdentifier identifier, Actor character)
|
private static void HandleIdentifier(ActorIdentifier identifier, Actor character)
|
||||||
{
|
{
|
||||||
if (!character.DrawObject)
|
if (!character.DrawObject || !identifier.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (identifier)
|
if (!Identifiers.TryGetValue(identifier, out var data))
|
||||||
{
|
{
|
||||||
case PlayerIdentifier p:
|
data = new ActorData(character, identifier.ToString());
|
||||||
if (!Identifiers.TryGetValue(p, out var data))
|
Identifiers[identifier] = data;
|
||||||
{
|
ListData.Add((identifier, data));
|
||||||
data = new ActorData(character,
|
}
|
||||||
World != p.HomeWorld
|
else
|
||||||
? $"{p.Name} ({Dalamud.GameData.GetExcelSheet<World>()!.GetRow(p.HomeWorld)!.Name})"
|
{
|
||||||
: p.Name.ToString());
|
data.Objects.Add(character);
|
||||||
Identifiers[p] = data;
|
|
||||||
ListData.Add((p, data));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data.Objects.Add(character);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case NpcIdentifier n when !n.Name.IsEmpty:
|
|
||||||
if (!Identifiers.TryGetValue(n, out data))
|
|
||||||
{
|
|
||||||
data = new ActorData(character, $"{n.Name} (at {n.ObjectIndex})");
|
|
||||||
Identifiers[n] = data;
|
|
||||||
ListData.Add((n, data));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data.Objects.Add(character);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case OwnedIdentifier o:
|
|
||||||
if (!Identifiers.TryGetValue(o, out data))
|
|
||||||
{
|
|
||||||
data = new ActorData(character,
|
|
||||||
World != o.OwnerHomeWorld
|
|
||||||
? $"{o.OwnerName}s {o.Name} ({Dalamud.GameData.GetExcelSheet<World>()!.GetRow(o.OwnerHomeWorld)!.Name})"
|
|
||||||
: $"{o.OwnerName}s {o.Name}");
|
|
||||||
Identifiers[o] = data;
|
|
||||||
ListData.Add((o, data));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data.Objects.Add(character);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,212 +1,305 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
|
using Glamourer.Interop;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
|
||||||
|
|
||||||
namespace Glamourer.Saves;
|
namespace Glamourer.Saves;
|
||||||
|
|
||||||
public class EquipmentDesign
|
public partial class Design
|
||||||
{
|
{
|
||||||
private Data _data = default;
|
public const int CurrentVersion = 1;
|
||||||
|
|
||||||
// @formatter:off
|
public FileInfo Identifier { get; private set; } = new(string.Empty);
|
||||||
//public Slot Head => new(ref _data, 0);
|
public string Name { get; private set; } = "New Design";
|
||||||
//public Slot Body => new(ref _data, 1);
|
public string Description { get; private set; } = string.Empty;
|
||||||
//public Slot Hands => new(ref _data, 2);
|
|
||||||
//public Slot Legs => new(ref _data, 3);
|
|
||||||
//public Slot Feet => new(ref _data, 4);
|
|
||||||
//public Slot Ears => new(ref _data, 5);
|
|
||||||
//public Slot Neck => new(ref _data, 6);
|
|
||||||
//public Slot Wrist => new(ref _data, 7);
|
|
||||||
//public Slot RFinger => new(ref _data, 8);
|
|
||||||
//public Slot LFinger => new(ref _data, 9);
|
|
||||||
//public Slot MainHand => new(ref _data, 10);
|
|
||||||
//public Slot OffHand => new(ref _data, 11);
|
|
||||||
//// @formatter:on
|
|
||||||
//
|
|
||||||
//public Slot this[EquipSlot slot]
|
|
||||||
// => new(ref _data, (int)slot.ToIndex());
|
|
||||||
//
|
|
||||||
//public Slot this[int idx]
|
|
||||||
// => idx is >= 0 and < Data.NumEquipment ? new Slot(ref _data, idx) : throw new IndexOutOfRangeException();
|
|
||||||
|
|
||||||
public unsafe struct Data
|
public DateTimeOffset CreationDate { get; private init; } = DateTimeOffset.UtcNow;
|
||||||
|
public DateTimeOffset LastUpdateDate { get; private set; } = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
private DesignFlagsV1 _flags;
|
||||||
|
|
||||||
|
public bool VisorState
|
||||||
{
|
{
|
||||||
public const int NumEquipment = 12;
|
get => _flags.HasFlag(DesignFlagsV1.VisorState);
|
||||||
|
private set => _flags = value ? _flags | DesignFlagsV1.VisorState : _flags & ~DesignFlagsV1.VisorState;
|
||||||
public fixed uint Ids[NumEquipment];
|
|
||||||
public fixed byte Stains[NumEquipment];
|
|
||||||
public ushort Flags;
|
|
||||||
public ushort StainFlags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//public ref struct Slot
|
public bool VisorApply
|
||||||
//{
|
|
||||||
// private readonly ref Data _data;
|
|
||||||
// private readonly int _index;
|
|
||||||
// private readonly ushort _flag;
|
|
||||||
//
|
|
||||||
// public Slot(ref Data data, int idx)
|
|
||||||
// {
|
|
||||||
// _data = data;
|
|
||||||
// _index = idx;
|
|
||||||
// _flag = (ushort)(1 << idx);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public unsafe uint ItemId
|
|
||||||
// {
|
|
||||||
// get => _data.Ids[_index];
|
|
||||||
// set => _data.Ids[_index] = value;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public unsafe StainId StainId
|
|
||||||
// {
|
|
||||||
// get => _data.Stains[_index];
|
|
||||||
// set => _data.Stains[_index] = value.Value;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public bool ApplyItem
|
|
||||||
// {
|
|
||||||
// get => (_data.Flags & _flag) != 0;
|
|
||||||
// set => _data.Flags = (ushort)(value ? _data.Flags | _flag : _data.Flags & ~_flag);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public bool ApplyStain
|
|
||||||
// {
|
|
||||||
// get => (_data.StainFlags & _flag) != 0;
|
|
||||||
// set => _data.StainFlags = (ushort)(value ? _data.StainFlags | _flag : _data.StainFlags & ~_flag);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class HumanDesign
|
|
||||||
{
|
|
||||||
public unsafe struct Data
|
|
||||||
{
|
{
|
||||||
public CustomizeData Values;
|
get => _flags.HasFlag(DesignFlagsV1.VisorApply);
|
||||||
public CustomizeFlag Flag;
|
private set => _flags = value ? _flags | DesignFlagsV1.VisorApply : _flags & ~DesignFlagsV1.VisorApply;
|
||||||
}
|
}
|
||||||
|
|
||||||
//public ref struct Choice<T> where T : unmanaged
|
public bool WeaponStateShown
|
||||||
//{
|
|
||||||
// private readonly ref Data _data;
|
|
||||||
// private readonly CustomizeFlag _flag;
|
|
||||||
//
|
|
||||||
// public Choice(ref Data data, CustomizeFlag flag)
|
|
||||||
// {
|
|
||||||
// _data = data;
|
|
||||||
// _flag = flag;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public bool ApplyChoice
|
|
||||||
// {
|
|
||||||
// get => _data.Flag.HasFlag(_flag);
|
|
||||||
// set => _data.Flag = value ? _data.Flag | _flag : _data.Flag & ~_flag;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Design
|
|
||||||
{
|
|
||||||
public string Name { get; private set; }
|
|
||||||
public string Description { get; private set; }
|
|
||||||
|
|
||||||
public DateTimeOffset CreationDate { get; }
|
|
||||||
public DateTimeOffset LastUpdateDate { get; private set; }
|
|
||||||
|
|
||||||
public bool ReadOnly { get; private set; }
|
|
||||||
|
|
||||||
public EquipmentDesign? Equipment;
|
|
||||||
public HumanDesign? Customize;
|
|
||||||
|
|
||||||
public string WriteJson()
|
|
||||||
{
|
{
|
||||||
return string.Empty;
|
get => _flags.HasFlag(DesignFlagsV1.WeaponStateShown);
|
||||||
|
private set => _flags = value ? _flags | DesignFlagsV1.WeaponStateShown : _flags & ~DesignFlagsV1.WeaponStateShown;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public bool WeaponStateApply
|
||||||
public struct DesignSaveV1
|
{
|
||||||
{
|
get => _flags.HasFlag(DesignFlagsV1.WeaponStateApply);
|
||||||
public string Name;
|
private set => _flags = value ? _flags | DesignFlagsV1.WeaponStateApply : _flags & ~DesignFlagsV1.WeaponStateApply;
|
||||||
public string Description;
|
}
|
||||||
|
|
||||||
public ulong CreationDate;
|
public bool WetnessState
|
||||||
public ulong LastUpdateDate;
|
{
|
||||||
|
get => _flags.HasFlag(DesignFlagsV1.WetnessState);
|
||||||
public EquipmentPiece Head;
|
private set => _flags = value ? _flags | DesignFlagsV1.WetnessState : _flags & ~DesignFlagsV1.WetnessState;
|
||||||
public EquipmentPiece Body;
|
}
|
||||||
public EquipmentPiece Hands;
|
|
||||||
public EquipmentPiece Legs;
|
public bool WetnessApply
|
||||||
public EquipmentPiece Feet;
|
{
|
||||||
public EquipmentPiece Ears;
|
get => _flags.HasFlag(DesignFlagsV1.WetnessApply);
|
||||||
public EquipmentPiece Neck;
|
private set => _flags = value ? _flags | DesignFlagsV1.WetnessApply : _flags & ~DesignFlagsV1.WetnessApply;
|
||||||
public EquipmentPiece Wrists;
|
}
|
||||||
public EquipmentPiece LFinger;
|
|
||||||
public EquipmentPiece RFinger;
|
public bool ReadOnly
|
||||||
|
{
|
||||||
public EquipmentPiece MainHand;
|
get => _flags.HasFlag(DesignFlagsV1.ReadOnly);
|
||||||
public EquipmentPiece OffHand;
|
private set => _flags = value ? _flags | DesignFlagsV1.ReadOnly : _flags & ~DesignFlagsV1.ReadOnly;
|
||||||
|
}
|
||||||
public CustomizationChoice<uint> ModelId;
|
|
||||||
public CustomizationChoice<Race> Race;
|
private static bool FromDesignable(string identifier, string name, IDesignable data, [NotNullWhen(true)] out Design? design,
|
||||||
public CustomizationChoice<Gender> Gender;
|
bool doWeapons = true, bool doFlags = true, bool doEquipment = true, bool doCustomize = true)
|
||||||
public CustomizationChoice BodyType;
|
{
|
||||||
public CustomizationChoice Height;
|
if (!data.Valid)
|
||||||
public CustomizationChoice<SubRace> Clan;
|
{
|
||||||
public CustomizationChoice Face;
|
design = null;
|
||||||
public CustomizationChoice Hairstyle;
|
return false;
|
||||||
public CustomizationChoice<bool> Highlights;
|
}
|
||||||
public CustomizationChoice SkinColor;
|
|
||||||
public CustomizationChoice EyeColorRight;
|
design = new Design
|
||||||
public CustomizationChoice HairColor;
|
{
|
||||||
public CustomizationChoice HighlightsColor;
|
Identifier = new FileInfo(identifier),
|
||||||
public CustomizationChoice<bool> FacialFeature1;
|
Name = name,
|
||||||
public CustomizationChoice<bool> FacialFeature2;
|
Description = string.Empty,
|
||||||
public CustomizationChoice<bool> FacialFeature3;
|
CreationDate = DateTimeOffset.UtcNow,
|
||||||
public CustomizationChoice<bool> FacialFeature4;
|
LastUpdateDate = DateTimeOffset.UtcNow,
|
||||||
public CustomizationChoice<bool> FacialFeature5;
|
ReadOnly = false,
|
||||||
public CustomizationChoice<bool> FacialFeature6;
|
VisorApply = doFlags,
|
||||||
public CustomizationChoice<bool> FacialFeature7;
|
WeaponStateApply = doFlags,
|
||||||
public CustomizationChoice<bool> LegacyTattoo;
|
WetnessApply = doFlags,
|
||||||
public CustomizationChoice TattooColor;
|
VisorState = data.VisorEnabled,
|
||||||
public CustomizationChoice Eyebrows;
|
WeaponStateShown = data.WeaponEnabled,
|
||||||
public CustomizationChoice EyeColorLeft;
|
WetnessState = data.IsWet,
|
||||||
public CustomizationChoice EyeShape;
|
};
|
||||||
public CustomizationChoice<bool> SmallIris;
|
|
||||||
public CustomizationChoice Nose;
|
if (doEquipment)
|
||||||
public CustomizationChoice Jaw;
|
{
|
||||||
public CustomizationChoice Mouth;
|
var equipment = data.Equip;
|
||||||
public CustomizationChoice<bool> Lipstick;
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
public CustomizationChoice MuscleMass;
|
{
|
||||||
public CustomizationChoice TailShape;
|
var s = design[slot];
|
||||||
public CustomizationChoice BustSize;
|
var e = equipment[slot];
|
||||||
public CustomizationChoice FacePaint;
|
s.StainId = e.Stain;
|
||||||
public CustomizationChoice<bool> FacePaintReversed;
|
s.ApplyStain = true;
|
||||||
public CustomizationChoice FacePaintColor;
|
s.ItemId = Glamourer.Identifier.Identify(e.Set, e.Variant, slot).FirstOrDefault()?.RowId ?? 0;
|
||||||
|
s.ApplyItem = s.ItemId != 0;
|
||||||
public bool ReadOnly;
|
}
|
||||||
|
}
|
||||||
public override string ToString()
|
|
||||||
=> Name;
|
if (doWeapons)
|
||||||
}
|
{
|
||||||
|
var m = design.MainHand;
|
||||||
public struct EquipmentPiece
|
var d = data.MainHand;
|
||||||
{
|
|
||||||
public uint Item;
|
m.StainId = d.Stain;
|
||||||
public bool ApplyItem;
|
m.ApplyStain = true;
|
||||||
public StainId Stain;
|
m.ItemId = Glamourer.Identifier.Identify(d.Set, d.Type, d.Variant, EquipSlot.MainHand).FirstOrDefault()?.RowId ?? 0;
|
||||||
public bool ApplyStain;
|
m.ApplyItem = m.ItemId != 0;
|
||||||
}
|
|
||||||
|
var o = design.OffHand;
|
||||||
public struct CustomizationChoice
|
d = data.OffHand;
|
||||||
{
|
o.StainId = d.Stain;
|
||||||
public CustomizeValue Value;
|
o.ApplyStain = true;
|
||||||
public bool Apply;
|
o.ItemId = Glamourer.Identifier.Identify(d.Set, d.Type, d.Variant, EquipSlot.MainHand).FirstOrDefault()?.RowId ?? 0;
|
||||||
}
|
o.ApplyItem = o.ItemId != 0;
|
||||||
|
}
|
||||||
public struct CustomizationChoice<T> where T : struct
|
|
||||||
{
|
if (doCustomize)
|
||||||
public T Value;
|
{
|
||||||
public bool Apply;
|
var customize = data.Customize;
|
||||||
|
design.CustomizeFlags = Glamourer.Customization.GetList(customize.Clan, customize.Gender).SettingAvailable
|
||||||
|
| CustomizeFlag.Gender
|
||||||
|
| CustomizeFlag.Race
|
||||||
|
| CustomizeFlag.Clan;
|
||||||
|
foreach (var c in Enum.GetValues<CustomizeIndex>())
|
||||||
|
{
|
||||||
|
if (!design.CustomizeFlags.HasFlag(c.ToFlag()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var choice = design[c];
|
||||||
|
choice.Value = customize[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var file = File.Open(Identifier.FullName, File.Exists(Identifier.FullName) ? FileMode.Truncate : FileMode.CreateNew);
|
||||||
|
WriteJson(file);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Glamourer.Log.Error($"Could not save design {Identifier.Name}:\n{ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteJson(Stream s, Formatting formatting = Formatting.Indented)
|
||||||
|
{
|
||||||
|
var obj = new JObject();
|
||||||
|
obj["Version"] = CurrentVersion;
|
||||||
|
obj[nameof(Name)] = Name;
|
||||||
|
obj[nameof(Description)] = Description;
|
||||||
|
obj[nameof(CreationDate)] = CreationDate.ToUnixTimeSeconds();
|
||||||
|
obj[nameof(LastUpdateDate)] = LastUpdateDate.ToUnixTimeSeconds();
|
||||||
|
obj[nameof(ReadOnly)] = ReadOnly;
|
||||||
|
WriteEquipment(obj);
|
||||||
|
WriteCustomization(obj);
|
||||||
|
WriteFlags(obj);
|
||||||
|
|
||||||
|
using var t = new StreamWriter(s);
|
||||||
|
using var j = new JsonTextWriter(t) { Formatting = formatting };
|
||||||
|
obj.WriteTo(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteFlags(JObject obj)
|
||||||
|
{
|
||||||
|
obj[nameof(VisorState)] = VisorState;
|
||||||
|
obj[nameof(VisorApply)] = VisorApply;
|
||||||
|
obj[nameof(WeaponStateShown)] = WeaponStateShown;
|
||||||
|
obj[nameof(WeaponStateApply)] = WeaponStateApply;
|
||||||
|
obj[nameof(WetnessState)] = WetnessState;
|
||||||
|
obj[nameof(WetnessApply)] = WetnessApply;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Load(string fileName, [NotNullWhen(true)] out Design? design)
|
||||||
|
{
|
||||||
|
design = null;
|
||||||
|
if (!File.Exists(fileName))
|
||||||
|
{
|
||||||
|
Glamourer.Log.Error($"Could not load design {fileName}:\nFile does not exist.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = File.ReadAllText(fileName);
|
||||||
|
var obj = JObject.Parse(data);
|
||||||
|
|
||||||
|
return obj["Version"]?.Value<int>() switch
|
||||||
|
{
|
||||||
|
null => NoVersion(fileName),
|
||||||
|
1 => LoadV1(fileName, obj, out design),
|
||||||
|
_ => UnknownVersion(fileName, obj["Version"]!.Value<int>()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Glamourer.Log.Error($"Could not load design {fileName}:\n{e}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool NoVersion(string fileName)
|
||||||
|
{
|
||||||
|
Glamourer.Log.Error($"Could not load design {fileName}:\nNo version available.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool UnknownVersion(string fileName, int version)
|
||||||
|
{
|
||||||
|
Glamourer.Log.Error($"Could not load design {fileName}:\nThe version {version} can not be handled.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool LoadV1(string fileName, JObject obj, [NotNullWhen(true)] out Design? design)
|
||||||
|
{
|
||||||
|
design = new Design
|
||||||
|
{
|
||||||
|
Identifier = new FileInfo(fileName),
|
||||||
|
Name = obj[nameof(Name)]?.Value<string>() ?? "New Design",
|
||||||
|
Description = obj[nameof(Description)]?.Value<string>() ?? string.Empty,
|
||||||
|
CreationDate = GetDateTime(obj[nameof(CreationDate)]?.Value<long>()),
|
||||||
|
LastUpdateDate = GetDateTime(obj[nameof(LastUpdateDate)]?.Value<long>()),
|
||||||
|
ReadOnly = obj[nameof(ReadOnly)]?.Value<bool>() ?? false,
|
||||||
|
VisorState = obj[nameof(VisorState)]?.Value<bool>() ?? false,
|
||||||
|
VisorApply = obj[nameof(VisorApply)]?.Value<bool>() ?? false,
|
||||||
|
WeaponStateShown = obj[nameof(WeaponStateShown)]?.Value<bool>() ?? false,
|
||||||
|
WeaponStateApply = obj[nameof(WeaponStateApply)]?.Value<bool>() ?? false,
|
||||||
|
WetnessState = obj[nameof(WetnessState)]?.Value<bool>() ?? false,
|
||||||
|
WetnessApply = obj[nameof(WetnessApply)]?.Value<bool>() ?? false,
|
||||||
|
};
|
||||||
|
|
||||||
|
var equipment = obj[nameof(Equipment)];
|
||||||
|
if (equipment == null)
|
||||||
|
{
|
||||||
|
design.EquipmentFlags = 0;
|
||||||
|
design.StainFlags = 0;
|
||||||
|
design._equipmentData = default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var slot in design.Equipment)
|
||||||
|
{
|
||||||
|
var s = equipment[SlotName[slot.Index]];
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
slot.ItemId = 0;
|
||||||
|
slot.ApplyItem = false;
|
||||||
|
slot.ApplyStain = false;
|
||||||
|
slot.StainId = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slot.ItemId = s[nameof(Slot.ItemId)]?.Value<uint>() ?? 0u;
|
||||||
|
slot.ApplyItem = obj[nameof(Slot.ApplyItem)]?.Value<bool>() ?? false;
|
||||||
|
slot.StainId = new StainId(s[nameof(Slot.StainId)]?.Value<byte>() ?? 0);
|
||||||
|
slot.ApplyStain = obj[nameof(Slot.ApplyStain)]?.Value<bool>() ?? false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var customize = obj[nameof(Customization)];
|
||||||
|
if (customize == null)
|
||||||
|
{
|
||||||
|
design.CustomizeFlags = 0;
|
||||||
|
design._customizeData = Customize.Default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var choice in design.Customization)
|
||||||
|
{
|
||||||
|
var c = customize[choice.Index.ToDefaultName()];
|
||||||
|
if (c == null)
|
||||||
|
{
|
||||||
|
choice.Value = Customize.Default.Get(choice.Index);
|
||||||
|
choice.Apply = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
choice.Value = new CustomizeValue(c[nameof(Choice.Value)]?.Value<byte>() ?? Customize.Default.Get(choice.Index).Value);
|
||||||
|
choice.Apply = c[nameof(Choice.Apply)]?.Value<bool>() ?? false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DateTimeOffset GetDateTime(long? value)
|
||||||
|
=> value == null ? DateTimeOffset.UtcNow : DateTimeOffset.FromUnixTimeSeconds(value.Value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
Glamourer/Saves/DesignFlagsV1.cs
Normal file
15
Glamourer/Saves/DesignFlagsV1.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Glamourer.Saves;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum DesignFlagsV1 : byte
|
||||||
|
{
|
||||||
|
VisorState = 0x01,
|
||||||
|
VisorApply = 0x02,
|
||||||
|
WeaponStateShown = 0x04,
|
||||||
|
WeaponStateApply = 0x08,
|
||||||
|
WetnessState = 0x10,
|
||||||
|
WetnessApply = 0x20,
|
||||||
|
ReadOnly = 0x40,
|
||||||
|
}
|
||||||
115
Glamourer/Saves/EquipmentDesign.cs
Normal file
115
Glamourer/Saves/EquipmentDesign.cs
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
namespace Glamourer.Saves;
|
||||||
|
|
||||||
|
public partial class Design
|
||||||
|
{
|
||||||
|
private unsafe struct EquipmentData
|
||||||
|
{
|
||||||
|
public const int NumEquipment = 12;
|
||||||
|
public fixed uint Ids[NumEquipment];
|
||||||
|
public fixed byte Stains[NumEquipment];
|
||||||
|
}
|
||||||
|
|
||||||
|
private EquipmentData _equipmentData = default;
|
||||||
|
public ushort EquipmentFlags { get; private set; }
|
||||||
|
public ushort StainFlags { get; private set; }
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
public Slot Head => new(this, 0);
|
||||||
|
public Slot Body => new(this, 1);
|
||||||
|
public Slot Hands => new(this, 2);
|
||||||
|
public Slot Legs => new(this, 3);
|
||||||
|
public Slot Feet => new(this, 4);
|
||||||
|
public Slot Ears => new(this, 5);
|
||||||
|
public Slot Neck => new(this, 6);
|
||||||
|
public Slot Wrists => new(this, 7);
|
||||||
|
public Slot RFinger => new(this, 8);
|
||||||
|
public Slot LFinger => new(this, 9);
|
||||||
|
public Slot MainHand => new(this, 10);
|
||||||
|
public Slot OffHand => new(this, 11);
|
||||||
|
// @formatter:on
|
||||||
|
|
||||||
|
public Slot this[EquipSlot slot]
|
||||||
|
=> new(this, (int)slot.ToIndex());
|
||||||
|
|
||||||
|
|
||||||
|
public static readonly string[] SlotName =
|
||||||
|
{
|
||||||
|
EquipSlot.Head.ToName(),
|
||||||
|
EquipSlot.Body.ToName(),
|
||||||
|
EquipSlot.Hands.ToName(),
|
||||||
|
EquipSlot.Legs.ToName(),
|
||||||
|
EquipSlot.Feet.ToName(),
|
||||||
|
EquipSlot.Ears.ToName(),
|
||||||
|
EquipSlot.Neck.ToName(),
|
||||||
|
EquipSlot.Wrists.ToName(),
|
||||||
|
EquipSlot.RFinger.ToName(),
|
||||||
|
EquipSlot.LFinger.ToName(),
|
||||||
|
EquipSlot.MainHand.ToName(),
|
||||||
|
EquipSlot.OffHand.ToName(),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public readonly unsafe struct Slot
|
||||||
|
{
|
||||||
|
private readonly Design _data;
|
||||||
|
public readonly int Index;
|
||||||
|
public readonly ushort Flag;
|
||||||
|
|
||||||
|
public Slot(Design design, int idx)
|
||||||
|
{
|
||||||
|
_data = design;
|
||||||
|
Index = idx;
|
||||||
|
Flag = (ushort)(1 << idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint ItemId
|
||||||
|
{
|
||||||
|
get => _data._equipmentData.Ids[Index];
|
||||||
|
set => _data._equipmentData.Ids[Index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StainId StainId
|
||||||
|
{
|
||||||
|
get => _data._equipmentData.Stains[Index];
|
||||||
|
set => _data._equipmentData.Stains[Index] = value.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApplyItem
|
||||||
|
{
|
||||||
|
get => (_data.EquipmentFlags & Flag) != 0;
|
||||||
|
set => _data.EquipmentFlags = (ushort)(value ? _data.EquipmentFlags | Flag : _data.EquipmentFlags & ~Flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApplyStain
|
||||||
|
{
|
||||||
|
get => (_data.StainFlags & Flag) != 0;
|
||||||
|
set => _data.StainFlags = (ushort)(value ? _data.StainFlags | Flag : _data.StainFlags & ~Flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Slot> Equipment
|
||||||
|
=> Enumerable.Range(0, EquipmentData.NumEquipment).Select(i => new Slot(this, i));
|
||||||
|
|
||||||
|
private void WriteEquipment(JObject obj)
|
||||||
|
{
|
||||||
|
var tok = new JObject();
|
||||||
|
foreach (var slot in Equipment)
|
||||||
|
{
|
||||||
|
tok[SlotName] = new JObject
|
||||||
|
{
|
||||||
|
[nameof(Slot.ItemId)] = slot.ItemId,
|
||||||
|
[nameof(Slot.ApplyItem)] = slot.ApplyItem,
|
||||||
|
[nameof(Slot.StainId)] = slot.StainId.Value,
|
||||||
|
[nameof(Slot.ApplyStain)] = slot.ApplyStain,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[nameof(Equipment)] = tok;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
Glamourer/Saves/HumanDesign.cs
Normal file
67
Glamourer/Saves/HumanDesign.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Glamourer.Customization;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||||
|
|
||||||
|
namespace Glamourer.Saves;
|
||||||
|
|
||||||
|
public partial class Design
|
||||||
|
{
|
||||||
|
private CustomizeData _customizeData;
|
||||||
|
public CustomizeFlag CustomizeFlags { get; private set; }
|
||||||
|
|
||||||
|
public Choice this[CustomizeIndex index]
|
||||||
|
=> new(this, index);
|
||||||
|
|
||||||
|
public unsafe Customize Customize
|
||||||
|
=> new((CustomizeData*)Unsafe.AsPointer(ref _customizeData));
|
||||||
|
|
||||||
|
public readonly struct Choice
|
||||||
|
{
|
||||||
|
private readonly Design _data;
|
||||||
|
private readonly CustomizeFlag _flag;
|
||||||
|
private readonly CustomizeIndex _index;
|
||||||
|
|
||||||
|
public Choice(Design design, CustomizeIndex index)
|
||||||
|
{
|
||||||
|
_data = design;
|
||||||
|
_index = index;
|
||||||
|
_flag = index.ToFlag();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomizeValue Value
|
||||||
|
{
|
||||||
|
get => _data._customizeData.Get(_index);
|
||||||
|
set => _data._customizeData.Set(_index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Apply
|
||||||
|
{
|
||||||
|
get => _data.CustomizeFlags.HasFlag(_flag);
|
||||||
|
set => _data.CustomizeFlags = value ? _data.CustomizeFlags | _flag : _data.CustomizeFlags & ~_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomizeIndex Index
|
||||||
|
=> _index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Choice> Customization
|
||||||
|
=> Enum.GetValues<CustomizeIndex>().Select(index => new Choice(this, index));
|
||||||
|
|
||||||
|
|
||||||
|
public IEnumerable<Choice> ActiveCustomizations
|
||||||
|
=> Customization.Where(c => c.Apply);
|
||||||
|
|
||||||
|
private void WriteCustomization(JObject obj)
|
||||||
|
{
|
||||||
|
var tok = new JObject();
|
||||||
|
foreach (var choice in Customization)
|
||||||
|
tok[choice.Index.ToString()] = choice.Value.Value;
|
||||||
|
|
||||||
|
obj[nameof(Customization)] = tok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Glamourer.State;
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum ApplicationFlags : uint
|
|
||||||
{
|
|
||||||
Customizations = 0x000001,
|
|
||||||
MainHand = 0x000002,
|
|
||||||
OffHand = 0x000004,
|
|
||||||
Head = 0x000008,
|
|
||||||
Body = 0x000010,
|
|
||||||
Hands = 0x000020,
|
|
||||||
Legs = 0x000040,
|
|
||||||
Feet = 0x000080,
|
|
||||||
Ears = 0x000100,
|
|
||||||
Neck = 0x000200,
|
|
||||||
Wrist = 0x000400,
|
|
||||||
RFinger = 0x000800,
|
|
||||||
LFinger = 0x001000,
|
|
||||||
SetVisor = 0x002000,
|
|
||||||
Visor = 0x004000,
|
|
||||||
SetWeapon = 0x008000,
|
|
||||||
Weapon = 0x010000,
|
|
||||||
SetWet = 0x020000,
|
|
||||||
Wet = 0x040000,
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ApplicationFlagExtensions
|
|
||||||
{
|
|
||||||
public static ApplicationFlags ToApplicationFlag(this EquipSlot slot)
|
|
||||||
=> slot switch
|
|
||||||
{
|
|
||||||
EquipSlot.MainHand => ApplicationFlags.MainHand,
|
|
||||||
EquipSlot.OffHand => ApplicationFlags.OffHand,
|
|
||||||
EquipSlot.Head => ApplicationFlags.Head,
|
|
||||||
EquipSlot.Body => ApplicationFlags.Body,
|
|
||||||
EquipSlot.Hands => ApplicationFlags.Hands,
|
|
||||||
EquipSlot.Legs => ApplicationFlags.Legs,
|
|
||||||
EquipSlot.Feet => ApplicationFlags.Feet,
|
|
||||||
EquipSlot.Ears => ApplicationFlags.Ears,
|
|
||||||
EquipSlot.Neck => ApplicationFlags.Neck,
|
|
||||||
EquipSlot.Wrists => ApplicationFlags.Wrist,
|
|
||||||
EquipSlot.RFinger => ApplicationFlags.RFinger,
|
|
||||||
EquipSlot.BothHand => ApplicationFlags.MainHand | ApplicationFlags.OffHand,
|
|
||||||
EquipSlot.LFinger => ApplicationFlags.LFinger,
|
|
||||||
EquipSlot.HeadBody => ApplicationFlags.Body,
|
|
||||||
EquipSlot.BodyHandsLegsFeet => ApplicationFlags.Body,
|
|
||||||
EquipSlot.LegsFeet => ApplicationFlags.Legs,
|
|
||||||
EquipSlot.FullBody => ApplicationFlags.Body,
|
|
||||||
EquipSlot.BodyHands => ApplicationFlags.Body,
|
|
||||||
EquipSlot.BodyLegsFeet => ApplicationFlags.Body,
|
|
||||||
EquipSlot.ChestHands => ApplicationFlags.Body,
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static EquipSlot ToSlot(this ApplicationFlags flags)
|
|
||||||
=> flags switch
|
|
||||||
{
|
|
||||||
ApplicationFlags.MainHand => EquipSlot.MainHand,
|
|
||||||
ApplicationFlags.OffHand => EquipSlot.OffHand,
|
|
||||||
ApplicationFlags.Head => EquipSlot.Head,
|
|
||||||
ApplicationFlags.Body => EquipSlot.Body,
|
|
||||||
ApplicationFlags.Hands => EquipSlot.Hands,
|
|
||||||
ApplicationFlags.Legs => EquipSlot.Legs,
|
|
||||||
ApplicationFlags.Feet => EquipSlot.Feet,
|
|
||||||
ApplicationFlags.Ears => EquipSlot.Ears,
|
|
||||||
ApplicationFlags.Neck => EquipSlot.Neck,
|
|
||||||
ApplicationFlags.Wrist => EquipSlot.Wrists,
|
|
||||||
ApplicationFlags.RFinger => EquipSlot.RFinger,
|
|
||||||
ApplicationFlags.LFinger => EquipSlot.LFinger,
|
|
||||||
_ => EquipSlot.Unknown,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
|
using ImGuiScene;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Penumbra.GameData.Enums;
|
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
using Penumbra.String.Functions;
|
||||||
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
|
||||||
using Functions = Penumbra.GameData.Util.Functions;
|
|
||||||
|
|
||||||
namespace Glamourer.State;
|
namespace Glamourer.State;
|
||||||
|
|
||||||
|
|
@ -36,7 +36,6 @@ public struct CharacterData
|
||||||
public const byte CurrentVersion = 3;
|
public const byte CurrentVersion = 3;
|
||||||
|
|
||||||
public uint ModelId;
|
public uint ModelId;
|
||||||
public ApplicationFlags Flags;
|
|
||||||
public CustomizeData CustomizeData;
|
public CustomizeData CustomizeData;
|
||||||
public CharacterWeapon MainHand;
|
public CharacterWeapon MainHand;
|
||||||
public CharacterWeapon OffHand;
|
public CharacterWeapon OffHand;
|
||||||
|
|
@ -77,7 +76,6 @@ public struct CharacterData
|
||||||
= new()
|
= new()
|
||||||
{
|
{
|
||||||
ModelId = 0,
|
ModelId = 0,
|
||||||
Flags = 0,
|
|
||||||
CustomizeData = Customize.Default,
|
CustomizeData = Customize.Default,
|
||||||
MainHand = CharacterWeapon.Empty,
|
MainHand = CharacterWeapon.Empty,
|
||||||
OffHand = CharacterWeapon.Empty,
|
OffHand = CharacterWeapon.Empty,
|
||||||
|
|
@ -98,28 +96,12 @@ public struct CharacterData
|
||||||
var data = new CharacterData();
|
var data = new CharacterData();
|
||||||
fixed (void* ptr = &this)
|
fixed (void* ptr = &this)
|
||||||
{
|
{
|
||||||
Functions.MemCpyUnchecked(&data, ptr, sizeof(CharacterData));
|
MemoryUtility.MemCpyUnchecked(&data, ptr, sizeof(CharacterData));
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const ApplicationFlags SaveFlags = ApplicationFlags.Customizations
|
|
||||||
| ApplicationFlags.Head
|
|
||||||
| ApplicationFlags.Body
|
|
||||||
| ApplicationFlags.Hands
|
|
||||||
| ApplicationFlags.Legs
|
|
||||||
| ApplicationFlags.Feet
|
|
||||||
| ApplicationFlags.Ears
|
|
||||||
| ApplicationFlags.Neck
|
|
||||||
| ApplicationFlags.Wrist
|
|
||||||
| ApplicationFlags.RFinger
|
|
||||||
| ApplicationFlags.LFinger
|
|
||||||
| ApplicationFlags.MainHand
|
|
||||||
| ApplicationFlags.OffHand
|
|
||||||
| ApplicationFlags.SetVisor
|
|
||||||
| ApplicationFlags.SetWeapon;
|
|
||||||
|
|
||||||
|
|
||||||
public void Load(IDesignable designable)
|
public void Load(IDesignable designable)
|
||||||
{
|
{
|
||||||
|
|
@ -128,7 +110,6 @@ public struct CharacterData
|
||||||
Equipment.Load(designable.Equip);
|
Equipment.Load(designable.Equip);
|
||||||
MainHand = designable.MainHand;
|
MainHand = designable.MainHand;
|
||||||
OffHand = designable.OffHand;
|
OffHand = designable.OffHand;
|
||||||
Flags = SaveFlags | (designable.VisorEnabled ? ApplicationFlags.Visor : 0) | (designable.WeaponEnabled ? ApplicationFlags.Weapon : 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,15 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
namespace Glamourer.State;
|
namespace Glamourer.State;
|
||||||
|
|
||||||
public class CurrentManipulations : IReadOnlyCollection<KeyValuePair<Actor.IIdentifier, CurrentDesign>>
|
public class CurrentManipulations : IReadOnlyCollection<KeyValuePair<ActorIdentifier, CurrentDesign>>
|
||||||
{
|
{
|
||||||
private readonly Dictionary<Actor.IIdentifier, CurrentDesign> _characterSaves = new();
|
private readonly Dictionary<ActorIdentifier, CurrentDesign> _characterSaves = new();
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<Actor.IIdentifier, CurrentDesign>> GetEnumerator()
|
public IEnumerator<KeyValuePair<ActorIdentifier, CurrentDesign>> GetEnumerator()
|
||||||
=> _characterSaves.GetEnumerator();
|
=> _characterSaves.GetEnumerator();
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
|
@ -32,10 +33,10 @@ public class CurrentManipulations : IReadOnlyCollection<KeyValuePair<Actor.IIden
|
||||||
return save;
|
return save;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteSave(Actor.IIdentifier identifier)
|
public void DeleteSave(ActorIdentifier identifier)
|
||||||
=> _characterSaves.Remove(identifier);
|
=> _characterSaves.Remove(identifier);
|
||||||
|
|
||||||
public bool TryGetDesign(Actor.IIdentifier identifier, [NotNullWhen(true)] out CurrentDesign? save)
|
public bool TryGetDesign(ActorIdentifier identifier, [NotNullWhen(true)] out CurrentDesign? save)
|
||||||
=> _characterSaves.TryGetValue(identifier, out save);
|
=> _characterSaves.TryGetValue(identifier, out save);
|
||||||
|
|
||||||
//public CharacterArmor? ChangeEquip(Actor actor, EquipSlot slot, CharacterArmor data)
|
//public CharacterArmor? ChangeEquip(Actor actor, EquipSlot slot, CharacterArmor data)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
namespace Glamourer.State;
|
namespace Glamourer.State;
|
||||||
|
|
||||||
public class FixedDesigns
|
public class FixedDesigns
|
||||||
{
|
{
|
||||||
public bool TryGetDesign(Actor.IIdentifier actor, [NotNullWhen(true)] out CharacterSave? save)
|
public bool TryGetDesign(ActorIdentifier actor, [NotNullWhen(true)] out CharacterSave? save)
|
||||||
{
|
{
|
||||||
save = null;
|
save = null;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue