mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Improvements.
This commit is contained in:
parent
df6eb3fdd2
commit
bb56faa288
9 changed files with 498 additions and 170 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit 5028fba767ca8febd75a1a5ebc312bd354efc81b
|
||||
Subproject commit 462acb87099650019996e4306d18cc70f76ca576
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 595ac5722c9c400bea36110503ed2ae7b02d1489
|
||||
Subproject commit 5fa4d0e7972423b73f8cf569bb2bfbeddd825c8a
|
||||
|
|
@ -14,8 +14,7 @@ namespace Penumbra.Mods.Groups;
|
|||
|
||||
public class ImcModGroup(Mod mod) : IModGroup
|
||||
{
|
||||
public const int DisabledIndex = 30;
|
||||
public const int NumAttributes = 10;
|
||||
public const int DisabledIndex = 60;
|
||||
|
||||
public Mod Mod { get; } = mod;
|
||||
public string Name { get; set; } = "Option";
|
||||
|
|
@ -55,23 +54,20 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
}
|
||||
}
|
||||
|
||||
public bool DefaultDisabled
|
||||
=> _canBeDisabled && DefaultSettings.HasFlag(DisabledIndex);
|
||||
|
||||
public IModOption? AddOption(string name, string description = "")
|
||||
{
|
||||
uint fullMask = GetFullMask();
|
||||
var firstUnset = (byte)BitOperations.TrailingZeroCount(~fullMask);
|
||||
// All attributes handled.
|
||||
if (firstUnset >= NumAttributes)
|
||||
return null;
|
||||
|
||||
var groupIdx = Mod.Groups.IndexOf(this);
|
||||
if (groupIdx < 0)
|
||||
return null;
|
||||
|
||||
var subMod = new ImcSubMod(this)
|
||||
{
|
||||
Name = name,
|
||||
Description = description,
|
||||
AttributeIndex = firstUnset,
|
||||
Name = name,
|
||||
Description = description,
|
||||
AttributeMask = 0,
|
||||
};
|
||||
OptionData.Add(subMod);
|
||||
return subMod;
|
||||
|
|
@ -100,7 +96,7 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
continue;
|
||||
|
||||
var option = OptionData[i];
|
||||
mask |= option.Attribute;
|
||||
mask |= option.AttributeMask;
|
||||
}
|
||||
|
||||
return mask;
|
||||
|
|
@ -109,11 +105,10 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
private ushort GetFullMask()
|
||||
=> GetCurrentMask(Setting.AllBits(63));
|
||||
|
||||
private ImcManipulation GetManip(ushort mask)
|
||||
public ImcManipulation GetManip(ushort mask)
|
||||
=> new(ObjectType, BodySlot, PrimaryId, SecondaryId.Id, Variant.Id, EquipSlot,
|
||||
DefaultEntry with { AttributeMask = mask });
|
||||
|
||||
|
||||
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
|
||||
{
|
||||
if (CanBeDisabled && setting.HasFlag(DisabledIndex))
|
||||
|
|
@ -150,8 +145,8 @@ public class ImcModGroup(Mod mod) : IModGroup
|
|||
{
|
||||
jWriter.WriteStartObject();
|
||||
SubMod.WriteModOption(jWriter, option);
|
||||
jWriter.WritePropertyName(nameof(option.AttributeIndex));
|
||||
jWriter.WriteValue(option.AttributeIndex);
|
||||
jWriter.WritePropertyName(nameof(option.AttributeMask));
|
||||
jWriter.WriteValue(option.AttributeMask);
|
||||
jWriter.WriteEndObject();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,9 @@ public class ModCacheManager : IDisposable
|
|||
foreach (var manip in mod.AllDataContainers.SelectMany(m => m.Manipulations))
|
||||
ComputeChangedItems(_identifier, changedItems, manip);
|
||||
|
||||
foreach(var imcGroup in mod.Groups.OfType<ImcModGroup>())
|
||||
ComputeChangedItems(_identifier, changedItems, imcGroup.GetManip(0));
|
||||
|
||||
mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant()));
|
||||
}
|
||||
|
||||
|
|
|
|||
123
Penumbra/Mods/Manager/OptionEditor/ImcAttributeCache.cs
Normal file
123
Penumbra/Mods/Manager/OptionEditor/ImcAttributeCache.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
using OtterGui;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Mods.Groups;
|
||||
using Penumbra.Mods.SubMods;
|
||||
|
||||
namespace Penumbra.Mods.Manager.OptionEditor;
|
||||
|
||||
public unsafe ref struct ImcAttributeCache
|
||||
{
|
||||
private fixed bool _canChange[ImcEntry.NumAttributes];
|
||||
private fixed byte _option[ImcEntry.NumAttributes];
|
||||
|
||||
/// <summary> Obtain the earliest unset flag, or 0 if none are unset. </summary>
|
||||
public readonly ushort LowestUnsetMask;
|
||||
|
||||
public ImcAttributeCache(ImcModGroup group)
|
||||
{
|
||||
for (var i = 0; i < ImcEntry.NumAttributes; ++i)
|
||||
{
|
||||
_canChange[i] = true;
|
||||
_option[i] = byte.MaxValue;
|
||||
|
||||
var flag = (ushort)(1 << i);
|
||||
var set = (group.DefaultEntry.AttributeMask & flag) != 0;
|
||||
if (set)
|
||||
{
|
||||
_canChange[i] = true;
|
||||
_option[i] = byte.MaxValue - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var (option, idx) in group.OptionData.WithIndex())
|
||||
{
|
||||
set = (option.AttributeMask & flag) != 0;
|
||||
if (set)
|
||||
{
|
||||
_canChange[i] = option.AttributeMask != flag;
|
||||
_option[i] = (byte)idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_option[i] == byte.MaxValue && LowestUnsetMask is 0)
|
||||
LowestUnsetMask = flag;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Checks whether an attribute flag can be set by anything, i.e. if it might be the only flag for an option and thus could not be removed from that option. </summary>
|
||||
public readonly bool CanChange(int idx)
|
||||
=> _canChange[idx];
|
||||
|
||||
/// <summary> Set a default attribute flag to a value if possible, remove it from its prior option if necessary, and return if anything changed. </summary>
|
||||
public readonly bool Set(ImcModGroup group, int idx, bool value)
|
||||
{
|
||||
var flag = 1 << idx;
|
||||
var oldMask = group.DefaultEntry.AttributeMask;
|
||||
if (!value)
|
||||
{
|
||||
var newMask = (ushort)(oldMask & ~flag);
|
||||
if (oldMask == newMask)
|
||||
return false;
|
||||
|
||||
group.DefaultEntry = group.DefaultEntry with { AttributeMask = newMask };
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_canChange[idx])
|
||||
return false;
|
||||
|
||||
var mask = (ushort)(oldMask | flag);
|
||||
if (oldMask == mask)
|
||||
return false;
|
||||
|
||||
group.DefaultEntry = group.DefaultEntry with { AttributeMask = mask };
|
||||
if (_option[idx] <= ImcEntry.NumAttributes)
|
||||
{
|
||||
var option = group.OptionData[_option[idx]];
|
||||
option.AttributeMask = (ushort)(option.AttributeMask & ~flag);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Set an attribute flag to a value if possible, remove it from its prior option or the default entry if necessary, and return if anything changed. </summary>
|
||||
public readonly bool Set(ImcSubMod option, int idx, bool value)
|
||||
{
|
||||
if (!_canChange[idx])
|
||||
return false;
|
||||
|
||||
var flag = 1 << idx;
|
||||
var oldMask = option.AttributeMask;
|
||||
if (!value)
|
||||
{
|
||||
var newMask = (ushort)(oldMask & ~flag);
|
||||
if (oldMask == newMask)
|
||||
return false;
|
||||
|
||||
option.AttributeMask = newMask;
|
||||
return true;
|
||||
}
|
||||
|
||||
var mask = (ushort)(oldMask | flag);
|
||||
if (oldMask == mask)
|
||||
return false;
|
||||
|
||||
option.AttributeMask = mask;
|
||||
if (_option[idx] <= ImcEntry.NumAttributes)
|
||||
{
|
||||
var oldOption = option.Group.OptionData[_option[idx]];
|
||||
oldOption.AttributeMask = (ushort)(oldOption.AttributeMask & ~flag);
|
||||
}
|
||||
else if (_option[idx] is byte.MaxValue - 1)
|
||||
{
|
||||
option.Group.DefaultEntry = option.Group.DefaultEntry with
|
||||
{
|
||||
AttributeMask = (ushort)(option.Group.DefaultEntry.AttributeMask & ~flag),
|
||||
};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods.Groups;
|
||||
using Penumbra.Mods.Settings;
|
||||
using Penumbra.Mods.SubMods;
|
||||
using Penumbra.Services;
|
||||
using static FFXIVClientStructs.FFXIV.Client.UI.Misc.ConfigModule;
|
||||
|
||||
namespace Penumbra.Mods.Manager.OptionEditor;
|
||||
|
||||
|
|
@ -26,6 +28,67 @@ public sealed class ImcModGroupEditor(CommunicatorService communicator, SaveServ
|
|||
return group;
|
||||
}
|
||||
|
||||
public ImcSubMod? AddOption(ImcModGroup group, in ImcAttributeCache cache, string name, string description = "",
|
||||
SaveType saveType = SaveType.Queue)
|
||||
{
|
||||
if (cache.LowestUnsetMask == 0)
|
||||
return null;
|
||||
|
||||
var subMod = new ImcSubMod(group)
|
||||
{
|
||||
Name = name,
|
||||
Description = description,
|
||||
AttributeMask = cache.LowestUnsetMask,
|
||||
};
|
||||
group.OptionData.Add(subMod);
|
||||
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionAdded, group.Mod, group, subMod, null, -1);
|
||||
return subMod;
|
||||
}
|
||||
|
||||
// Hide this method.
|
||||
private new ImcSubMod? AddOption(ImcModGroup group, string name, SaveType saveType)
|
||||
=> null;
|
||||
|
||||
public void ChangeDefaultAttribute(ImcModGroup group, in ImcAttributeCache cache, int idx, bool value, SaveType saveType = SaveType.Queue)
|
||||
{
|
||||
if (!cache.Set(group, idx, value))
|
||||
return;
|
||||
|
||||
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
|
||||
}
|
||||
|
||||
public void ChangeDefaultEntry(ImcModGroup group, in ImcEntry newEntry, SaveType saveType = SaveType.Queue)
|
||||
{
|
||||
var entry = newEntry with { AttributeMask = group.DefaultEntry.AttributeMask };
|
||||
if (entry.MaterialId == 0 || group.DefaultEntry.Equals(entry))
|
||||
return;
|
||||
|
||||
group.DefaultEntry = entry;
|
||||
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
|
||||
}
|
||||
|
||||
public void ChangeOptionAttribute(ImcSubMod option, in ImcAttributeCache cache, int idx, bool value, SaveType saveType = SaveType.Queue)
|
||||
{
|
||||
if (!cache.Set(option, idx, value))
|
||||
return;
|
||||
|
||||
SaveService.Save(saveType, new ModSaveGroup(option.Group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, option.Mod, option.Group, option, null, -1);
|
||||
}
|
||||
|
||||
public void ChangeCanBeDisabled(ImcModGroup group, bool canBeDisabled, SaveType saveType = SaveType.Queue)
|
||||
{
|
||||
if (group.CanBeDisabled == canBeDisabled)
|
||||
return;
|
||||
|
||||
group.CanBeDisabled = canBeDisabled;
|
||||
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
|
||||
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMetaChanged, group.Mod, group, null, null, -1);
|
||||
}
|
||||
|
||||
protected override ImcModGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync)
|
||||
=> new(mod)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Mods.Groups;
|
||||
|
||||
namespace Penumbra.Mods.SubMods;
|
||||
|
|
@ -11,15 +12,13 @@ public class ImcSubMod(ImcModGroup group) : IModOption
|
|||
: this(group)
|
||||
{
|
||||
SubMod.LoadOptionData(json, this);
|
||||
AttributeMask = (ushort)((json[nameof(AttributeMask)]?.ToObject<ushort>() ?? 0) & ImcEntry.AttributesMask);
|
||||
}
|
||||
|
||||
public Mod Mod
|
||||
=> Group.Mod;
|
||||
|
||||
public byte AttributeIndex;
|
||||
|
||||
public ushort Attribute
|
||||
=> (ushort)(1 << AttributeIndex);
|
||||
public ushort AttributeMask;
|
||||
|
||||
Mod IModOption.Mod
|
||||
=> Mod;
|
||||
|
|
|
|||
161
Penumbra/UI/ModsTab/AddGroupDrawer.cs
Normal file
161
Penumbra/UI/ModsTab/AddGroupDrawer.cs
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
using ImGuiNET;
|
||||
using OtterGui.Services;
|
||||
using OtterGui.Text;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Manager;
|
||||
using Penumbra.Mods.Manager.OptionEditor;
|
||||
using Penumbra.UI.Classes;
|
||||
|
||||
namespace Penumbra.UI.ModsTab;
|
||||
|
||||
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)
|
||||
{
|
||||
var buttonWidth = new Vector2((width - ImUtf8.ItemInnerSpacing.X) / 2, 0);
|
||||
DrawBasicGroups(mod, width, buttonWidth);
|
||||
DrawImcData(mod, buttonWidth);
|
||||
}
|
||||
|
||||
private void DrawBasicGroups(Mod mod, float width, Vector2 buttonWidth)
|
||||
{
|
||||
ImGui.SetNextItemWidth(width);
|
||||
if (ImUtf8.InputText("##name"u8, ref _groupName, "Enter New Name..."u8))
|
||||
_groupNameValid = ModGroupEditor.VerifyFileName(mod, null, _groupName, false);
|
||||
|
||||
DrawSingleGroupButton(mod, buttonWidth);
|
||||
ImUtf8.SameLineInner();
|
||||
DrawMultiGroupButton(mod, buttonWidth);
|
||||
}
|
||||
|
||||
private void DrawSingleGroupButton(Mod mod, Vector2 width)
|
||||
{
|
||||
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,
|
||||
width, !_groupNameValid))
|
||||
return;
|
||||
|
||||
_modManager.OptionEditor.AddModGroup(mod, GroupType.Single, _groupName);
|
||||
_groupName = string.Empty;
|
||||
_groupNameValid = false;
|
||||
}
|
||||
|
||||
private void DrawMultiGroupButton(Mod mod, Vector2 width)
|
||||
{
|
||||
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,
|
||||
width, !_groupNameValid))
|
||||
return;
|
||||
|
||||
_modManager.OptionEditor.AddModGroup(mod, GroupType.Multi, _groupName);
|
||||
_groupName = string.Empty;
|
||||
_groupNameValid = false;
|
||||
}
|
||||
|
||||
private void DrawImcInput(float width)
|
||||
{
|
||||
var change = MetaManipulationDrawer.DrawObjectType(ref _imcManip, width);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawPrimaryId(ref _imcManip, width);
|
||||
if (_imcManip.ObjectType is ObjectType.Weapon or ObjectType.Monster)
|
||||
{
|
||||
change |= MetaManipulationDrawer.DrawSecondaryId(ref _imcManip, width);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawVariant(ref _imcManip, width);
|
||||
}
|
||||
else if (_imcManip.ObjectType is ObjectType.DemiHuman)
|
||||
{
|
||||
var quarterWidth = (width - ImUtf8.ItemInnerSpacing.X / ImUtf8.GlobalScale) / 2;
|
||||
change |= MetaManipulationDrawer.DrawSecondaryId(ref _imcManip, width);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawSlot(ref _imcManip, quarterWidth);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawVariant(ref _imcManip, quarterWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
change |= MetaManipulationDrawer.DrawSlot(ref _imcManip, width);
|
||||
ImUtf8.SameLineInner();
|
||||
change |= MetaManipulationDrawer.DrawVariant(ref _imcManip, width);
|
||||
}
|
||||
|
||||
if (change)
|
||||
UpdateEntry();
|
||||
}
|
||||
|
||||
private void DrawImcData(Mod mod, Vector2 width)
|
||||
{
|
||||
var halfWidth = width.X / ImUtf8.GlobalScale;
|
||||
DrawImcInput(halfWidth);
|
||||
DrawImcButton(mod, width);
|
||||
}
|
||||
|
||||
private void DrawImcButton(Mod mod, Vector2 width)
|
||||
{
|
||||
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,
|
||||
width, !_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"u8
|
||||
: "IMC File Does Not Exist"u8;
|
||||
ImUtf8.TextFramed(text, Colors.PressEnterWarningBg, 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,12 @@
|
|||
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;
|
||||
|
|
@ -22,7 +19,6 @@ 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;
|
||||
|
||||
|
|
@ -104,8 +100,10 @@ public static class MetaManipulationDrawer
|
|||
return ret;
|
||||
}
|
||||
|
||||
// A number input for ids with a optional max id of given width.
|
||||
// Returns true if newId changed against currentId.
|
||||
/// <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)
|
||||
{
|
||||
|
|
@ -121,142 +119,12 @@ public static class MetaManipulationDrawer
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
FilenameService filenames,
|
||||
DescriptionEditPopup descriptionPopup) : IUiService
|
||||
DescriptionEditPopup descriptionPopup,
|
||||
MetaFileManager metaManager) : IUiService
|
||||
{
|
||||
private static ReadOnlySpan<byte> DragDropLabel
|
||||
=> "##DragOption"u8;
|
||||
|
|
@ -501,7 +369,111 @@ public sealed class ModGroupEditDrawer(
|
|||
}
|
||||
|
||||
private void DrawImcGroup(ImcModGroup group)
|
||||
{ }
|
||||
{
|
||||
using (ImUtf8.Group())
|
||||
{
|
||||
ImUtf8.Text("Object Type"u8);
|
||||
if (group.ObjectType is ObjectType.Equipment or ObjectType.Accessory or ObjectType.DemiHuman)
|
||||
ImUtf8.Text("Slot"u8);
|
||||
ImUtf8.Text("Primary ID");
|
||||
if (group.ObjectType is not ObjectType.Equipment and not ObjectType.Accessory)
|
||||
ImUtf8.Text("Secondary ID");
|
||||
ImUtf8.Text("Variant"u8);
|
||||
|
||||
ImUtf8.TextFrameAligned("Material ID"u8);
|
||||
ImUtf8.TextFrameAligned("Material Animation ID"u8);
|
||||
ImUtf8.TextFrameAligned("Decal ID"u8);
|
||||
ImUtf8.TextFrameAligned("VFX ID"u8);
|
||||
ImUtf8.TextFrameAligned("Sound ID"u8);
|
||||
ImUtf8.TextFrameAligned("Can Be Disabled"u8);
|
||||
ImUtf8.TextFrameAligned("Default Attributes"u8);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
var attributeCache = new ImcAttributeCache(group);
|
||||
|
||||
using (ImUtf8.Group())
|
||||
{
|
||||
ImUtf8.Text(group.ObjectType.ToName());
|
||||
if (group.ObjectType is ObjectType.Equipment or ObjectType.Accessory or ObjectType.DemiHuman)
|
||||
ImUtf8.Text(group.EquipSlot.ToName());
|
||||
ImUtf8.Text($"{group.PrimaryId.Id}");
|
||||
if (group.ObjectType is not ObjectType.Equipment and not ObjectType.Accessory)
|
||||
ImUtf8.Text($"{group.SecondaryId.Id}");
|
||||
ImUtf8.Text($"{group.Variant.Id}");
|
||||
|
||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.MaterialId}");
|
||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.MaterialAnimationId}");
|
||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.DecalId}");
|
||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.VfxId}");
|
||||
ImUtf8.TextFrameAligned($"{group.DefaultEntry.SoundId}");
|
||||
|
||||
var canBeDisabled = group.CanBeDisabled;
|
||||
if (ImUtf8.Checkbox("##disabled"u8, ref canBeDisabled))
|
||||
modManager.OptionEditor.ImcEditor.ChangeCanBeDisabled(group, canBeDisabled, SaveType.Queue);
|
||||
|
||||
var defaultDisabled = group.DefaultDisabled;
|
||||
ImUtf8.SameLineInner();
|
||||
if (ImUtf8.Checkbox("##defaultDisabled"u8, ref defaultDisabled))
|
||||
modManager.OptionEditor.ChangeModGroupDefaultOption(group,
|
||||
group.DefaultSettings.SetBit(ImcModGroup.DisabledIndex, defaultDisabled));
|
||||
|
||||
DrawAttributes(modManager.OptionEditor.ImcEditor, attributeCache, group.DefaultEntry.AttributeMask, group);
|
||||
}
|
||||
|
||||
|
||||
foreach (var (option, optionIdx) in group.OptionData.WithIndex())
|
||||
{
|
||||
using var id = ImRaii.PushId(optionIdx);
|
||||
DrawOptionPosition(group, option, optionIdx);
|
||||
|
||||
ImUtf8.SameLineInner();
|
||||
DrawOptionDefaultMultiBehaviour(group, option, optionIdx);
|
||||
|
||||
ImUtf8.SameLineInner();
|
||||
DrawOptionName(option);
|
||||
|
||||
ImUtf8.SameLineInner();
|
||||
DrawOptionDescription(option);
|
||||
|
||||
ImUtf8.SameLineInner();
|
||||
DrawOptionDelete(option);
|
||||
|
||||
ImUtf8.SameLineInner();
|
||||
ImGui.Dummy(new Vector2(_priorityWidth, 0));
|
||||
|
||||
ImGui.SetCursorPosX(ImGui.GetCursorPosX() + _optionIdxSelectable.X + ImUtf8.ItemInnerSpacing.X * 2 + ImUtf8.FrameHeight);
|
||||
DrawAttributes(modManager.OptionEditor.ImcEditor, attributeCache, option.AttributeMask, option);
|
||||
}
|
||||
|
||||
DrawNewOption(group, attributeCache);
|
||||
return;
|
||||
|
||||
static void DrawAttributes(ImcModGroupEditor editor, in ImcAttributeCache cache, ushort mask, object data)
|
||||
{
|
||||
for (var i = 0; i < ImcEntry.NumAttributes; ++i)
|
||||
{
|
||||
using var id = ImRaii.PushId(i);
|
||||
var value = (mask & (1 << i)) != 0;
|
||||
using (ImRaii.Disabled(!cache.CanChange(i)))
|
||||
{
|
||||
if (ImUtf8.Checkbox(TerminatedByteString.Empty, ref value))
|
||||
{
|
||||
if (data is ImcModGroup g)
|
||||
editor.ChangeDefaultAttribute(g, cache, i, value);
|
||||
else
|
||||
editor.ChangeOptionAttribute((ImcSubMod)data, cache, i, value);
|
||||
}
|
||||
}
|
||||
|
||||
ImUtf8.HoverTooltip($"{(char)('A' + i)}");
|
||||
if (i != 9)
|
||||
ImUtf8.SameLineInner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void DrawOptionPosition(IModGroup group, IModOption option, int optionIdx)
|
||||
|
|
@ -575,14 +547,14 @@ public sealed class ModGroupEditDrawer(
|
|||
if (count >= int.MaxValue)
|
||||
return;
|
||||
|
||||
DrawNewOptionBase(group, count);
|
||||
var name = DrawNewOptionBase(group, count);
|
||||
|
||||
var validName = _newOptionName?.Length > 0;
|
||||
var validName = name.Length > 0;
|
||||
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, validName
|
||||
? "Add a new option to this group."u8
|
||||
: "Please enter a name for the new option."u8, !validName))
|
||||
{
|
||||
modManager.OptionEditor.SingleEditor.AddOption(group, _newOptionName!);
|
||||
modManager.OptionEditor.SingleEditor.AddOption(group, name);
|
||||
_newOptionName = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -593,25 +565,36 @@ public sealed class ModGroupEditDrawer(
|
|||
if (count >= IModGroup.MaxMultiOptions)
|
||||
return;
|
||||
|
||||
DrawNewOptionBase(group, count);
|
||||
var name = DrawNewOptionBase(group, count);
|
||||
|
||||
var validName = _newOptionName?.Length > 0;
|
||||
var validName = name.Length > 0;
|
||||
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, validName
|
||||
? "Add a new option to this group."u8
|
||||
: "Please enter a name for the new option."u8, !validName))
|
||||
{
|
||||
modManager.OptionEditor.MultiEditor.AddOption(group, _newOptionName!);
|
||||
modManager.OptionEditor.MultiEditor.AddOption(group, name);
|
||||
_newOptionName = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawNewOption(ImcModGroup group)
|
||||
private void DrawNewOption(ImcModGroup group, in ImcAttributeCache cache)
|
||||
{
|
||||
// TODO
|
||||
if (cache.LowestUnsetMask == 0)
|
||||
return;
|
||||
|
||||
var name = DrawNewOptionBase(group, group.Options.Count);
|
||||
var validName = name.Length > 0;
|
||||
if (ImUtf8.IconButton(FontAwesomeIcon.Plus, validName
|
||||
? "Add a new option to this group."u8
|
||||
: "Please enter a name for the new option."u8, !validName))
|
||||
{
|
||||
modManager.OptionEditor.ImcEditor.AddOption(group, cache, name);
|
||||
_newOptionName = null;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void DrawNewOptionBase(IModGroup group, int count)
|
||||
private string DrawNewOptionBase(IModGroup group, int count)
|
||||
{
|
||||
ImUtf8.Selectable($"Option #{count + 1}", false, size: _optionIdxSelectable);
|
||||
Target(group, count);
|
||||
|
|
@ -631,6 +614,7 @@ public sealed class ModGroupEditDrawer(
|
|||
}
|
||||
|
||||
ImUtf8.SameLineInner();
|
||||
return newName;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue