Improvements.

This commit is contained in:
Ottermandias 2024-05-20 18:28:40 +02:00
parent df6eb3fdd2
commit bb56faa288
9 changed files with 498 additions and 170 deletions

@ -1 +1 @@
Subproject commit 5028fba767ca8febd75a1a5ebc312bd354efc81b
Subproject commit 462acb87099650019996e4306d18cc70f76ca576

@ -1 +1 @@
Subproject commit 595ac5722c9c400bea36110503ed2ae7b02d1489
Subproject commit 5fa4d0e7972423b73f8cf569bb2bfbeddd825c8a

View file

@ -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();
}

View file

@ -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()));
}

View 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;
}
}

View file

@ -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)
{

View file

@ -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;

View 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);
}
}

View file

@ -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)]