diff --git a/Penumbra/Mods/Groups/ImcModGroup.cs b/Penumbra/Mods/Groups/ImcModGroup.cs
index 03896134..5f99673e 100644
--- a/Penumbra/Mods/Groups/ImcModGroup.cs
+++ b/Penumbra/Mods/Groups/ImcModGroup.cs
@@ -242,7 +242,7 @@ public class ImcModGroup(Mod mod) : IModGroup
continue;
var option = OptionData[i];
- mask |= option.AttributeMask;
+ mask ^= option.AttributeMask;
}
return mask;
diff --git a/Penumbra/Mods/Manager/OptionEditor/ImcAttributeCache.cs b/Penumbra/Mods/Manager/OptionEditor/ImcAttributeCache.cs
index e1235c5b..a7b73ac9 100644
--- a/Penumbra/Mods/Manager/OptionEditor/ImcAttributeCache.cs
+++ b/Penumbra/Mods/Manager/OptionEditor/ImcAttributeCache.cs
@@ -21,23 +21,14 @@ public unsafe ref struct ImcAttributeCache
_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.AttributeMask & flag) == 0)
+ continue;
+
+ _canChange[i] = option.AttributeMask != flag;
+ _option[i] = (byte)idx;
+ break;
}
if (_option[i] == byte.MaxValue && LowestUnsetMask is 0)
@@ -65,25 +56,16 @@ public unsafe ref struct ImcAttributeCache
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;
}
/// 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.
- public readonly bool Set(ImcSubMod option, int idx, bool value)
+ public readonly bool Set(ImcSubMod option, int idx, bool value, bool turnOffDefault = false)
{
if (!_canChange[idx])
return false;
@@ -110,7 +92,7 @@ public unsafe ref struct ImcAttributeCache
var oldOption = option.Group.OptionData[_option[idx]];
oldOption.AttributeMask = (ushort)(oldOption.AttributeMask & ~flag);
}
- else if (_option[idx] is byte.MaxValue - 1)
+ else if (turnOffDefault && _option[idx] is byte.MaxValue - 1)
{
option.Group.DefaultEntry = option.Group.DefaultEntry with
{
diff --git a/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs b/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs
index bbb5e54e..9d1ab78a 100644
--- a/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs
+++ b/Penumbra/UI/ModsTab/Groups/ImcModGroupEditDrawer.cs
@@ -3,6 +3,9 @@ using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using OtterGui.Text;
+using OtterGui.Text.Widget;
+using OtterGui.Widgets;
+using OtterGuiInternal.Utility;
using Penumbra.GameData.Structs;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager.OptionEditor;
@@ -86,7 +89,8 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr
foreach (var (option, idx) in group.OptionData.WithIndex().Where(o => !o.Value.IsDisableSubMod))
{
using var id = ImUtf8.PushId(idx);
- DrawAttributes(editor.ModManager.OptionEditor.ImcEditor, attributeCache, option.AttributeMask, option);
+ DrawAttributes(editor.ModManager.OptionEditor.ImcEditor, attributeCache, option.AttributeMask, option,
+ group.DefaultEntry.AttributeMask);
}
}
}
@@ -132,15 +136,18 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr
}
}
- private static void DrawAttributes(ImcModGroupEditor editor, in ImcAttributeCache cache, ushort mask, object data)
+ private static void DrawAttributes(ImcModGroupEditor editor, in ImcAttributeCache cache, ushort mask, object data,
+ ushort? defaultMask = null)
{
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)))
+ using var id = ImRaii.PushId(i);
+ var flag = 1 << i;
+ var value = (mask & flag) != 0;
+ var inDefault = defaultMask.HasValue && (defaultMask & flag) != 0;
+ using (ImRaii.Disabled(defaultMask != null && !cache.CanChange(i)))
{
- if (ImUtf8.Checkbox(""u8, ref value))
+ if (inDefault ? NegativeCheckbox.Instance.Draw(""u8, ref value) : ImUtf8.Checkbox(""u8, ref value))
{
if (data is ImcModGroup g)
editor.ChangeDefaultAttribute(g, cache, i, value);
@@ -154,4 +161,21 @@ public readonly struct ImcModGroupEditDrawer(ModGroupEditDrawer editor, ImcModGr
ImUtf8.SameLineInner();
}
}
+
+ private sealed class NegativeCheckbox : MultiStateCheckbox
+ {
+ public static readonly NegativeCheckbox Instance = new();
+
+ protected override void RenderSymbol(bool value, Vector2 position, float size)
+ {
+ if (value)
+ SymbolHelpers.RenderCross(ImGui.GetWindowDrawList(), position, ImGui.GetColorU32(ImGuiCol.CheckMark), size);
+ }
+
+ protected override bool NextValue(bool value)
+ => !value;
+
+ protected override bool PreviousValue(bool value)
+ => !value;
+ }
}