Get rid off all MetaManipulation things.

This commit is contained in:
Ottermandias 2024-06-14 13:38:36 +02:00
parent 361082813b
commit 3170edfeb6
63 changed files with 2422 additions and 2847 deletions

View file

@ -0,0 +1,159 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class EqdpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<EqdpIdentifier, EqdpEntryInternal>(editor, metaFiles), IService
{
public override ReadOnlySpan<byte> Label
=> "Racial Model Edits (EQDP)###EQDP"u8;
public override int NumColumns
=> 7;
protected override void Initialize()
{
Identifier = new EqdpIdentifier(1, EquipSlot.Head, GenderRace.MidlanderMale);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = new EqdpEntryInternal(ExpandedEqdpFile.GetDefault(MetaFiles, Identifier), Identifier.Slot);
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current EQDP manipulations to clipboard."u8, MetaDictionary.SerializeTo([], Editor.Eqdp));
ImGui.TableNextColumn();
var validRaceCode = CharacterUtilityData.EqdpIdx(Identifier.GenderRace, false) >= 0;
var canAdd = validRaceCode && !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 :
validRaceCode ? "This entry is already edited."u8 : "This combination of race and gender can not be used."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
UpdateEntry();
DrawEntry(Entry, ref Entry, true);
}
protected override void DrawEntry(EqdpIdentifier identifier, EqdpEntryInternal entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
var defaultEntry = new EqdpEntryInternal(ExpandedEqdpFile.GetDefault(MetaFiles, identifier), identifier.Slot);
if (DrawEntry(defaultEntry, ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(EqdpIdentifier, EqdpEntryInternal)> Enumerate()
=> Editor.Eqdp.Select(kvp => (kvp.Key, kvp.Value));
private static bool DrawIdentifierInput(ref EqdpIdentifier identifier)
{
ImGui.TableNextColumn();
var changes = DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
changes |= DrawRace(ref identifier);
ImGui.TableNextColumn();
changes |= DrawGender(ref identifier);
ImGui.TableNextColumn();
changes |= DrawEquipSlot(ref identifier);
return changes;
}
private static void DrawIdentifier(EqdpIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.SetId.Id}", FrameColor);
ImUtf8.HoverTooltip("Model Set ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Gender.ToName(), FrameColor);
ImUtf8.HoverTooltip("Model Race"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Race.ToName(), FrameColor);
ImUtf8.HoverTooltip("Gender"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Slot.ToName(), FrameColor);
ImUtf8.HoverTooltip("Equip Slot"u8);
}
private static bool DrawEntry(EqdpEntryInternal defaultEntry, ref EqdpEntryInternal entry, bool disabled)
{
var changes = false;
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
if (Checkmark("Material##eqdp"u8, "\0"u8, entry.Material, defaultEntry.Material, out var newMaterial))
{
entry = entry with { Material = newMaterial };
changes = true;
}
ImGui.SameLine();
if (Checkmark("Model##eqdp"u8, "\0"u8, entry.Model, defaultEntry.Model, out var newModel))
{
entry = entry with { Material = newModel };
changes = true;
}
return changes;
}
public static bool DrawPrimaryId(ref EqdpIdentifier identifier, float unscaledWidth = 100)
{
var ret = IdInput("##eqdpPrimaryId"u8, unscaledWidth, identifier.SetId.Id, out var setId, 0, ExpandedEqpGmpBase.Count - 1,
identifier.SetId.Id <= 1);
ImUtf8.HoverTooltip(
"Model Set ID - You can usually find this as the 'e####' part of an item path.\nThis should generally not be left <= 1 unless you explicitly want that."u8);
if (ret)
identifier = identifier with { SetId = setId };
return ret;
}
public static bool DrawRace(ref EqdpIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.Race("##eqdpRace", identifier.Race, out var race, unscaledWidth);
ImUtf8.HoverTooltip("Model Race"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(identifier.Gender, race) };
return ret;
}
public static bool DrawGender(ref EqdpIdentifier identifier, float unscaledWidth = 120)
{
var ret = Combos.Gender("##eqdpGender", identifier.Gender, out var gender, unscaledWidth);
ImUtf8.HoverTooltip("Gender"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(gender, identifier.Race) };
return ret;
}
public static bool DrawEquipSlot(ref EqdpIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.EqdpEquipSlot("##eqdpSlot", identifier.Slot, out var slot, unscaledWidth);
ImUtf8.HoverTooltip("Equip Slot"u8);
if (ret)
identifier = identifier with { Slot = slot };
return ret;
}
}

View file

@ -0,0 +1,134 @@
using Dalamud.Interface;
using ImGuiNET;
using OtterGui.Raii;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class EqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<EqpIdentifier, EqpEntryInternal>(editor, metaFiles), IService
{
public override ReadOnlySpan<byte> Label
=> "Equipment Parameter Edits (EQP)###EQP"u8;
public override int NumColumns
=> 5;
protected override void Initialize()
{
Identifier = new EqpIdentifier(1, EquipSlot.Body);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = new EqpEntryInternal(ExpandedEqpFile.GetDefault(MetaFiles, Identifier.SetId), Identifier.Slot);
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current EQP manipulations to clipboard."u8, MetaDictionary.SerializeTo([], Editor.Eqp));
ImGui.TableNextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
UpdateEntry();
DrawEntry(Identifier.Slot, Entry, ref Entry, true);
}
protected override void DrawEntry(EqpIdentifier identifier, EqpEntryInternal entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
var defaultEntry = new EqpEntryInternal(ExpandedEqpFile.GetDefault(MetaFiles, identifier.SetId), identifier.Slot);
if (DrawEntry(identifier.Slot, defaultEntry, ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(EqpIdentifier, EqpEntryInternal)> Enumerate()
=> Editor.Eqp.Select(kvp => (kvp.Key, kvp.Value));
private static bool DrawIdentifierInput(ref EqpIdentifier identifier)
{
ImGui.TableNextColumn();
var changes = DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
changes |= DrawEquipSlot(ref identifier);
return changes;
}
private static void DrawIdentifier(EqpIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.SetId.Id}", FrameColor);
ImUtf8.HoverTooltip("Model Set ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Slot.ToName(), FrameColor);
ImUtf8.HoverTooltip("Equip Slot"u8);
}
private static bool DrawEntry(EquipSlot slot, EqpEntryInternal defaultEntry, ref EqpEntryInternal entry, bool disabled)
{
var changes = false;
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
var offset = Eqp.OffsetAndMask(slot).Item1;
DrawBox(ref entry, 0);
for (var i = 1; i < Eqp.EqpAttributes[slot].Count; ++i)
{
ImUtf8.SameLineInner();
DrawBox(ref entry, i);
}
return changes;
void DrawBox(ref EqpEntryInternal entry, int i)
{
using var id = ImUtf8.PushId(i);
var flag = 1u << i;
var eqpFlag = (EqpEntry)((ulong)flag << offset);
var defaultValue = (flag & defaultEntry.Value) != 0;
var value = (flag & entry.Value) != 0;
if (Checkmark("##eqp"u8, eqpFlag.ToLocalName(), value, defaultValue, out var newValue))
{
entry = new EqpEntryInternal(newValue ? entry.Value | flag : entry.Value & ~flag);
changes = true;
}
}
}
public static bool DrawPrimaryId(ref EqpIdentifier identifier, float unscaledWidth = 100)
{
var ret = IdInput("##eqpPrimaryId"u8, unscaledWidth, identifier.SetId.Id, out var setId, 0, ExpandedEqpGmpBase.Count - 1,
identifier.SetId.Id <= 1);
ImUtf8.HoverTooltip(
"Model Set ID - You can usually find this as the 'e####' part of an item path.\nThis should generally not be left <= 1 unless you explicitly want that."u8);
if (ret)
identifier = identifier with { SetId = setId };
return ret;
}
public static bool DrawEquipSlot(ref EqpIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.EqpEquipSlot("##eqpSlot", identifier.Slot, out var slot, unscaledWidth);
ImUtf8.HoverTooltip("Equip Slot"u8);
if (ret)
identifier = identifier with { Slot = slot };
return ret;
}
}

View file

@ -0,0 +1,147 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class EstMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<EstIdentifier, EstEntry>(editor, metaFiles), IService
{
public override ReadOnlySpan<byte> Label
=> "Extra Skeleton Parameters (EST)###EST"u8;
public override int NumColumns
=> 7;
protected override void Initialize()
{
Identifier = new EstIdentifier(1, EstType.Hair, GenderRace.MidlanderMale);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = EstFile.GetDefault(MetaFiles, Identifier.Slot, Identifier.GenderRace, Identifier.SetId);
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current EST manipulations to clipboard."u8, MetaDictionary.SerializeTo([], Editor.Est));
ImGui.TableNextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
UpdateEntry();
DrawEntry(Entry, ref Entry, true);
}
protected override void DrawEntry(EstIdentifier identifier, EstEntry entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
var defaultEntry = EstFile.GetDefault(MetaFiles, identifier.Slot, identifier.GenderRace, identifier.SetId);
if (DrawEntry(defaultEntry, ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(EstIdentifier, EstEntry)> Enumerate()
=> Editor.Est.Select(kvp => (kvp.Key, kvp.Value));
private static bool DrawIdentifierInput(ref EstIdentifier identifier)
{
ImGui.TableNextColumn();
var changes = DrawPrimaryId(ref identifier);
ImGui.TableNextColumn();
changes |= DrawRace(ref identifier);
ImGui.TableNextColumn();
changes |= DrawGender(ref identifier);
ImGui.TableNextColumn();
changes |= DrawSlot(ref identifier);
return changes;
}
private static void DrawIdentifier(EstIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.SetId.Id}", FrameColor);
ImUtf8.HoverTooltip("Model Set ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Race.ToName(), FrameColor);
ImUtf8.HoverTooltip("Model Race"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Gender.ToName(), FrameColor);
ImUtf8.HoverTooltip("Gender"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Slot.ToString(), FrameColor);
ImUtf8.HoverTooltip("Extra Skeleton Type"u8);
}
private static bool DrawEntry(EstEntry defaultEntry, ref EstEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
var ret = DragInput("##estValue"u8, [], 100f * ImUtf8.GlobalScale, entry.Value, defaultEntry.Value, out var newValue, (ushort)0,
ushort.MaxValue, 0.05f, !disabled);
if (ret)
entry = new EstEntry(newValue);
return ret;
}
public static bool DrawPrimaryId(ref EstIdentifier identifier, float unscaledWidth = 100)
{
var ret = IdInput("##estPrimaryId"u8, unscaledWidth, identifier.SetId.Id, out var setId, 0, ExpandedEqpGmpBase.Count - 1,
identifier.SetId.Id <= 1);
ImUtf8.HoverTooltip(
"Model Set ID - You can usually find this as the 'e####' part of an item path.\nThis should generally not be left <= 1 unless you explicitly want that."u8);
if (ret)
identifier = identifier with { SetId = setId };
return ret;
}
public static bool DrawRace(ref EstIdentifier identifier, float unscaledWidth = 100)
{
var ret = Combos.Race("##estRace", identifier.Race, out var race, unscaledWidth);
ImUtf8.HoverTooltip("Model Race"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(identifier.Gender, race) };
return ret;
}
public static bool DrawGender(ref EstIdentifier identifier, float unscaledWidth = 120)
{
var ret = Combos.Gender("##estGender", identifier.Gender, out var gender, unscaledWidth);
ImUtf8.HoverTooltip("Gender"u8);
if (ret)
identifier = identifier with { GenderRace = Names.CombinedRace(gender, identifier.Race) };
return ret;
}
public static bool DrawSlot(ref EstIdentifier identifier, float unscaledWidth = 200)
{
var ret = Combos.EstSlot("##estSlot", identifier.Slot, out var slot, unscaledWidth);
ImUtf8.HoverTooltip("Extra Skeleton Type"u8);
if (ret)
identifier = identifier with { Slot = slot };
return ret;
}
}

View file

@ -0,0 +1,111 @@
using Dalamud.Interface;
using ImGuiNET;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class GlobalEqpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<GlobalEqpManipulation, byte>(editor, metaFiles), IService
{
public override ReadOnlySpan<byte> Label
=> "Global Equipment Parameter Edits (Global EQP)###GEQP"u8;
public override int NumColumns
=> 4;
protected override void Initialize()
{
Identifier = new GlobalEqpManipulation()
{
Condition = 1,
Type = GlobalEqpType.DoNotHideEarrings,
};
}
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current global EQP manipulations to clipboard."u8, MetaDictionary.SerializeTo([], Editor.GlobalEqp));
ImGui.TableNextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier);
DrawIdentifierInput(ref Identifier);
}
protected override void DrawEntry(GlobalEqpManipulation identifier, byte _)
{
DrawMetaButtons(identifier, 0);
DrawIdentifier(identifier);
}
protected override IEnumerable<(GlobalEqpManipulation, byte)> Enumerate()
=> Editor.GlobalEqp.Select(identifier => (identifier, (byte)0));
private static void DrawIdentifierInput(ref GlobalEqpManipulation identifier)
{
ImGui.TableNextColumn();
DrawType(ref identifier);
ImGui.TableNextColumn();
if (identifier.Type.HasCondition())
DrawCondition(ref identifier);
else
ImUtf8.ScaledDummy(100);
}
private static void DrawIdentifier(GlobalEqpManipulation identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Type.ToName(), FrameColor);
ImUtf8.HoverTooltip("Global EQP Type"u8);
ImGui.TableNextColumn();
if (identifier.Type.HasCondition())
{
ImUtf8.TextFramed($"{identifier.Condition.Id}", FrameColor);
ImUtf8.HoverTooltip("Conditional Model ID"u8);
}
}
public static bool DrawType(ref GlobalEqpManipulation identifier, float unscaledWidth = 250)
{
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using var combo = ImUtf8.Combo("##geqpType"u8, identifier.Type.ToName());
if (!combo)
return false;
var ret = false;
foreach (var type in Enum.GetValues<GlobalEqpType>())
{
if (ImUtf8.Selectable(type.ToName(), type == identifier.Type))
{
identifier = new GlobalEqpManipulation
{
Type = type,
Condition = type.HasCondition() ? identifier.Type.HasCondition() ? identifier.Condition : 1 : 0,
};
ret = true;
}
ImUtf8.HoverTooltip(type.ToDescription());
}
return ret;
}
public static void DrawCondition(ref GlobalEqpManipulation identifier, float unscaledWidth = 100)
{
if (IdInput("##geqpCond"u8, unscaledWidth, identifier.Condition.Id, out var newId, 1, ushort.MaxValue,
identifier.Condition.Id <= 1))
identifier = identifier with { Condition = newId };
ImUtf8.HoverTooltip("The Model ID for the item that should not be hidden."u8);
}
}

View file

@ -0,0 +1,148 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Structs;
using Penumbra.Meta.Files;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class GmpMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<GmpIdentifier, GmpEntry>(editor, metaFiles), IService
{
public override ReadOnlySpan<byte> Label
=> "Visor/Gimmick Edits (GMP)###GMP"u8;
public override int NumColumns
=> 7;
protected override void Initialize()
{
Identifier = new GmpIdentifier(1);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = ExpandedGmpFile.GetDefault(MetaFiles, Identifier.SetId);
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current Gmp manipulations to clipboard."u8, MetaDictionary.SerializeTo([], Editor.Gmp));
ImGui.TableNextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
UpdateEntry();
DrawEntry(Entry, ref Entry, true);
}
protected override void DrawEntry(GmpIdentifier identifier, GmpEntry entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
var defaultEntry = ExpandedGmpFile.GetDefault(MetaFiles, identifier.SetId);
if (DrawEntry(defaultEntry, ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(GmpIdentifier, GmpEntry)> Enumerate()
=> Editor.Gmp.Select(kvp => (kvp.Key, kvp.Value));
private static bool DrawIdentifierInput(ref GmpIdentifier identifier)
{
ImGui.TableNextColumn();
return DrawPrimaryId(ref identifier);
}
private static void DrawIdentifier(GmpIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.SetId.Id}", FrameColor);
ImUtf8.HoverTooltip("Model Set ID"u8);
}
private static bool DrawEntry(GmpEntry defaultEntry, ref GmpEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
var changes = false;
if (Checkmark("##gmpEnabled"u8, "Gimmick Enabled", entry.Enabled, defaultEntry.Enabled, out var enabled))
{
entry = entry with { Enabled = enabled };
changes = true;
}
ImGui.TableNextColumn();
if (Checkmark("##gmpAnimated"u8, "Gimmick Animated", entry.Animated, defaultEntry.Animated, out var animated))
{
entry = entry with { Animated = animated };
changes = true;
}
var rotationWidth = 75 * ImUtf8.GlobalScale;
ImGui.TableNextColumn();
if (DragInput("##gmpRotationA"u8, "Rotation A in Degrees"u8, rotationWidth, entry.RotationA, defaultEntry.RotationA, out var rotationA,
(ushort)0, (ushort)360, 0.05f, !disabled))
{
entry = entry with { RotationA = rotationA };
changes = true;
}
ImUtf8.SameLineInner();
if (DragInput("##gmpRotationB"u8, "Rotation B in Degrees"u8, rotationWidth, entry.RotationB, defaultEntry.RotationB, out var rotationB,
(ushort)0, (ushort)360, 0.05f, !disabled))
{
entry = entry with { RotationB = rotationB };
changes = true;
}
ImUtf8.SameLineInner();
if (DragInput("##gmpRotationC"u8, "Rotation C in Degrees"u8, rotationWidth, entry.RotationC, defaultEntry.RotationC, out var rotationC,
(ushort)0, (ushort)360, 0.05f, !disabled))
{
entry = entry with { RotationC = rotationC };
changes = true;
}
var unkWidth = 50 * ImUtf8.GlobalScale;
ImGui.TableNextColumn();
if (DragInput("##gmpUnkA"u8, "Animation Type A?"u8, unkWidth, entry.UnknownA, defaultEntry.UnknownA, out var unknownA,
(byte)0, (byte)15, 0.01f, !disabled))
{
entry = entry with { UnknownA = unknownA };
changes = true;
}
ImUtf8.SameLineInner();
if (DragInput("##gmpUnkB"u8, "Animation Type B?"u8, unkWidth, entry.UnknownB, defaultEntry.UnknownB, out var unknownB,
(byte)0, (byte)15, 0.01f, !disabled))
{
entry = entry with { UnknownB = unknownB };
changes = true;
}
return changes;
}
public static bool DrawPrimaryId(ref GmpIdentifier identifier, float unscaledWidth = 100)
{
var ret = IdInput("##gmpPrimaryId"u8, unscaledWidth, identifier.SetId.Id, out var setId, 1, ExpandedEqpGmpBase.Count - 1,
identifier.SetId.Id <= 1);
ImUtf8.HoverTooltip(
"Model Set ID - You can usually find this as the 'e####' part of an item path.\nThis should generally not be left <= 1 unless you explicitly want that."u8);
if (ret)
identifier = new GmpIdentifier(setId);
return ret;
}
}

View file

@ -12,22 +12,16 @@ using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class ImcMetaDrawer(ModEditor editor, MetaFileManager metaFiles)
public sealed class ImcMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<ImcIdentifier, ImcEntry>(editor, metaFiles), IService
{
private bool _fileExists;
public override ReadOnlySpan<byte> Label
=> "Variant Edits (IMC)###IMC"u8;
private const string ModelSetIdTooltipShort = "Model Set ID";
private const string EquipSlotTooltip = "Equip Slot";
private const string ModelRaceTooltip = "Model Race";
private const string GenderTooltip = "Gender";
private const string ObjectTypeTooltip = "Object Type";
private const string SecondaryIdTooltip = "Secondary ID";
private const string PrimaryIdTooltipShort = "Primary ID";
private const string VariantIdTooltip = "Variant ID";
private const string EstTypeTooltip = "EST Type";
private const string RacialTribeTooltip = "Racial Tribe";
private const string ScalingTypeTooltip = "Scaling Type";
public override int NumColumns
=> 10;
private bool _fileExists;
protected override void Initialize()
{
@ -41,14 +35,14 @@ public sealed class ImcMetaDrawer(ModEditor editor, MetaFileManager metaFiles)
protected override void DrawNew()
{
ImGui.TableNextColumn();
// Copy To Clipboard
CopyToClipboardButton("Copy all current IMC manipulations to clipboard."u8, MetaDictionary.SerializeTo([], Editor.Imc));
ImGui.TableNextColumn();
var canAdd = _fileExists && Editor.MetaEditor.CanAdd(Identifier);
var canAdd = _fileExists && !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : !_fileExists ? "This IMC file does not exist."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.MetaEditor.TryAdd(Identifier, Entry);
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifier(ref Identifier))
if (DrawIdentifierInput(ref Identifier))
UpdateEntry();
using var disabled = ImRaii.Disabled();
@ -57,46 +51,15 @@ public sealed class ImcMetaDrawer(ModEditor editor, MetaFileManager metaFiles)
protected override void DrawEntry(ImcIdentifier identifier, ImcEntry entry)
{
const uint frameColor = 0;
// Meta Buttons
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.ObjectType.ToName(), frameColor);
ImUtf8.HoverTooltip("Object Type"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.PrimaryId.Id}", frameColor);
ImUtf8.HoverTooltip("Primary ID");
ImGui.TableNextColumn();
if (identifier.ObjectType is ObjectType.Equipment or ObjectType.Accessory)
{
ImUtf8.TextFramed(identifier.EquipSlot.ToName(), frameColor);
ImUtf8.HoverTooltip("Equip Slot"u8);
}
else
{
ImUtf8.TextFramed($"{identifier.SecondaryId.Id}", frameColor);
ImUtf8.HoverTooltip("Secondary ID"u8);
}
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.Variant.Id}", frameColor);
ImUtf8.HoverTooltip("Variant"u8);
ImGui.TableNextColumn();
if (identifier.ObjectType is ObjectType.DemiHuman)
{
ImUtf8.TextFramed(identifier.EquipSlot.ToName(), frameColor);
ImUtf8.HoverTooltip("Equip Slot"u8);
}
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
var defaultEntry = MetaFiles.ImcChecker.GetDefaultEntry(identifier, true).Entry;
if (DrawEntry(defaultEntry, ref entry, true))
Editor.MetaEditor.Update(identifier, entry);
Editor.Changes |= Editor.Update(identifier, entry);
}
private static bool DrawIdentifier(ref ImcIdentifier identifier)
private static bool DrawIdentifierInput(ref ImcIdentifier identifier)
{
ImGui.TableNextColumn();
var change = DrawObjectType(ref identifier);
@ -121,6 +84,41 @@ public sealed class ImcMetaDrawer(ModEditor editor, MetaFileManager metaFiles)
return change;
}
private static void DrawIdentifier(ImcIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.ObjectType.ToName(), FrameColor);
ImUtf8.HoverTooltip("Object Type"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.PrimaryId.Id}", FrameColor);
ImUtf8.HoverTooltip("Primary ID");
ImGui.TableNextColumn();
if (identifier.ObjectType is ObjectType.Equipment or ObjectType.Accessory)
{
ImUtf8.TextFramed(identifier.EquipSlot.ToName(), FrameColor);
ImUtf8.HoverTooltip("Equip Slot"u8);
}
else
{
ImUtf8.TextFramed($"{identifier.SecondaryId.Id}", FrameColor);
ImUtf8.HoverTooltip("Secondary ID"u8);
}
ImGui.TableNextColumn();
ImUtf8.TextFramed($"{identifier.Variant.Id}", FrameColor);
ImUtf8.HoverTooltip("Variant"u8);
ImGui.TableNextColumn();
if (identifier.ObjectType is ObjectType.DemiHuman)
{
ImUtf8.TextFramed(identifier.EquipSlot.ToName(), FrameColor);
ImUtf8.HoverTooltip("Equip Slot"u8);
}
}
private static bool DrawEntry(ImcEntry defaultEntry, ref ImcEntry entry, bool addDefault)
{
ImGui.TableNextColumn();
@ -142,7 +140,7 @@ public sealed class ImcMetaDrawer(ModEditor editor, MetaFileManager metaFiles)
protected override IEnumerable<(ImcIdentifier, ImcEntry)> Enumerate()
=> Editor.MetaEditor.Imc.Select(kvp => (kvp.Key, kvp.Value));
=> Editor.Imc.Select(kvp => (kvp.Key, kvp.Value));
public static bool DrawObjectType(ref ImcIdentifier identifier, float width = 110)
{
@ -270,7 +268,7 @@ public sealed class ImcMetaDrawer(ModEditor editor, MetaFileManager metaFiles)
return true;
}
public static bool DrawAttributes(ImcEntry defaultEntry, ref ImcEntry entry)
private static bool DrawAttributes(ImcEntry defaultEntry, ref ImcEntry entry)
{
var changes = false;
for (var i = 0; i < ImcEntry.NumAttributes; ++i)
@ -292,62 +290,4 @@ public sealed class ImcMetaDrawer(ModEditor editor, MetaFileManager metaFiles)
return changes;
}
/// <summary>
/// A number input for ids with an optional max id of given width.
/// Returns true if newId changed against currentId.
/// </summary>
private static bool IdInput(ReadOnlySpan<byte> label, float unscaledWidth, ushort currentId, out ushort newId, int minId, int maxId,
bool border)
{
int tmp = currentId;
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, UiHelpers.Scale, border);
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder, border);
if (ImUtf8.InputScalar(label, ref tmp))
tmp = Math.Clamp(tmp, minId, maxId);
newId = (ushort)tmp;
return newId != currentId;
}
/// <summary>
/// A dragging int input of given width that compares against a default value, shows a tooltip and clamps against min and max.
/// Returns true if newValue changed against currentValue.
/// </summary>
private static bool DragInput<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> tooltip, float width, T currentValue, T defaultValue,
out T newValue, T minValue, T maxValue, float speed, bool addDefault) where T : unmanaged, INumber<T>
{
newValue = currentValue;
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue > currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
ImGui.SetNextItemWidth(width);
if (ImUtf8.DragScalar(label, ref newValue, minValue, maxValue, speed))
newValue = newValue <= minValue ? minValue : newValue >= maxValue ? maxValue : newValue;
if (addDefault)
ImUtf8.HoverTooltip($"{tooltip}\nDefault Value: {defaultValue}");
else
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
/// <summary>
/// A checkmark that compares against a default value and shows a tooltip.
/// Returns true if newValue is changed against currentValue.
/// </summary>
private static bool Checkmark(ReadOnlySpan<byte> label, ReadOnlySpan<byte> tooltip, bool currentValue, bool defaultValue,
out bool newValue)
{
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
}

View file

@ -0,0 +1,154 @@
using Dalamud.Interface;
using ImGuiNET;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.Api.Api;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public interface IMetaDrawer
{
public ReadOnlySpan<byte> Label { get; }
public int NumColumns { get; }
public void Draw();
}
public abstract class MetaDrawer<TIdentifier, TEntry>(ModMetaEditor editor, MetaFileManager metaFiles) : IMetaDrawer
where TIdentifier : unmanaged, IMetaIdentifier
where TEntry : unmanaged
{
protected const uint FrameColor = 0;
protected readonly ModMetaEditor Editor = editor;
protected readonly MetaFileManager MetaFiles = metaFiles;
protected TIdentifier Identifier;
protected TEntry Entry;
private bool _initialized;
public void Draw()
{
if (!_initialized)
{
Initialize();
_initialized = true;
}
using var id = ImUtf8.PushId((int)Identifier.Type);
DrawNew();
foreach (var ((identifier, entry), idx) in Enumerate().WithIndex())
{
id.Push(idx);
DrawEntry(identifier, entry);
id.Pop();
}
}
public abstract ReadOnlySpan<byte> Label { get; }
public abstract int NumColumns { get; }
protected abstract void DrawNew();
protected abstract void Initialize();
protected abstract void DrawEntry(TIdentifier identifier, TEntry entry);
protected abstract IEnumerable<(TIdentifier, TEntry)> Enumerate();
/// <summary>
/// A number input for ids with an optional max id of given width.
/// Returns true if newId changed against currentId.
/// </summary>
protected static bool IdInput(ReadOnlySpan<byte> label, float unscaledWidth, ushort currentId, out ushort newId, int minId, int maxId,
bool border)
{
int tmp = currentId;
ImGui.SetNextItemWidth(unscaledWidth * ImUtf8.GlobalScale);
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, UiHelpers.Scale, border);
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder, border);
if (ImUtf8.InputScalar(label, ref tmp))
tmp = Math.Clamp(tmp, minId, maxId);
newId = (ushort)tmp;
return newId != currentId;
}
/// <summary>
/// A dragging int input of given width that compares against a default value, shows a tooltip and clamps against min and max.
/// Returns true if newValue changed against currentValue.
/// </summary>
protected static bool DragInput<T>(ReadOnlySpan<byte> label, ReadOnlySpan<byte> tooltip, float width, T currentValue, T defaultValue,
out T newValue, T minValue, T maxValue, float speed, bool addDefault) where T : unmanaged, INumber<T>
{
newValue = currentValue;
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue > currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
ImGui.SetNextItemWidth(width);
if (ImUtf8.DragScalar(label, ref newValue, minValue, maxValue, speed))
newValue = newValue <= minValue ? minValue : newValue >= maxValue ? maxValue : newValue;
if (addDefault)
ImUtf8.HoverTooltip($"{tooltip}\nDefault Value: {defaultValue}");
else
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
/// <summary>
/// A checkmark that compares against a default value and shows a tooltip.
/// Returns true if newValue is changed against currentValue.
/// </summary>
protected static bool Checkmark(ReadOnlySpan<byte> label, ReadOnlySpan<byte> tooltip, bool currentValue, bool defaultValue,
out bool newValue)
{
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
/// <summary>
/// A checkmark that compares against a default value and shows a tooltip.
/// Returns true if newValue is changed against currentValue.
/// </summary>
protected static bool Checkmark(ReadOnlySpan<byte> label, ReadOnlySpan<char> tooltip, bool currentValue, bool defaultValue,
out bool newValue)
{
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
newValue = currentValue;
ImUtf8.Checkbox(label, ref newValue);
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, tooltip);
return newValue != currentValue;
}
protected void DrawMetaButtons(TIdentifier identifier, TEntry entry)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy this manipulation to clipboard."u8, new JArray { MetaDictionary.Serialize(identifier, entry)! });
ImGui.TableNextColumn();
if (ImUtf8.IconButton(FontAwesomeIcon.Trash, "Delete this meta manipulation."u8))
Editor.Changes |= Editor.Remove(identifier);
}
protected void CopyToClipboardButton(ReadOnlySpan<byte> tooltip, JToken? manipulations)
{
if (!ImUtf8.IconButton(FontAwesomeIcon.Clipboard, tooltip))
return;
var text = Functions.ToCompressedBase64(manipulations, MetaApi.CurrentVersion);
if (text.Length > 0)
ImGui.SetClipboardText(text);
}
}

View file

@ -0,0 +1,35 @@
using OtterGui.Services;
using Penumbra.Meta.Manipulations;
namespace Penumbra.UI.AdvancedWindow.Meta;
public class MetaDrawers(
EqdpMetaDrawer eqdp,
EqpMetaDrawer eqp,
EstMetaDrawer est,
GlobalEqpMetaDrawer globalEqp,
GmpMetaDrawer gmp,
ImcMetaDrawer imc,
RspMetaDrawer rsp) : IService
{
public readonly EqdpMetaDrawer Eqdp = eqdp;
public readonly EqpMetaDrawer Eqp = eqp;
public readonly EstMetaDrawer Est = est;
public readonly GmpMetaDrawer Gmp = gmp;
public readonly RspMetaDrawer Rsp = rsp;
public readonly ImcMetaDrawer Imc = imc;
public readonly GlobalEqpMetaDrawer GlobalEqp = globalEqp;
public IMetaDrawer? Get(MetaManipulationType type)
=> type switch
{
MetaManipulationType.Imc => Imc,
MetaManipulationType.Eqdp => Eqdp,
MetaManipulationType.Eqp => Eqp,
MetaManipulationType.Est => Est,
MetaManipulationType.Gmp => Gmp,
MetaManipulationType.Rsp => Rsp,
MetaManipulationType.GlobalEqp => GlobalEqp,
_ => null,
};
}

View file

@ -0,0 +1,112 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using OtterGui.Services;
using OtterGui.Text;
using Penumbra.GameData.Enums;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.Classes;
namespace Penumbra.UI.AdvancedWindow.Meta;
public sealed class RspMetaDrawer(ModMetaEditor editor, MetaFileManager metaFiles)
: MetaDrawer<RspIdentifier, RspEntry>(editor, metaFiles), IService
{
public override ReadOnlySpan<byte> Label
=> "Racial Scaling Edits (RSP)###RSP"u8;
public override int NumColumns
=> 5;
protected override void Initialize()
{
Identifier = new RspIdentifier(SubRace.Midlander, RspAttribute.MaleMinSize);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = CmpFile.GetDefault(MetaFiles, Identifier.SubRace, Identifier.Attribute);
protected override void DrawNew()
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current RSP manipulations to clipboard."u8, MetaDictionary.SerializeTo([], Editor.Rsp));
ImGui.TableNextColumn();
var canAdd = !Editor.Contains(Identifier);
var tt = canAdd ? "Stage this edit."u8 : "This entry is already edited."u8;
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, tt, disabled: !canAdd))
Editor.Changes |= Editor.TryAdd(Identifier, Entry);
if (DrawIdentifierInput(ref Identifier))
UpdateEntry();
DrawEntry(Entry, ref Entry, true);
}
protected override void DrawEntry(RspIdentifier identifier, RspEntry entry)
{
DrawMetaButtons(identifier, entry);
DrawIdentifier(identifier);
var defaultEntry = CmpFile.GetDefault(MetaFiles, identifier.SubRace, identifier.Attribute);
if (DrawEntry(defaultEntry, ref entry, false))
Editor.Changes |= Editor.Update(identifier, entry);
}
protected override IEnumerable<(RspIdentifier, RspEntry)> Enumerate()
=> Editor.Rsp.Select(kvp => (kvp.Key, kvp.Value));
private static bool DrawIdentifierInput(ref RspIdentifier identifier)
{
ImGui.TableNextColumn();
var changes = DrawSubRace(ref identifier);
ImGui.TableNextColumn();
changes |= DrawAttribute(ref identifier);
return changes;
}
private static void DrawIdentifier(RspIdentifier identifier)
{
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.SubRace.ToName(), FrameColor);
ImUtf8.HoverTooltip("Model Set ID"u8);
ImGui.TableNextColumn();
ImUtf8.TextFramed(identifier.Attribute.ToFullString(), FrameColor);
ImUtf8.HoverTooltip("Equip Slot"u8);
}
private static bool DrawEntry(RspEntry defaultEntry, ref RspEntry entry, bool disabled)
{
using var dis = ImRaii.Disabled(disabled);
ImGui.TableNextColumn();
var ret = DragInput("##rspValue"u8, [], ImUtf8.GlobalScale * 150, defaultEntry.Value, entry.Value, out var newValue,
RspEntry.MinValue, RspEntry.MaxValue, 0.001f, !disabled);
if (ret)
entry = new RspEntry(newValue);
return ret;
}
public static bool DrawSubRace(ref RspIdentifier identifier, float unscaledWidth = 150)
{
var ret = Combos.SubRace("##rspSubRace", identifier.SubRace, out var subRace, unscaledWidth);
ImUtf8.HoverTooltip("Racial Clan"u8);
if (ret)
identifier = identifier with { SubRace = subRace };
return ret;
}
public static bool DrawAttribute(ref RspIdentifier identifier, float unscaledWidth = 200)
{
var ret = Combos.RspAttribute("##rspAttribute", identifier.Attribute, out var attribute, unscaledWidth);
ImUtf8.HoverTooltip("Scaling Attribute"u8);
if (ret)
identifier = identifier with { Attribute = attribute };
return ret;
}
}

View file

@ -1,27 +1,18 @@
using System.Reflection.Emit;
using Dalamud.Interface;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
using OtterGui.Text.EndObjects;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Files;
using Penumbra.Api.Api;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.UI.AdvancedWindow.Meta;
using Penumbra.UI.Classes;
using Penumbra.UI.ModsTab;
namespace Penumbra.UI.AdvancedWindow;
public partial class ModEditWindow
{
private const string ModelSetIdTooltip =
"Model Set ID - You can usually find this as the 'e####' part of an item path.\nThis should generally not be left <= 1 unless you explicitly want that.";
private readonly MetaDrawers _metaDrawers;
private void DrawMetaTab()
{
@ -56,80 +47,42 @@ public partial class ModEditWindow
if (!child)
return;
DrawEditHeader(MetaManipulation.Type.Eqp);
DrawEditHeader(MetaManipulation.Type.Eqdp);
DrawEditHeader(MetaManipulation.Type.Imc);
DrawEditHeader(MetaManipulation.Type.Est);
DrawEditHeader(MetaManipulation.Type.Gmp);
DrawEditHeader(MetaManipulation.Type.Rsp);
DrawEditHeader(MetaManipulation.Type.GlobalEqp);
DrawEditHeader(MetaManipulationType.Eqp);
DrawEditHeader(MetaManipulationType.Eqdp);
DrawEditHeader(MetaManipulationType.Imc);
DrawEditHeader(MetaManipulationType.Est);
DrawEditHeader(MetaManipulationType.Gmp);
DrawEditHeader(MetaManipulationType.Rsp);
DrawEditHeader(MetaManipulationType.GlobalEqp);
}
private static ReadOnlySpan<byte> Label(MetaManipulation.Type type)
=> type switch
{
MetaManipulation.Type.Imc => "Variant Edits (IMC)###IMC"u8,
MetaManipulation.Type.Eqdp => "Racial Model Edits (EQDP)###EQDP"u8,
MetaManipulation.Type.Eqp => "Equipment Parameter Edits (EQP)###EQP"u8,
MetaManipulation.Type.Est => "Extra Skeleton Parameters (EST)###EST"u8,
MetaManipulation.Type.Gmp => "Visor/Gimmick Edits (GMP)###GMP"u8,
MetaManipulation.Type.Rsp => "Racial Scaling Edits (RSP)###RSP"u8,
MetaManipulation.Type.GlobalEqp => "Global Equipment Parameter Edits (Global EQP)###GEQP"u8,
_ => "\0"u8,
};
private static int ColumnCount(MetaManipulation.Type type)
=> type switch
{
MetaManipulation.Type.Imc => 10,
MetaManipulation.Type.Eqdp => 7,
MetaManipulation.Type.Eqp => 5,
MetaManipulation.Type.Est => 7,
MetaManipulation.Type.Gmp => 7,
MetaManipulation.Type.Rsp => 5,
MetaManipulation.Type.GlobalEqp => 4,
_ => 0,
};
private void DrawEditHeader(MetaManipulation.Type type)
private void DrawEditHeader(MetaManipulationType type)
{
var drawer = _metaDrawers.Get(type);
if (drawer == null)
return;
var oldPos = ImGui.GetCursorPosY();
var header = ImUtf8.CollapsingHeader($"{_editor.MetaEditor.GetCount(type)} {Label(type)}");
var header = ImUtf8.CollapsingHeader($"{_editor.MetaEditor.GetCount(type)} {drawer.Label}");
DrawOtherOptionData(type, oldPos, ImGui.GetCursorPos());
if (!header)
return;
DrawTable(type);
DrawTable(drawer);
}
private IMetaDrawer? Drawer(MetaManipulation.Type type)
=> type switch
{
//MetaManipulation.Type.Imc => expr,
//MetaManipulation.Type.Eqdp => expr,
//MetaManipulation.Type.Eqp => expr,
//MetaManipulation.Type.Est => expr,
//MetaManipulation.Type.Gmp => expr,
//MetaManipulation.Type.Rsp => expr,
//MetaManipulation.Type.GlobalEqp => expr,
_ => null,
};
private void DrawTable(MetaManipulation.Type type)
private static void DrawTable(IMetaDrawer drawer)
{
const ImGuiTableFlags flags = ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.BordersInnerV;
using var table = ImUtf8.Table(Label(type), ColumnCount(type), flags);
using var table = ImUtf8.Table(drawer.Label, drawer.NumColumns, flags);
if (!table)
return;
if (Drawer(type) is not { } drawer)
return;
drawer.Draw();
ImGui.NewLine();
}
private void DrawOtherOptionData(MetaManipulation.Type type, float oldPos, Vector2 newPos)
private void DrawOtherOptionData(MetaManipulationType type, float oldPos, Vector2 newPos)
{
var otherOptionData = _editor.MetaEditor.OtherData[type];
if (otherOptionData.TotalCount <= 0)
@ -149,577 +102,12 @@ public partial class ModEditWindow
ImGui.SetCursorPos(newPos);
}
#if false
private static class EqpRow
{
private static EqpIdentifier _newIdentifier = new(1, EquipSlot.Body);
private static float IdWidth
=> 100 * UiHelpers.Scale;
public static void DrawNew(MetaFileManager metaFileManager, ModEditor editor, Vector2 iconSize)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current EQP manipulations to clipboard.", iconSize,
editor.MetaEditor.Eqp.Select(m => (MetaManipulation)m));
ImGui.TableNextColumn();
var canAdd = editor.MetaEditor.CanAdd(_new);
var tt = canAdd ? "Stage this edit." : "This entry is already edited.";
var defaultEntry = ExpandedEqpFile.GetDefault(metaFileManager, _new.SetId);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, tt, !canAdd, true))
editor.MetaEditor.Add(_new.Copy(defaultEntry));
// Identifier
ImGui.TableNextColumn();
if (IdInput("##eqpId", IdWidth, _new.SetId.Id, out var setId, 1, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
_new = new EqpManipulation(ExpandedEqpFile.GetDefault(metaFileManager, setId), _new.Slot, setId);
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
ImGui.TableNextColumn();
if (Combos.EqpEquipSlot("##eqpSlot", _new.Slot, out var slot))
_new = new EqpManipulation(ExpandedEqpFile.GetDefault(metaFileManager, setId), slot, _new.SetId);
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
// Values
using var disabled = ImRaii.Disabled();
ImGui.TableNextColumn();
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
new Vector2(3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y));
foreach (var flag in Eqp.EqpAttributes[_new.Slot])
{
var value = defaultEntry.HasFlag(flag);
Checkmark("##eqp", flag.ToLocalName(), value, value, out _);
ImGui.SameLine();
}
ImGui.NewLine();
}
public static void Draw(MetaFileManager metaFileManager, EqpManipulation meta, ModEditor editor, Vector2 iconSize)
{
DrawMetaButtons(meta, editor, iconSize);
// Identifier
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.SetId.ToString());
ImGuiUtil.HoverTooltip(ModelSetIdTooltipShort);
var defaultEntry = ExpandedEqpFile.GetDefault(metaFileManager, meta.SetId);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.Slot.ToName());
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
// Values
ImGui.TableNextColumn();
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
new Vector2(3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y));
var idx = 0;
foreach (var flag in Eqp.EqpAttributes[meta.Slot])
{
using var id = ImRaii.PushId(idx++);
var defaultValue = defaultEntry.HasFlag(flag);
var currentValue = meta.Entry.HasFlag(flag);
if (Checkmark("##eqp", flag.ToLocalName(), currentValue, defaultValue, out var value))
editor.MetaEditor.Change(meta.Copy(value ? meta.Entry | flag : meta.Entry & ~flag));
ImGui.SameLine();
}
ImGui.NewLine();
}
}
private static class EqdpRow
{
private static EqdpManipulation _new = new(EqdpEntry.Invalid, EquipSlot.Head, Gender.Male, ModelRace.Midlander, 1);
private static float IdWidth
=> 100 * UiHelpers.Scale;
public static void DrawNew(MetaFileManager metaFileManager, ModEditor editor, Vector2 iconSize)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current EQDP manipulations to clipboard.", iconSize,
editor.MetaEditor.Eqdp.Select(m => (MetaManipulation)m));
ImGui.TableNextColumn();
var raceCode = Names.CombinedRace(_new.Gender, _new.Race);
var validRaceCode = CharacterUtilityData.EqdpIdx(raceCode, false) >= 0;
var canAdd = validRaceCode && editor.MetaEditor.CanAdd(_new);
var tt = canAdd ? "Stage this edit." :
validRaceCode ? "This entry is already edited." : "This combination of race and gender can not be used.";
var defaultEntry = validRaceCode
? ExpandedEqdpFile.GetDefault(metaFileManager, Names.CombinedRace(_new.Gender, _new.Race), _new.Slot.IsAccessory(), _new.SetId)
: 0;
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, tt, !canAdd, true))
editor.MetaEditor.Add(_new.Copy(defaultEntry));
// Identifier
ImGui.TableNextColumn();
if (IdInput("##eqdpId", IdWidth, _new.SetId.Id, out var setId, 0, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
{
var newDefaultEntry = ExpandedEqdpFile.GetDefault(metaFileManager, Names.CombinedRace(_new.Gender, _new.Race),
_new.Slot.IsAccessory(), setId);
_new = new EqdpManipulation(newDefaultEntry, _new.Slot, _new.Gender, _new.Race, setId);
}
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
ImGui.TableNextColumn();
if (Combos.Race("##eqdpRace", _new.Race, out var race))
{
var newDefaultEntry = ExpandedEqdpFile.GetDefault(metaFileManager, Names.CombinedRace(_new.Gender, race),
_new.Slot.IsAccessory(), _new.SetId);
_new = new EqdpManipulation(newDefaultEntry, _new.Slot, _new.Gender, race, _new.SetId);
}
ImGuiUtil.HoverTooltip(ModelRaceTooltip);
ImGui.TableNextColumn();
if (Combos.Gender("##eqdpGender", _new.Gender, out var gender))
{
var newDefaultEntry = ExpandedEqdpFile.GetDefault(metaFileManager, Names.CombinedRace(gender, _new.Race),
_new.Slot.IsAccessory(), _new.SetId);
_new = new EqdpManipulation(newDefaultEntry, _new.Slot, gender, _new.Race, _new.SetId);
}
ImGuiUtil.HoverTooltip(GenderTooltip);
ImGui.TableNextColumn();
if (Combos.EqdpEquipSlot("##eqdpSlot", _new.Slot, out var slot))
{
var newDefaultEntry = ExpandedEqdpFile.GetDefault(metaFileManager, Names.CombinedRace(_new.Gender, _new.Race),
slot.IsAccessory(), _new.SetId);
_new = new EqdpManipulation(newDefaultEntry, slot, _new.Gender, _new.Race, _new.SetId);
}
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
// Values
using var disabled = ImRaii.Disabled();
ImGui.TableNextColumn();
var (bit1, bit2) = defaultEntry.ToBits(_new.Slot);
Checkmark("Material##eqdpCheck1", string.Empty, bit1, bit1, out _);
ImGui.SameLine();
Checkmark("Model##eqdpCheck2", string.Empty, bit2, bit2, out _);
}
public static void Draw(MetaFileManager metaFileManager, EqdpManipulation meta, ModEditor editor, Vector2 iconSize)
{
DrawMetaButtons(meta, editor, iconSize);
// Identifier
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.SetId.ToString());
ImGuiUtil.HoverTooltip(ModelSetIdTooltipShort);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.Race.ToName());
ImGuiUtil.HoverTooltip(ModelRaceTooltip);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.Gender.ToName());
ImGuiUtil.HoverTooltip(GenderTooltip);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.Slot.ToName());
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
// Values
var defaultEntry = ExpandedEqdpFile.GetDefault(metaFileManager, Names.CombinedRace(meta.Gender, meta.Race), meta.Slot.IsAccessory(),
meta.SetId);
var (defaultBit1, defaultBit2) = defaultEntry.ToBits(meta.Slot);
var (bit1, bit2) = meta.Entry.ToBits(meta.Slot);
ImGui.TableNextColumn();
if (Checkmark("Material##eqdpCheck1", string.Empty, bit1, defaultBit1, out var newBit1))
editor.MetaEditor.Change(meta.Copy(Eqdp.FromSlotAndBits(meta.Slot, newBit1, bit2)));
ImGui.SameLine();
if (Checkmark("Model##eqdpCheck2", string.Empty, bit2, defaultBit2, out var newBit2))
editor.MetaEditor.Change(meta.Copy(Eqdp.FromSlotAndBits(meta.Slot, bit1, newBit2)));
}
}
private static class EstRow
{
private static EstManipulation _new = new(Gender.Male, ModelRace.Midlander, EstType.Body, 1, EstEntry.Zero);
private static float IdWidth
=> 100 * UiHelpers.Scale;
public static void DrawNew(MetaFileManager metaFileManager, ModEditor editor, Vector2 iconSize)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current EST manipulations to clipboard.", iconSize,
editor.MetaEditor.Est.Select(m => (MetaManipulation)m));
ImGui.TableNextColumn();
var canAdd = editor.MetaEditor.CanAdd(_new);
var tt = canAdd ? "Stage this edit." : "This entry is already edited.";
var defaultEntry = EstFile.GetDefault(metaFileManager, _new.Slot, Names.CombinedRace(_new.Gender, _new.Race), _new.SetId);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, tt, !canAdd, true))
editor.MetaEditor.Add(_new.Copy(defaultEntry));
// Identifier
ImGui.TableNextColumn();
if (IdInput("##estId", IdWidth, _new.SetId.Id, out var setId, 0, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
{
var newDefaultEntry = EstFile.GetDefault(metaFileManager, _new.Slot, Names.CombinedRace(_new.Gender, _new.Race), setId);
_new = new EstManipulation(_new.Gender, _new.Race, _new.Slot, setId, newDefaultEntry);
}
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
ImGui.TableNextColumn();
if (Combos.Race("##estRace", _new.Race, out var race))
{
var newDefaultEntry = EstFile.GetDefault(metaFileManager, _new.Slot, Names.CombinedRace(_new.Gender, race), _new.SetId);
_new = new EstManipulation(_new.Gender, race, _new.Slot, _new.SetId, newDefaultEntry);
}
ImGuiUtil.HoverTooltip(ModelRaceTooltip);
ImGui.TableNextColumn();
if (Combos.Gender("##estGender", _new.Gender, out var gender))
{
var newDefaultEntry = EstFile.GetDefault(metaFileManager, _new.Slot, Names.CombinedRace(gender, _new.Race), _new.SetId);
_new = new EstManipulation(gender, _new.Race, _new.Slot, _new.SetId, newDefaultEntry);
}
ImGuiUtil.HoverTooltip(GenderTooltip);
ImGui.TableNextColumn();
if (Combos.EstSlot("##estSlot", _new.Slot, out var slot))
{
var newDefaultEntry = EstFile.GetDefault(metaFileManager, slot, Names.CombinedRace(_new.Gender, _new.Race), _new.SetId);
_new = new EstManipulation(_new.Gender, _new.Race, slot, _new.SetId, newDefaultEntry);
}
ImGuiUtil.HoverTooltip(EstTypeTooltip);
// Values
using var disabled = ImRaii.Disabled();
ImGui.TableNextColumn();
IntDragInput("##estSkeleton", "Skeleton Index", IdWidth, _new.Entry.Value, defaultEntry.Value, out _, 0, ushort.MaxValue, 0.05f);
}
public static void Draw(MetaFileManager metaFileManager, EstManipulation meta, ModEditor editor, Vector2 iconSize)
{
DrawMetaButtons(meta, editor, iconSize);
// Identifier
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.SetId.ToString());
ImGuiUtil.HoverTooltip(ModelSetIdTooltipShort);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.Race.ToName());
ImGuiUtil.HoverTooltip(ModelRaceTooltip);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.Gender.ToName());
ImGuiUtil.HoverTooltip(GenderTooltip);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.Slot.ToString());
ImGuiUtil.HoverTooltip(EstTypeTooltip);
// Values
var defaultEntry = EstFile.GetDefault(metaFileManager, meta.Slot, Names.CombinedRace(meta.Gender, meta.Race), meta.SetId);
ImGui.TableNextColumn();
if (IntDragInput("##estSkeleton", $"Skeleton Index\nDefault Value: {defaultEntry}", IdWidth, meta.Entry.Value, defaultEntry.Value,
out var entry, 0, ushort.MaxValue, 0.05f))
editor.MetaEditor.Change(meta.Copy(new EstEntry((ushort)entry)));
}
}
private static class GmpRow
{
private static GmpManipulation _new = new(GmpEntry.Default, 1);
private static float RotationWidth
=> 75 * UiHelpers.Scale;
private static float UnkWidth
=> 50 * UiHelpers.Scale;
private static float IdWidth
=> 100 * UiHelpers.Scale;
public static void DrawNew(MetaFileManager metaFileManager, ModEditor editor, Vector2 iconSize)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current GMP manipulations to clipboard.", iconSize,
editor.MetaEditor.Gmp.Select(m => (MetaManipulation)m));
ImGui.TableNextColumn();
var canAdd = editor.MetaEditor.CanAdd(_new);
var tt = canAdd ? "Stage this edit." : "This entry is already edited.";
var defaultEntry = ExpandedGmpFile.GetDefault(metaFileManager, _new.SetId);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, tt, !canAdd, true))
editor.MetaEditor.Add(_new.Copy(defaultEntry));
// Identifier
ImGui.TableNextColumn();
if (IdInput("##gmpId", IdWidth, _new.SetId.Id, out var setId, 1, ExpandedEqpGmpBase.Count - 1, _new.SetId <= 1))
_new = new GmpManipulation(ExpandedGmpFile.GetDefault(metaFileManager, setId), setId);
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
// Values
using var disabled = ImRaii.Disabled();
ImGui.TableNextColumn();
Checkmark("##gmpEnabled", "Gimmick Enabled", defaultEntry.Enabled, defaultEntry.Enabled, out _);
ImGui.TableNextColumn();
Checkmark("##gmpAnimated", "Gimmick Animated", defaultEntry.Animated, defaultEntry.Animated, out _);
ImGui.TableNextColumn();
IntDragInput("##gmpRotationA", "Rotation A in Degrees", RotationWidth, defaultEntry.RotationA, defaultEntry.RotationA, out _, 0,
360, 0f);
ImGui.SameLine();
IntDragInput("##gmpRotationB", "Rotation B in Degrees", RotationWidth, defaultEntry.RotationB, defaultEntry.RotationB, out _, 0,
360, 0f);
ImGui.SameLine();
IntDragInput("##gmpRotationC", "Rotation C in Degrees", RotationWidth, defaultEntry.RotationC, defaultEntry.RotationC, out _, 0,
360, 0f);
ImGui.TableNextColumn();
IntDragInput("##gmpUnkA", "Animation Type A?", UnkWidth, defaultEntry.UnknownA, defaultEntry.UnknownA, out _, 0, 15, 0f);
ImGui.SameLine();
IntDragInput("##gmpUnkB", "Animation Type B?", UnkWidth, defaultEntry.UnknownB, defaultEntry.UnknownB, out _, 0, 15, 0f);
}
public static void Draw(MetaFileManager metaFileManager, GmpManipulation meta, ModEditor editor, Vector2 iconSize)
{
DrawMetaButtons(meta, editor, iconSize);
// Identifier
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.SetId.ToString());
ImGuiUtil.HoverTooltip(ModelSetIdTooltipShort);
// Values
var defaultEntry = ExpandedGmpFile.GetDefault(metaFileManager, meta.SetId);
ImGui.TableNextColumn();
if (Checkmark("##gmpEnabled", "Gimmick Enabled", meta.Entry.Enabled, defaultEntry.Enabled, out var enabled))
editor.MetaEditor.Change(meta.Copy(meta.Entry with { Enabled = enabled }));
ImGui.TableNextColumn();
if (Checkmark("##gmpAnimated", "Gimmick Animated", meta.Entry.Animated, defaultEntry.Animated, out var animated))
editor.MetaEditor.Change(meta.Copy(meta.Entry with { Animated = animated }));
ImGui.TableNextColumn();
if (IntDragInput("##gmpRotationA", $"Rotation A in Degrees\nDefault Value: {defaultEntry.RotationA}", RotationWidth,
meta.Entry.RotationA, defaultEntry.RotationA, out var rotationA, 0, 360, 0.05f))
editor.MetaEditor.Change(meta.Copy(meta.Entry with { RotationA = (ushort)rotationA }));
ImGui.SameLine();
if (IntDragInput("##gmpRotationB", $"Rotation B in Degrees\nDefault Value: {defaultEntry.RotationB}", RotationWidth,
meta.Entry.RotationB, defaultEntry.RotationB, out var rotationB, 0, 360, 0.05f))
editor.MetaEditor.Change(meta.Copy(meta.Entry with { RotationB = (ushort)rotationB }));
ImGui.SameLine();
if (IntDragInput("##gmpRotationC", $"Rotation C in Degrees\nDefault Value: {defaultEntry.RotationC}", RotationWidth,
meta.Entry.RotationC, defaultEntry.RotationC, out var rotationC, 0, 360, 0.05f))
editor.MetaEditor.Change(meta.Copy(meta.Entry with { RotationC = (ushort)rotationC }));
ImGui.TableNextColumn();
if (IntDragInput("##gmpUnkA", $"Animation Type A?\nDefault Value: {defaultEntry.UnknownA}", UnkWidth, meta.Entry.UnknownA,
defaultEntry.UnknownA, out var unkA, 0, 15, 0.01f))
editor.MetaEditor.Change(meta.Copy(meta.Entry with { UnknownA = (byte)unkA }));
ImGui.SameLine();
if (IntDragInput("##gmpUnkB", $"Animation Type B?\nDefault Value: {defaultEntry.UnknownB}", UnkWidth, meta.Entry.UnknownB,
defaultEntry.UnknownB, out var unkB, 0, 15, 0.01f))
editor.MetaEditor.Change(meta.Copy(meta.Entry with { UnknownB = (byte)unkB }));
}
}
private static class RspRow
{
private static RspManipulation _new = new(SubRace.Midlander, RspAttribute.MaleMinSize, RspEntry.One);
private static float FloatWidth
=> 150 * UiHelpers.Scale;
public static void DrawNew(MetaFileManager metaFileManager, ModEditor editor, Vector2 iconSize)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current RSP manipulations to clipboard.", iconSize,
editor.MetaEditor.Rsp.Select(m => (MetaManipulation)m));
ImGui.TableNextColumn();
var canAdd = editor.MetaEditor.CanAdd(_new);
var tt = canAdd ? "Stage this edit." : "This entry is already edited.";
var defaultEntry = CmpFile.GetDefault(metaFileManager, _new.SubRace, _new.Attribute);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, tt, !canAdd, true))
editor.MetaEditor.Add(_new.Copy(defaultEntry));
// Identifier
ImGui.TableNextColumn();
if (Combos.SubRace("##rspSubRace", _new.SubRace, out var subRace))
_new = new RspManipulation(subRace, _new.Attribute, CmpFile.GetDefault(metaFileManager, subRace, _new.Attribute));
ImGuiUtil.HoverTooltip(RacialTribeTooltip);
ImGui.TableNextColumn();
if (Combos.RspAttribute("##rspAttribute", _new.Attribute, out var attribute))
_new = new RspManipulation(_new.SubRace, attribute, CmpFile.GetDefault(metaFileManager, subRace, attribute));
ImGuiUtil.HoverTooltip(ScalingTypeTooltip);
// Values
using var disabled = ImRaii.Disabled();
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(FloatWidth);
var value = defaultEntry.Value;
ImGui.DragFloat("##rspValue", ref value, 0f);
}
public static void Draw(MetaFileManager metaFileManager, RspManipulation meta, ModEditor editor, Vector2 iconSize)
{
DrawMetaButtons(meta, editor, iconSize);
// Identifier
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.SubRace.ToName());
ImGuiUtil.HoverTooltip(RacialTribeTooltip);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImGui.TextUnformatted(meta.Attribute.ToFullString());
ImGuiUtil.HoverTooltip(ScalingTypeTooltip);
ImGui.TableNextColumn();
// Values
var def = CmpFile.GetDefault(metaFileManager, meta.SubRace, meta.Attribute).Value;
var value = meta.Entry.Value;
ImGui.SetNextItemWidth(FloatWidth);
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
def < value ? ColorId.IncreasedMetaValue.Value() : ColorId.DecreasedMetaValue.Value(),
def != value);
if (ImGui.DragFloat("##rspValue", ref value, 0.001f, RspEntry.MinValue, RspEntry.MaxValue)
&& value is >= RspEntry.MinValue and <= RspEntry.MaxValue)
editor.MetaEditor.Change(meta.Copy(new RspEntry(value)));
ImGuiUtil.HoverTooltip($"Default Value: {def:0.###}");
}
}
private static class GlobalEqpRow
{
private static GlobalEqpManipulation _new = new()
{
Type = GlobalEqpType.DoNotHideEarrings,
Condition = 1,
};
public static void DrawNew(MetaFileManager metaFileManager, ModEditor editor, Vector2 iconSize)
{
ImGui.TableNextColumn();
CopyToClipboardButton("Copy all current global EQP manipulations to clipboard.", iconSize,
editor.MetaEditor.GlobalEqp.Select(m => (MetaManipulation)m));
ImGui.TableNextColumn();
var canAdd = editor.MetaEditor.CanAdd(_new);
var tt = canAdd ? "Stage this edit." : "This entry is already manipulated.";
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), iconSize, tt, !canAdd, true))
editor.MetaEditor.Add(_new);
// Identifier
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(250 * ImUtf8.GlobalScale);
using (var combo = ImUtf8.Combo("##geqpType"u8, _new.Type.ToName()))
{
if (combo)
foreach (var type in Enum.GetValues<GlobalEqpType>())
{
if (ImUtf8.Selectable(type.ToName(), type == _new.Type))
_new = new GlobalEqpManipulation
{
Type = type,
Condition = type.HasCondition() ? _new.Type.HasCondition() ? _new.Condition : 1 : 0,
};
ImUtf8.HoverTooltip(type.ToDescription());
}
}
ImUtf8.HoverTooltip(_new.Type.ToDescription());
ImGui.TableNextColumn();
if (!_new.Type.HasCondition())
return;
if (IdInput("##geqpCond", 100 * ImUtf8.GlobalScale, _new.Condition.Id, out var newId, 1, ushort.MaxValue, _new.Condition.Id <= 1))
_new = _new with { Condition = newId };
ImUtf8.HoverTooltip("The Model ID for the item that should not be hidden."u8);
}
public static void Draw(MetaFileManager metaFileManager, GlobalEqpManipulation meta, ModEditor editor, Vector2 iconSize)
{
DrawMetaButtons(meta, editor, iconSize);
ImGui.TableNextColumn();
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImUtf8.Text(meta.Type.ToName());
ImUtf8.HoverTooltip(meta.Type.ToDescription());
ImGui.TableNextColumn();
if (meta.Type.HasCondition())
{
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + ImGui.GetStyle().FramePadding.X);
ImUtf8.Text($"{meta.Condition.Id}");
}
}
}
#endif
// A number input for ids with a optional max id of given width.
// Returns true if newId changed against currentId.
private static bool IdInput(string label, float width, ushort currentId, out ushort newId, int minId, int maxId, bool border)
{
int tmp = currentId;
ImGui.SetNextItemWidth(width);
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, UiHelpers.Scale, border);
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder, border);
if (ImGui.InputInt(label, ref tmp, 0))
tmp = Math.Clamp(tmp, minId, maxId);
newId = (ushort)tmp;
return newId != currentId;
}
// A checkmark that compares against a default value and shows a tooltip.
// Returns true if newValue is changed against currentValue.
private static bool Checkmark(string label, string tooltip, bool currentValue, bool defaultValue, out bool newValue)
{
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
newValue = currentValue;
ImGui.Checkbox(label, ref newValue);
ImGuiUtil.HoverTooltip(tooltip, ImGuiHoveredFlags.AllowWhenDisabled);
return newValue != currentValue;
}
// A dragging int input of given width that compares against a default value, shows a tooltip and clamps against min and max.
// Returns true if newValue changed against currentValue.
private static bool IntDragInput(string label, string tooltip, float width, int currentValue, int defaultValue, out int newValue,
int minValue, int maxValue, float speed)
{
newValue = currentValue;
using var color = ImRaii.PushColor(ImGuiCol.FrameBg,
defaultValue > currentValue ? ColorId.DecreasedMetaValue.Value() : ColorId.IncreasedMetaValue.Value(),
defaultValue != currentValue);
ImGui.SetNextItemWidth(width);
if (ImGui.DragInt(label, ref newValue, speed, minValue, maxValue))
newValue = Math.Clamp(newValue, minValue, maxValue);
ImGuiUtil.HoverTooltip(tooltip, ImGuiHoveredFlags.AllowWhenDisabled);
return newValue != currentValue;
}
private static void CopyToClipboardButton(string tooltip, Vector2 iconSize, MetaDictionary manipulations)
{
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), iconSize, tooltip, false, true))
return;
var text = Functions.ToCompressedBase64(manipulations, MetaManipulation.CurrentVersion);
var text = Functions.ToCompressedBase64(manipulations, MetaApi.CurrentVersion);
if (text.Length > 0)
ImGui.SetClipboardText(text);
}
@ -731,8 +119,11 @@ public partial class ModEditWindow
var clipboard = ImGuiUtil.GetClipboardText();
var version = Functions.FromCompressedBase64<MetaDictionary>(clipboard, out var manips);
if (version == MetaManipulation.CurrentVersion && manips != null)
if (version == MetaApi.CurrentVersion && manips != null)
{
_editor.MetaEditor.UpdateTo(manips);
_editor.MetaEditor.Changes = true;
}
}
ImGuiUtil.HoverTooltip(
@ -745,194 +136,14 @@ public partial class ModEditWindow
{
var clipboard = ImGuiUtil.GetClipboardText();
var version = Functions.FromCompressedBase64<MetaDictionary>(clipboard, out var manips);
if (version == MetaManipulation.CurrentVersion && manips != null)
if (version == MetaApi.CurrentVersion && manips != null)
{
_editor.MetaEditor.SetTo(manips);
_editor.MetaEditor.Changes = true;
}
}
ImGuiUtil.HoverTooltip(
"Try to set the current meta manipulations to the set currently stored in the clipboard.\nRemoves all other manipulations.");
}
private static void DrawMetaButtons(MetaManipulation meta, ModEditor editor, Vector2 iconSize)
{
//ImGui.TableNextColumn();
//CopyToClipboardButton("Copy this manipulation to clipboard.", iconSize, Array.Empty<MetaManipulation>().Append(meta));
//
//ImGui.TableNextColumn();
//if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Trash.ToIconString(), iconSize, "Delete this meta manipulation.", false, true))
// editor.MetaEditor.Delete(meta);
}
}
public interface IMetaDrawer
{
public void Draw();
}
public abstract class MetaDrawer<TIdentifier, TEntry>(ModEditor editor, MetaFileManager metaFiles) : IMetaDrawer
where TIdentifier : unmanaged, IMetaIdentifier
where TEntry : unmanaged
{
protected readonly ModEditor Editor = editor;
protected readonly MetaFileManager MetaFiles = metaFiles;
protected TIdentifier Identifier;
protected TEntry Entry;
private bool _initialized;
public void Draw()
{
if (!_initialized)
{
Initialize();
_initialized = true;
}
DrawNew();
foreach (var ((identifier, entry), idx) in Enumerate().WithIndex())
{
using var id = ImUtf8.PushId(idx);
DrawEntry(identifier, entry);
}
}
protected abstract void DrawNew();
protected abstract void Initialize();
protected abstract void DrawEntry(TIdentifier identifier, TEntry entry);
protected abstract IEnumerable<(TIdentifier, TEntry)> Enumerate();
}
#if false
public sealed class GmpMetaDrawer(ModEditor editor) : MetaDrawer<GmpIdentifier, GmpEntry>, IService
{
protected override void Initialize()
{
Identifier = new GmpIdentifier(1, EquipSlot.Body);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = ExpandedEqpFile.GetDefault(metaManager, Identifier.SetId);
protected override void DrawNew()
{ }
protected override void DrawEntry(GmpIdentifier identifier, GmpEntry entry)
{ }
protected override IEnumerable<(GmpIdentifier, GmpEntry)> Enumerate()
=> editor.MetaEditor.Eqp.Select(kvp => (kvp.Key, kvp.Value.ToEntry(kvp.Key.Slot)));
}
public sealed class EstMetaDrawer(ModEditor editor) : MetaDrawer<EstIdentifier, EstEntry>, IService
{
protected override void Initialize()
{
Identifier = new EqpIdentifier(1, EquipSlot.Body);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = ExpandedEqpFile.GetDefault(metaManager, Identifier.SetId);
protected override void DrawNew()
{ }
protected override void DrawEntry(EstIdentifier identifier, EstEntry entry)
{ }
protected override IEnumerable<(EstIdentifier, EstEntry)> Enumerate()
=> editor.MetaEditor.Eqp.Select(kvp => (kvp.Key, kvp.Value.ToEntry(kvp.Key.Slot)));
}
public sealed class EqdpMetaDrawer(ModEditor editor) : MetaDrawer<EqdpIdentifier, EqdpEntry>, IService
{
protected override void Initialize()
{
Identifier = new EqdpIdentifier(1, EquipSlot.Body);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = ExpandedEqpFile.GetDefault(metaManager, Identifier.SetId);
protected override void DrawNew()
{ }
protected override void DrawEntry(EqdpIdentifier identifier, EqdpEntry entry)
{ }
protected override IEnumerable<(EqdpIdentifier, EqdpEntry)> Enumerate()
=> editor.MetaEditor.Eqp.Select(kvp => (kvp.Key, kvp.Value.ToEntry(kvp.Key.Slot)));
}
public sealed class EqpMetaDrawer(ModEditor editor, MetaFileManager metaManager) : MetaDrawer<EqpIdentifier, EqpEntry>, IService
{
protected override void Initialize()
{
Identifier = new EqpIdentifier(1, EquipSlot.Body);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = ExpandedEqpFile.GetDefault(metaManager, Identifier.SetId);
protected override void DrawNew()
{ }
protected override void DrawEntry(EqpIdentifier identifier, EqpEntry entry)
{ }
protected override IEnumerable<(EqpIdentifier, EqpEntry)> Enumerate()
=> editor.MetaEditor.Eqp.Select(kvp => (kvp.Key, kvp.Value.ToEntry(kvp.Key.Slot)));
}
public sealed class RspMetaDrawer(ModEditor editor) : MetaDrawer<RspIdentifier, RspEntry>, IService
{
protected override void Initialize()
{
Identifier = new RspIdentifier(1, EquipSlot.Body);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = ExpandedEqpFile.GetDefault(metaManager, Identifier.SetId);
protected override void DrawNew()
{ }
protected override void DrawEntry(RspIdentifier identifier, RspEntry entry)
{ }
protected override IEnumerable<(RspIdentifier, RspEntry)> Enumerate()
=> editor.MetaEditor.Eqp.Select(kvp => (kvp.Key, kvp.Value.ToEntry(kvp.Key.Slot)));
}
public sealed class GlobalEqpMetaDrawer(ModEditor editor) : MetaDrawer<GlobalEqpManipulation, byte>, IService
{
protected override void Initialize()
{
Identifier = new EqpIdentifier(1, EquipSlot.Body);
UpdateEntry();
}
private void UpdateEntry()
=> Entry = ExpandedEqpFile.GetDefault(metaManager, Identifier.SetId);
protected override void DrawNew()
{ }
protected override void DrawEntry(GlobalEqpManipulation identifier, byte _)
{ }
protected override IEnumerable<(GlobalEqpManipulation, byte)> Enumerate()
=> editor.MetaEditor.Eqp.Select(kvp => (kvp.Key, kvp.Value.ToEntry(kvp.Key.Slot)));
}
#endif

View file

@ -95,7 +95,7 @@ public partial class ModEditWindow
task.ContinueWith(t => { GamePaths = FinalizeIo(t); }, TaskScheduler.Default);
}
private EstManipulation[] GetCurrentEstManipulations()
private KeyValuePair<EstIdentifier, EstEntry>[] GetCurrentEstManipulations()
{
var mod = _edit._editor.Mod;
var option = _edit._editor.Option;
@ -106,9 +106,7 @@ public partial class ModEditWindow
return mod.AllDataContainers
.Where(subMod => subMod != option)
.Prepend(option)
.SelectMany(subMod => subMod.Manipulations)
.Where(manipulation => manipulation.ManipulationType is MetaManipulation.Type.Est)
.Select(manipulation => manipulation.Est)
.SelectMany(subMod => subMod.Manipulations.Est)
.ToArray();
}

View file

@ -25,6 +25,7 @@ using Penumbra.Mods.SubMods;
using Penumbra.Services;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.UI.AdvancedWindow.Meta;
using Penumbra.UI.Classes;
using Penumbra.Util;
using MdlMaterialEditor = Penumbra.Mods.Editor.MdlMaterialEditor;
@ -586,7 +587,7 @@ public partial class ModEditWindow : Window, IDisposable
StainService stainService, ActiveCollections activeCollections, ModMergeTab modMergeTab,
CommunicatorService communicator, TextureManager textures, ModelManager models, IDragDropManager dragDropManager,
ResourceTreeViewerFactory resourceTreeViewerFactory, ObjectManager objects, IFramework framework,
CharacterBaseDestructor characterBaseDestructor)
CharacterBaseDestructor characterBaseDestructor, MetaDrawers metaDrawers)
: base(WindowBaseLabel)
{
_performance = performance;
@ -606,6 +607,7 @@ public partial class ModEditWindow : Window, IDisposable
_objects = objects;
_framework = framework;
_characterBaseDestructor = characterBaseDestructor;
_metaDrawers = metaDrawers;
_materialTab = new FileEditor<MtrlTab>(this, _communicator, gameData, config, _editor.Compactor, _fileDialog, "Materials", ".mtrl",
() => PopulateIsOnPlayer(_editor.Files.Mtrl, ResourceType.Mtrl), DrawMaterialPanel, () => Mod?.ModPath.FullName ?? string.Empty,
(bytes, path, writable) => new MtrlTab(this, new MtrlFile(bytes), path, writable));

View file

@ -120,9 +120,9 @@ public class ModPanelConflictsTab(CollectionManager collectionManager, ModFileSy
{
var _ = data switch
{
Utf8GamePath p => ImGuiNative.igSelectable_Bool(p.Path.Path, 0, ImGuiSelectableFlags.None, Vector2.Zero) > 0,
MetaManipulation m => ImGui.Selectable(m.Manipulation?.ToString() ?? string.Empty),
_ => false,
Utf8GamePath p => ImGuiNative.igSelectable_Bool(p.Path.Path, 0, ImGuiSelectableFlags.None, Vector2.Zero) > 0,
IMetaIdentifier m => ImGui.Selectable(m.ToString()),
_ => false,
};
}
}

View file

@ -98,7 +98,7 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
// Filters mean we can not use the known counts.
if (hasFilters)
{
var it2 = m.Select(p => (p.Key.ToString(), p.Value.Name));
var it2 = m.IdentifierSources.Select(p => (p.Item1.ToString(), p.Item2.Name));
if (stop >= 0)
{
ImGuiClip.DrawEndDummy(stop + it2.Count(CheckFilters), height);
@ -117,7 +117,7 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
}
else
{
stop = ImGuiClip.ClippedDraw(m, skips, DrawLine, m.Count, ~stop);
stop = ImGuiClip.ClippedDraw(m.IdentifierSources, skips, DrawLine, m.Count, ~stop);
ImGuiClip.DrawEndDummy(stop, height);
}
}
@ -152,11 +152,11 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
ImGui.TableNextColumn();
ImGuiUtil.PrintIcon(FontAwesomeIcon.LongArrowAltLeft);
ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(name);
ImGuiUtil.CopyOnClickSelectable(name.Text);
}
/// <summary> Draw a line for a unfiltered/unconverted manipulation and mod-index pair. </summary>
private static void DrawLine(KeyValuePair<MetaManipulation, IMod> pair)
private static void DrawLine((IMetaIdentifier, IMod) pair)
{
var (manipulation, mod) = pair;
ImGui.TableNextColumn();
@ -165,7 +165,7 @@ public class EffectiveTab(CollectionManager collectionManager, CollectionSelectH
ImGui.TableNextColumn();
ImGuiUtil.PrintIcon(FontAwesomeIcon.LongArrowAltLeft);
ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(mod.Name);
ImGuiUtil.CopyOnClickSelectable(mod.Name.Text);
}
/// <summary> Check filters for file replacements. </summary>