mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 05:04:15 +01:00
Add some early support for IMC groups.
This commit is contained in:
parent
d47d31b665
commit
df6eb3fdd2
15 changed files with 360 additions and 146 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit 866389b3988d9c4926a786f6c78ac9d5265591ac
|
||||
Subproject commit 5028fba767ca8febd75a1a5ebc312bd354efc81b
|
||||
|
|
@ -36,7 +36,7 @@ public readonly struct ImcCache : IDisposable
|
|||
|
||||
public bool ApplyMod(MetaFileManager manager, ModCollection collection, ImcManipulation manip)
|
||||
{
|
||||
if (!manip.Validate())
|
||||
if (!manip.Validate(true))
|
||||
return false;
|
||||
|
||||
var idx = _imcManipulations.FindIndex(p => p.Item1.Equals(manip));
|
||||
|
|
@ -77,7 +77,7 @@ public readonly struct ImcCache : IDisposable
|
|||
|
||||
public bool RevertMod(MetaFileManager manager, ModCollection collection, ImcManipulation m)
|
||||
{
|
||||
if (!m.Validate())
|
||||
if (!m.Validate(false))
|
||||
return false;
|
||||
|
||||
var idx = _imcManipulations.FindIndex(p => p.Item1.Equals(m));
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ public partial class TexToolsMeta
|
|||
{
|
||||
var imc = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, i, manip.EquipSlot,
|
||||
value);
|
||||
if (imc.Validate())
|
||||
if (imc.Validate(true))
|
||||
MetaManipulations.Add(imc);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
|
|||
public bool Apply(ImcFile file)
|
||||
=> file.SetEntry(ImcFile.PartIndex(EquipSlot), Variant.Id, Entry);
|
||||
|
||||
public bool Validate()
|
||||
public bool Validate(bool withMaterial)
|
||||
{
|
||||
switch (ObjectType)
|
||||
{
|
||||
|
|
@ -178,7 +178,7 @@ public readonly struct ImcManipulation : IMetaManipulation<ImcManipulation>
|
|||
break;
|
||||
}
|
||||
|
||||
if (Entry.MaterialId == 0)
|
||||
if (withMaterial && Entry.MaterialId == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
return;
|
||||
case ImcManipulation m:
|
||||
Imc = m;
|
||||
ManipulationType = m.Validate() ? Type.Imc : Type.Unknown;
|
||||
ManipulationType = m.Validate(true) ? Type.Imc : Type.Unknown;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ public readonly struct MetaManipulation : IEquatable<MetaManipulation>, ICompara
|
|||
{
|
||||
return ManipulationType switch
|
||||
{
|
||||
Type.Imc => Imc.Validate(),
|
||||
Type.Imc => Imc.Validate(true),
|
||||
Type.Eqdp => Eqdp.Validate(),
|
||||
Type.Eqp => Eqp.Validate(),
|
||||
Type.Est => Est.Validate(),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
|
@ -157,4 +160,44 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
|
||||
public (int Redirections, int Swaps, int Manips) GetCounts()
|
||||
=> (0, 0, 1);
|
||||
|
||||
public static ImcModGroup? Load(Mod mod, JObject json)
|
||||
{
|
||||
var options = json["Options"];
|
||||
var ret = new ImcModGroup(mod)
|
||||
{
|
||||
Name = json[nameof(Name)]?.ToObject<string>() ?? string.Empty,
|
||||
Description = json[nameof(Description)]?.ToObject<string>() ?? string.Empty,
|
||||
Priority = json[nameof(Priority)]?.ToObject<ModPriority>() ?? ModPriority.Default,
|
||||
DefaultSettings = json[nameof(DefaultSettings)]?.ToObject<Setting>() ?? Setting.Zero,
|
||||
ObjectType = json[nameof(ObjectType)]?.ToObject<ObjectType>() ?? ObjectType.Unknown,
|
||||
BodySlot = json[nameof(BodySlot)]?.ToObject<BodySlot>() ?? BodySlot.Unknown,
|
||||
EquipSlot = json[nameof(EquipSlot)]?.ToObject<EquipSlot>() ?? EquipSlot.Unknown,
|
||||
PrimaryId = new PrimaryId(json[nameof(PrimaryId)]?.ToObject<ushort>() ?? 0),
|
||||
SecondaryId = new SecondaryId(json[nameof(SecondaryId)]?.ToObject<ushort>() ?? 0),
|
||||
Variant = new Variant(json[nameof(Variant)]?.ToObject<byte>() ?? 0),
|
||||
CanBeDisabled = json[nameof(CanBeDisabled)]?.ToObject<bool>() ?? false,
|
||||
DefaultEntry = json[nameof(DefaultEntry)]?.ToObject<ImcEntry>() ?? new ImcEntry(),
|
||||
};
|
||||
if (ret.Name.Length == 0)
|
||||
return null;
|
||||
|
||||
if (options != null)
|
||||
foreach (var child in options.Children())
|
||||
{
|
||||
var subMod = new ImcSubMod(ret, child);
|
||||
ret.OptionData.Add(subMod);
|
||||
}
|
||||
|
||||
if (!new ImcManipulation(ret.ObjectType, ret.BodySlot, ret.PrimaryId, ret.SecondaryId.Id, ret.Variant.Id, ret.EquipSlot,
|
||||
ret.DefaultEntry).Validate(true))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage($"Could not add IMC group because the associated IMC Entry is invalid.",
|
||||
NotificationType.Warning);
|
||||
return null;
|
||||
}
|
||||
|
||||
ret.DefaultSettings = ret.FixSetting(ret.DefaultSettings);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Groups;
|
||||
using Penumbra.Mods.Settings;
|
||||
using Penumbra.Mods.SubMods;
|
||||
|
|
@ -11,13 +12,43 @@ namespace Penumbra.Mods.Manager.OptionEditor;
|
|||
public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveService saveService, Configuration config)
|
||||
: ModOptionEditor<ImcModGroup, ImcSubMod>(communicator, saveService, config), IService
|
||||
{
|
||||
/// <summary> Add a new, empty imc group with the given manipulation data. </summary>
|
||||
public ImcModGroup? AddModGroup(Mod mod, string newName, ImcManipulation manip, SaveType saveType = SaveType.ImmediateSync)
|
||||
{
|
||||
if (!ModGroupEditor.VerifyFileName(mod, null, newName, true))
|
||||
return null;
|
||||
|
||||
var maxPriority = mod.Groups.Count == 0 ? ModPriority.Default : mod.Groups.Max(o => o.Priority) + 1;
|
||||
var group = CreateGroup(mod, newName, manip, maxPriority);
|
||||
mod.Groups.Add(group);
|
||||
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupAdded, mod, group, null, null, -1);
|
||||
return group;
|
||||
}
|
||||
|
||||
protected override ImcModGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync)
|
||||
=> new(mod)
|
||||
{
|
||||
Name = newName,
|
||||
Name = newName,
|
||||
Priority = priority,
|
||||
};
|
||||
|
||||
|
||||
private static ImcModGroup CreateGroup(Mod mod, string newName, ImcManipulation manip, ModPriority priority,
|
||||
SaveType saveType = SaveType.ImmediateSync)
|
||||
=> new(mod)
|
||||
{
|
||||
Name = newName,
|
||||
Priority = priority,
|
||||
ObjectType = manip.ObjectType,
|
||||
EquipSlot = manip.EquipSlot,
|
||||
BodySlot = manip.BodySlot,
|
||||
PrimaryId = manip.PrimaryId,
|
||||
SecondaryId = manip.SecondaryId.Id,
|
||||
Variant = manip.Variant,
|
||||
DefaultEntry = manip.Entry,
|
||||
};
|
||||
|
||||
protected override ImcSubMod? CloneOption(ImcModGroup group, IModOption option)
|
||||
=> null;
|
||||
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ public class ModGroupEditor(
|
|||
{
|
||||
GroupType.Single => SingleEditor.AddModGroup(mod, newName, saveType),
|
||||
GroupType.Multi => MultiEditor.AddModGroup(mod, newName, saveType),
|
||||
GroupType.Imc => ImcEditor.AddModGroup(mod, newName, saveType),
|
||||
GroupType.Imc => ImcEditor.AddModGroup(mod, newName, default, saveType),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -434,6 +434,7 @@ public partial class ModCreator(
|
|||
{
|
||||
case GroupType.Multi: return MultiModGroup.Load(mod, json);
|
||||
case GroupType.Single: return SingleModGroup.Load(mod, json);
|
||||
case GroupType.Imc: return ImcModGroup.Load(mod, json);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.Mods.Groups;
|
||||
|
||||
namespace Penumbra.Mods.SubMods;
|
||||
|
|
@ -6,6 +7,12 @@ public class ImcSubMod(ImcModGroup group) : IModOption
|
|||
{
|
||||
public readonly ImcModGroup Group = group;
|
||||
|
||||
public ImcSubMod(ImcModGroup group, JToken json)
|
||||
: this(group)
|
||||
{
|
||||
SubMod.LoadOptionData(json, this);
|
||||
}
|
||||
|
||||
public Mod Mod
|
||||
=> Group.Mod;
|
||||
|
||||
|
|
|
|||
|
|
@ -639,11 +639,11 @@ public class ItemSwapTab : IDisposable, ITab
|
|||
ImGui.TextUnformatted(text);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
_dirty |= Combos.Gender("##Gender", InputWidth, _currentGender, out _currentGender);
|
||||
_dirty |= Combos.Gender("##Gender", _currentGender, out _currentGender, InputWidth);
|
||||
if (drawRace == 1)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
_dirty |= Combos.Race("##Race", InputWidth, _currentRace, out _currentRace);
|
||||
_dirty |= Combos.Race("##Race", _currentRace, out _currentRace, InputWidth);
|
||||
}
|
||||
else if (drawRace == 2)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Penumbra.Meta.Files;
|
|||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Editor;
|
||||
using Penumbra.UI.Classes;
|
||||
using Penumbra.UI.ModsTab;
|
||||
|
||||
namespace Penumbra.UI.AdvancedWindow;
|
||||
|
||||
|
|
@ -145,7 +146,7 @@ public partial class ModEditWindow
|
|||
ImGuiUtil.HoverTooltip(ModelSetIdTooltip);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (Combos.EqpEquipSlot("##eqpSlot", 100, _new.Slot, out var slot))
|
||||
if (Combos.EqpEquipSlot("##eqpSlot", _new.Slot, out var slot))
|
||||
_new = new EqpManipulation(ExpandedEqpFile.GetDefault(metaFileManager, setId), slot, _new.SetId);
|
||||
|
||||
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
|
||||
|
|
@ -351,90 +352,31 @@ public partial class ModEditWindow
|
|||
|
||||
// Identifier
|
||||
ImGui.TableNextColumn();
|
||||
if (Combos.ImcType("##imcType", _new.ObjectType, out var type))
|
||||
{
|
||||
var equipSlot = type switch
|
||||
{
|
||||
ObjectType.Equipment => _new.EquipSlot.IsEquipment() ? _new.EquipSlot : EquipSlot.Head,
|
||||
ObjectType.DemiHuman => _new.EquipSlot.IsEquipment() ? _new.EquipSlot : EquipSlot.Head,
|
||||
ObjectType.Accessory => _new.EquipSlot.IsAccessory() ? _new.EquipSlot : EquipSlot.Ears,
|
||||
_ => EquipSlot.Unknown,
|
||||
};
|
||||
_new = new ImcManipulation(type, _new.BodySlot, _new.PrimaryId, _new.SecondaryId == 0 ? (ushort)1 : _new.SecondaryId,
|
||||
_new.Variant.Id, equipSlot, _new.Entry);
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip(ObjectTypeTooltip);
|
||||
var change = MetaManipulationDrawer.DrawObjectType(ref _new);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (IdInput("##imcId", IdWidth, _new.PrimaryId.Id, out var setId, 0, ushort.MaxValue, _new.PrimaryId <= 1))
|
||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, setId, _new.SecondaryId, _new.Variant.Id, _new.EquipSlot, _new.Entry)
|
||||
.Copy(GetDefault(metaFileManager, _new)
|
||||
?? new ImcEntry());
|
||||
|
||||
ImGuiUtil.HoverTooltip(PrimaryIdTooltip);
|
||||
|
||||
change |= MetaManipulationDrawer.DrawPrimaryId(ref _new);
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
|
||||
new Vector2(3 * UiHelpers.Scale, ImGui.GetStyle().ItemSpacing.Y));
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
// Equipment and accessories are slightly different imcs than other types.
|
||||
if (_new.ObjectType is ObjectType.Equipment)
|
||||
{
|
||||
if (Combos.EqpEquipSlot("##imcSlot", 100, _new.EquipSlot, out var slot))
|
||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant.Id, slot,
|
||||
_new.Entry)
|
||||
.Copy(GetDefault(metaFileManager, _new)
|
||||
?? new ImcEntry());
|
||||
|
||||
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
|
||||
}
|
||||
else if (_new.ObjectType is ObjectType.Accessory)
|
||||
{
|
||||
if (Combos.AccessorySlot("##imcSlot", _new.EquipSlot, out var slot))
|
||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant.Id, slot,
|
||||
_new.Entry)
|
||||
.Copy(GetDefault(metaFileManager, _new)
|
||||
?? new ImcEntry());
|
||||
|
||||
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
|
||||
}
|
||||
if (_new.ObjectType is ObjectType.Equipment or ObjectType.Accessory)
|
||||
change |= MetaManipulationDrawer.DrawSlot(ref _new);
|
||||
else
|
||||
{
|
||||
if (IdInput("##imcId2", 100 * UiHelpers.Scale, _new.SecondaryId.Id, out var setId2, 0, ushort.MaxValue, false))
|
||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, setId2, _new.Variant.Id, _new.EquipSlot,
|
||||
_new.Entry)
|
||||
.Copy(GetDefault(metaFileManager, _new)
|
||||
?? new ImcEntry());
|
||||
|
||||
ImGuiUtil.HoverTooltip(SecondaryIdTooltip);
|
||||
}
|
||||
change |= MetaManipulationDrawer.DrawSecondaryId(ref _new);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (IdInput("##imcVariant", SmallIdWidth, _new.Variant.Id, out var variant, 0, byte.MaxValue, false))
|
||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, variant, _new.EquipSlot,
|
||||
_new.Entry).Copy(GetDefault(metaFileManager, _new)
|
||||
?? new ImcEntry());
|
||||
|
||||
ImGuiUtil.HoverTooltip(VariantIdTooltip);
|
||||
change |= MetaManipulationDrawer.DrawVariant(ref _new);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (_new.ObjectType is ObjectType.DemiHuman)
|
||||
{
|
||||
if (Combos.EqpEquipSlot("##imcSlot", 70, _new.EquipSlot, out var slot))
|
||||
_new = new ImcManipulation(_new.ObjectType, _new.BodySlot, _new.PrimaryId, _new.SecondaryId, _new.Variant.Id, slot,
|
||||
_new.Entry)
|
||||
.Copy(GetDefault(metaFileManager, _new)
|
||||
?? new ImcEntry());
|
||||
|
||||
ImGuiUtil.HoverTooltip(EquipSlotTooltip);
|
||||
}
|
||||
change |= MetaManipulationDrawer.DrawSlot(ref _new, 70);
|
||||
else
|
||||
{
|
||||
ImGui.Dummy(new Vector2(70 * UiHelpers.Scale, 0));
|
||||
}
|
||||
|
||||
|
||||
if (change)
|
||||
_new = _new.Copy(GetDefault(metaFileManager, _new) ?? new ImcEntry());
|
||||
// Values
|
||||
using var disabled = ImRaii.Disabled();
|
||||
ImGui.TableNextColumn();
|
||||
|
|
|
|||
|
|
@ -8,41 +8,35 @@ namespace Penumbra.UI.Classes;
|
|||
public static class Combos
|
||||
{
|
||||
// Different combos to use with enums.
|
||||
public static bool Race(string label, ModelRace current, out ModelRace race)
|
||||
=> Race(label, 100, current, out race);
|
||||
|
||||
public static bool Race(string label, float unscaledWidth, ModelRace current, out ModelRace race)
|
||||
public static bool Race(string label, ModelRace current, out ModelRace race, float unscaledWidth = 100)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out race, RaceEnumExtensions.ToName, 1);
|
||||
|
||||
public static bool Gender(string label, Gender current, out Gender gender)
|
||||
=> Gender(label, 120, current, out gender);
|
||||
public static bool Gender(string label, Gender current, out Gender gender, float unscaledWidth = 120)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth, current, out gender, RaceEnumExtensions.ToName, 1);
|
||||
|
||||
public static bool Gender(string label, float unscaledWidth, Gender current, out Gender gender)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out gender, RaceEnumExtensions.ToName, 1);
|
||||
|
||||
public static bool EqdpEquipSlot(string label, EquipSlot current, out EquipSlot slot)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, 100 * UiHelpers.Scale, current, out slot, EquipSlotExtensions.EqdpSlots,
|
||||
public static bool EqdpEquipSlot(string label, EquipSlot current, out EquipSlot slot, float unscaledWidth = 100)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out slot, EquipSlotExtensions.EqdpSlots,
|
||||
EquipSlotExtensions.ToName);
|
||||
|
||||
public static bool EqpEquipSlot(string label, float width, EquipSlot current, out EquipSlot slot)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, width * UiHelpers.Scale, current, out slot, EquipSlotExtensions.EquipmentSlots,
|
||||
public static bool EqpEquipSlot(string label, EquipSlot current, out EquipSlot slot, float unscaledWidth = 100)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out slot, EquipSlotExtensions.EquipmentSlots,
|
||||
EquipSlotExtensions.ToName);
|
||||
|
||||
public static bool AccessorySlot(string label, EquipSlot current, out EquipSlot slot)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, 100 * UiHelpers.Scale, current, out slot, EquipSlotExtensions.AccessorySlots,
|
||||
public static bool AccessorySlot(string label, EquipSlot current, out EquipSlot slot, float unscaledWidth = 100)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out slot, EquipSlotExtensions.AccessorySlots,
|
||||
EquipSlotExtensions.ToName);
|
||||
|
||||
public static bool SubRace(string label, SubRace current, out SubRace subRace)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, 150 * UiHelpers.Scale, current, out subRace, RaceEnumExtensions.ToName, 1);
|
||||
public static bool SubRace(string label, SubRace current, out SubRace subRace, float unscaledWidth = 150)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out subRace, RaceEnumExtensions.ToName, 1);
|
||||
|
||||
public static bool RspAttribute(string label, RspAttribute current, out RspAttribute attribute)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, 200 * UiHelpers.Scale, current, out attribute,
|
||||
public static bool RspAttribute(string label, RspAttribute current, out RspAttribute attribute, float unscaledWidth = 200)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out attribute,
|
||||
RspAttributeExtensions.ToFullString, 0, 1);
|
||||
|
||||
public static bool EstSlot(string label, EstManipulation.EstType current, out EstManipulation.EstType attribute)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, 200 * UiHelpers.Scale, current, out attribute);
|
||||
public static bool EstSlot(string label, EstManipulation.EstType current, out EstManipulation.EstType attribute, float unscaledWidth = 200)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out attribute);
|
||||
|
||||
public static bool ImcType(string label, ObjectType current, out ObjectType type)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, 110 * UiHelpers.Scale, current, out type, ObjectTypeExtensions.ValidImcTypes,
|
||||
public static bool ImcType(string label, ObjectType current, out ObjectType type, float unscaledWidth = 110)
|
||||
=> ImGuiUtil.GenericEnumCombo(label, unscaledWidth * UiHelpers.Scale, current, out type, ObjectTypeExtensions.ValidImcTypes,
|
||||
ObjectTypeExtensions.ToName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.Utility;
|
||||
using ImGuiNET;
|
||||
using Lumina.Data.Files;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Text;
|
||||
using OtterGui.Text.EndObjects;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Groups;
|
||||
using Penumbra.Mods.Manager;
|
||||
|
|
@ -15,9 +22,236 @@ using Penumbra.Mods.Settings;
|
|||
using Penumbra.Mods.SubMods;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.UI.Classes;
|
||||
using ImcFile = Penumbra.Meta.Files.ImcFile;
|
||||
|
||||
namespace Penumbra.UI.ModsTab;
|
||||
|
||||
public static class MetaManipulationDrawer
|
||||
{
|
||||
public static bool DrawObjectType(ref ImcManipulation manip, float width = 110)
|
||||
{
|
||||
var ret = Combos.ImcType("##imcType", manip.ObjectType, out var type, width);
|
||||
ImUtf8.HoverTooltip("Object Type"u8);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
var equipSlot = type switch
|
||||
{
|
||||
ObjectType.Equipment => manip.EquipSlot.IsEquipment() ? manip.EquipSlot : EquipSlot.Head,
|
||||
ObjectType.DemiHuman => manip.EquipSlot.IsEquipment() ? manip.EquipSlot : EquipSlot.Head,
|
||||
ObjectType.Accessory => manip.EquipSlot.IsAccessory() ? manip.EquipSlot : EquipSlot.Ears,
|
||||
_ => EquipSlot.Unknown,
|
||||
};
|
||||
manip = new ImcManipulation(type, manip.BodySlot, manip.PrimaryId, manip.SecondaryId == 0 ? 1 : manip.SecondaryId,
|
||||
manip.Variant.Id, equipSlot, manip.Entry);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static bool DrawPrimaryId(ref ImcManipulation manip, float unscaledWidth = 80)
|
||||
{
|
||||
var ret = IdInput("##imcPrimaryId"u8, unscaledWidth, manip.PrimaryId.Id, out var newId, 0, ushort.MaxValue,
|
||||
manip.PrimaryId.Id <= 1);
|
||||
ImUtf8.HoverTooltip("Primary ID - You can usually find this as the 'x####' part of an item path.\n"u8
|
||||
+ "This should generally not be left <= 1 unless you explicitly want that."u8);
|
||||
if (ret)
|
||||
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, newId, manip.SecondaryId, manip.Variant.Id, manip.EquipSlot,
|
||||
manip.Entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static bool DrawSecondaryId(ref ImcManipulation manip, float unscaledWidth = 100)
|
||||
{
|
||||
var ret = IdInput("##imcSecondaryId"u8, unscaledWidth, manip.SecondaryId.Id, out var newId, 0, ushort.MaxValue, false);
|
||||
ImUtf8.HoverTooltip("Secondary ID"u8);
|
||||
if (ret)
|
||||
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, newId, manip.Variant.Id, manip.EquipSlot,
|
||||
manip.Entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static bool DrawVariant(ref ImcManipulation manip, float unscaledWidth = 45)
|
||||
{
|
||||
var ret = IdInput("##imcVariant"u8, unscaledWidth, manip.Variant.Id, out var newId, 0, byte.MaxValue, false);
|
||||
ImUtf8.HoverTooltip("Variant ID"u8);
|
||||
if (ret)
|
||||
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, (byte)newId, manip.EquipSlot,
|
||||
manip.Entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static bool DrawSlot(ref ImcManipulation manip, float unscaledWidth = 100)
|
||||
{
|
||||
bool ret;
|
||||
EquipSlot slot;
|
||||
switch (manip.ObjectType)
|
||||
{
|
||||
case ObjectType.Equipment:
|
||||
case ObjectType.DemiHuman:
|
||||
ret = Combos.EqpEquipSlot("##slot", manip.EquipSlot, out slot, unscaledWidth);
|
||||
break;
|
||||
case ObjectType.Accessory:
|
||||
ret = Combos.AccessorySlot("##slot", manip.EquipSlot, out slot, unscaledWidth);
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
ImUtf8.HoverTooltip("Equip Slot"u8);
|
||||
if (ret)
|
||||
manip = new ImcManipulation(manip.ObjectType, manip.BodySlot, manip.PrimaryId, manip.SecondaryId, manip.Variant.Id, slot,
|
||||
manip.Entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// A number input for ids with a optional max id of given width.
|
||||
// Returns true if newId changed against currentId.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public class AddGroupDrawer : IUiService
|
||||
{
|
||||
private string _groupName = string.Empty;
|
||||
private bool _groupNameValid = false;
|
||||
|
||||
private ImcManipulation _imcManip = new(EquipSlot.Head, 1, 1, new ImcEntry());
|
||||
private ImcEntry _defaultEntry;
|
||||
private bool _imcFileExists;
|
||||
private bool _entryExists;
|
||||
private bool _entryInvalid;
|
||||
private readonly MetaFileManager _metaManager;
|
||||
private readonly ModManager _modManager;
|
||||
|
||||
public AddGroupDrawer(MetaFileManager metaManager, ModManager modManager)
|
||||
{
|
||||
_metaManager = metaManager;
|
||||
_modManager = modManager;
|
||||
UpdateEntry();
|
||||
}
|
||||
|
||||
public void Draw(Mod mod, float width)
|
||||
{
|
||||
DrawBasicGroups(mod, width);
|
||||
DrawImcData(mod, width);
|
||||
}
|
||||
|
||||
private void UpdateEntry()
|
||||
{
|
||||
try
|
||||
{
|
||||
_defaultEntry = ImcFile.GetDefault(_metaManager, _imcManip.GamePath(), _imcManip.EquipSlot, _imcManip.Variant,
|
||||
out _entryExists);
|
||||
_imcFileExists = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_defaultEntry = new ImcEntry();
|
||||
_imcFileExists = false;
|
||||
_entryExists = false;
|
||||
}
|
||||
|
||||
_imcManip = _imcManip.Copy(_entryExists ? _defaultEntry : new ImcEntry());
|
||||
_entryInvalid = !_imcManip.Validate(true);
|
||||
}
|
||||
|
||||
|
||||
private void DrawBasicGroups(Mod mod, float width)
|
||||
{
|
||||
ImGui.SetNextItemWidth(width);
|
||||
if (ImUtf8.InputText("##name"u8, ref _groupName, "Enter New Name..."u8))
|
||||
_groupNameValid = ModGroupEditor.VerifyFileName(mod, null, _groupName, false);
|
||||
|
||||
var buttonWidth = new Vector2((width - ImUtf8.ItemInnerSpacing.X) / 2, 0);
|
||||
if (ImUtf8.ButtonEx("Add Single Group"u8, _groupNameValid
|
||||
? "Add a new single selection option group to this mod."u8
|
||||
: "Can not add a new group of this name."u8,
|
||||
buttonWidth, !_groupNameValid))
|
||||
{
|
||||
_modManager.OptionEditor.AddModGroup(mod, GroupType.Single, _groupName);
|
||||
_groupName = string.Empty;
|
||||
_groupNameValid = false;
|
||||
}
|
||||
|
||||
ImUtf8.SameLineInner();
|
||||
if (ImUtf8.ButtonEx("Add Multi Group"u8, _groupNameValid
|
||||
? "Add a new multi selection option group to this mod."u8
|
||||
: "Can not add a new group of this name."u8,
|
||||
buttonWidth, !_groupNameValid))
|
||||
{
|
||||
_modManager.OptionEditor.AddModGroup(mod, GroupType.Multi, _groupName);
|
||||
_groupName = string.Empty;
|
||||
_groupNameValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawImcData(Mod mod, float width)
|
||||
{
|
||||
var halfWidth = (width - ImUtf8.ItemInnerSpacing.X) / 2 / ImUtf8.GlobalScale;
|
||||
var change = MetaManipulationDrawer.DrawObjectType(ref _imcManip, halfWidth);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawPrimaryId(ref _imcManip, halfWidth);
|
||||
if (_imcManip.ObjectType is ObjectType.Weapon or ObjectType.Monster)
|
||||
{
|
||||
change |= MetaManipulationDrawer.DrawSecondaryId(ref _imcManip, halfWidth);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawVariant(ref _imcManip, halfWidth);
|
||||
}
|
||||
else if (_imcManip.ObjectType is ObjectType.DemiHuman)
|
||||
{
|
||||
var quarterWidth = (halfWidth - ImUtf8.ItemInnerSpacing.X / ImUtf8.GlobalScale) / 2;
|
||||
change |= MetaManipulationDrawer.DrawSecondaryId(ref _imcManip, halfWidth);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawSlot(ref _imcManip, quarterWidth);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawVariant(ref _imcManip, quarterWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
change |= MetaManipulationDrawer.DrawSlot(ref _imcManip, halfWidth);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawVariant(ref _imcManip, halfWidth);
|
||||
}
|
||||
|
||||
if (change)
|
||||
UpdateEntry();
|
||||
|
||||
var buttonWidth = new Vector2(halfWidth * ImUtf8.GlobalScale, 0);
|
||||
|
||||
if (ImUtf8.ButtonEx("Add IMC Group"u8, !_groupNameValid
|
||||
? "Can not add a new group of this name."u8
|
||||
: _entryInvalid ?
|
||||
"The associated IMC entry is invalid."u8
|
||||
: "Add a new multi selection option group to this mod."u8,
|
||||
buttonWidth, !_groupNameValid || _entryInvalid))
|
||||
{
|
||||
_modManager.OptionEditor.ImcEditor.AddModGroup(mod, _groupName, _imcManip);
|
||||
_groupName = string.Empty;
|
||||
_groupNameValid = false;
|
||||
}
|
||||
|
||||
if (_entryInvalid)
|
||||
{
|
||||
ImUtf8.SameLineInner();
|
||||
var text = _imcFileExists
|
||||
? "IMC Entry Does Not Exist"
|
||||
: "IMC File Does Not Exist";
|
||||
ImGuiUtil.DrawTextButton(text, buttonWidth, Colors.PressEnterWarningBg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ModGroupEditDrawer(
|
||||
ModManager modManager,
|
||||
Configuration config,
|
||||
|
|
@ -267,9 +501,7 @@ public sealed class ModGroupEditDrawer(
|
|||
}
|
||||
|
||||
private void DrawImcGroup(ImcModGroup group)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
{ }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void DrawOptionPosition(IModGroup group, IModOption option, int optionIdx)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ public class ModPanelEditTab(
|
|||
Configuration config,
|
||||
PredefinedTagManager predefinedTagManager,
|
||||
ModGroupEditDrawer groupEditDrawer,
|
||||
DescriptionEditPopup descriptionPopup)
|
||||
DescriptionEditPopup descriptionPopup,
|
||||
AddGroupDrawer addGroupDrawer)
|
||||
: ITab
|
||||
{
|
||||
private readonly TagButtons _modTags = new();
|
||||
|
|
@ -75,7 +76,7 @@ public class ModPanelEditTab(
|
|||
selector.Selected!);
|
||||
|
||||
UiHelpers.DefaultLineSpace();
|
||||
AddOptionGroup.Draw(filenames, modManager, _mod, config.ReplaceNonAsciiOnImport);
|
||||
addGroupDrawer.Draw(_mod, UiHelpers.InputTextWidth.X);
|
||||
UiHelpers.DefaultLineSpace();
|
||||
|
||||
groupEditDrawer.Draw(_mod);
|
||||
|
|
@ -84,7 +85,6 @@ public class ModPanelEditTab(
|
|||
|
||||
public void Reset()
|
||||
{
|
||||
AddOptionGroup.Reset();
|
||||
MoveDirectory.Reset();
|
||||
Input.Reset();
|
||||
}
|
||||
|
|
@ -202,42 +202,6 @@ public class ModPanelEditTab(
|
|||
Process.Start(new ProcessStartInfo(filenames.ModMetaPath(_mod)) { UseShellExecute = true });
|
||||
}
|
||||
|
||||
/// <summary> Text input to add a new option group at the end of the current groups. </summary>
|
||||
private static class AddOptionGroup
|
||||
{
|
||||
private static string _newGroupName = string.Empty;
|
||||
|
||||
public static void Reset()
|
||||
=> _newGroupName = string.Empty;
|
||||
|
||||
public static void Draw(FilenameService filenames, ModManager modManager, Mod mod, bool onlyAscii)
|
||||
{
|
||||
using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(UiHelpers.ScaleX3));
|
||||
ImGui.SetNextItemWidth(UiHelpers.InputTextMinusButton3);
|
||||
ImGui.InputTextWithHint("##newGroup", "Add new option group...", ref _newGroupName, 256);
|
||||
ImGui.SameLine();
|
||||
var defaultFile = filenames.OptionGroupFile(mod, -1, onlyAscii);
|
||||
var fileExists = File.Exists(defaultFile);
|
||||
var tt = fileExists
|
||||
? "Open the default option json file in the text editor of your choice."
|
||||
: "The default option json file does not exist.";
|
||||
if (ImGuiUtil.DrawDisabledButton($"{FontAwesomeIcon.FileExport.ToIconString()}##defaultFile", UiHelpers.IconButtonSize, tt,
|
||||
!fileExists, true))
|
||||
Process.Start(new ProcessStartInfo(defaultFile) { UseShellExecute = true });
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
var nameValid = ModGroupEditor.VerifyFileName(mod, null, _newGroupName, false);
|
||||
tt = nameValid ? "Add new option group to the mod." : "Can not add a group of this name.";
|
||||
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), UiHelpers.IconButtonSize,
|
||||
tt, !nameValid, true))
|
||||
return;
|
||||
|
||||
modManager.OptionEditor.SingleEditor.AddModGroup(mod, _newGroupName);
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> A text input for the new directory name and a button to apply the move. </summary>
|
||||
private static class MoveDirectory
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue