From b759797e5139e51ddf497b4413485810e6f54599 Mon Sep 17 00:00:00 2001 From: 90135 <101243352+InoryS@users.noreply.github.com> Date: Sun, 25 Jan 2026 17:05:27 +0800 Subject: [PATCH 1/2] Support * as a wildcard in menu conditional statements. Enhanced the CheckIfSlotDisableApplies method to support '*' wildcards in menu names when matching slot file names. This allows for more flexible slot disable rules by using regular expressions for matching, improving usability for complex slot configurations. --- ShapekeyMaster/CategorySlotHandler.cs | 29 ++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/ShapekeyMaster/CategorySlotHandler.cs b/ShapekeyMaster/CategorySlotHandler.cs index e003412..cfdf246 100644 --- a/ShapekeyMaster/CategorySlotHandler.cs +++ b/ShapekeyMaster/CategorySlotHandler.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; namespace ShapeKeyMaster { @@ -44,6 +45,8 @@ internal class SlotChecker internal static bool CheckIfSlotDisableApplies(ShapeKeyEntry entry, Maid maid) { + var allSlots = (DisableWhenEquipped[])Enum.GetValues(typeof(DisableWhenEquipped)); + #if (DEBUG) ShapeKeyMaster.pluginLogger.LogDebug("Running slot check..."); #endif @@ -54,7 +57,7 @@ internal static bool CheckIfSlotDisableApplies(ShapeKeyEntry entry, Maid maid) ShapeKeyMaster.pluginLogger.LogDebug("WhenAll"); #endif //When all is equipped disable key - foreach (var slot in Enum.GetValues(typeof(DisableWhenEquipped)).Cast()) + foreach (var slot in allSlots) { #if (DEBUG) ShapeKeyMaster.pluginLogger.LogDebug($"Checking {slot}"); @@ -83,10 +86,17 @@ internal static bool CheckIfSlotDisableApplies(ShapeKeyEntry entry, Maid maid) #if (DEBUG) ShapeKeyMaster.pluginLogger.LogDebug($"Checking {menu}"); #endif + // support for * wildcard + string pattern = menu.Contains("*") + ? "^" + Regex.Escape(menu).Replace("\\*", ".*") + "$" + : Regex.Escape(menu); + + Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); + var bodySkins = maid.body0 .FetchGoSlot() .Select(tbody => tbody) - .Where(str => str.m_mp != null && str.m_mp.strFileName.Contains(menu, StringComparison.OrdinalIgnoreCase)) + .Where(slot => slot?.m_mp?.strFileName != null && regex.IsMatch(slot.m_mp.strFileName)) .ToArray(); if (!bodySkins.Any()) @@ -107,7 +117,7 @@ internal static bool CheckIfSlotDisableApplies(ShapeKeyEntry entry, Maid maid) #endif //When any is equipped disable key - foreach (var slot in Enum.GetValues(typeof(DisableWhenEquipped)).Cast()) + foreach (var slot in allSlots) { #if (DEBUG) ShapeKeyMaster.pluginLogger.LogDebug($"Checking {slot}"); @@ -127,10 +137,19 @@ internal static bool CheckIfSlotDisableApplies(ShapeKeyEntry entry, Maid maid) #if (DEBUG) ShapeKeyMaster.pluginLogger.LogDebug($"Checking {menu}"); #endif + // support for * wildcard + string pattern = menu.Contains("*") + ? "^" + Regex.Escape(menu).Replace("\\*", ".*") + "$" + : Regex.Escape(menu); + + Regex regex = new Regex(pattern, RegexOptions.IgnoreCase); + var bodySkins = maid.body0 .FetchGoSlot() .Select(tbody => tbody) - .Where(str => str.m_mp != null && str.m_mp.strFileName.Contains(menu, StringComparison.OrdinalIgnoreCase)); + .Where(slot => slot?.m_mp?.strFileName != null && regex.IsMatch(slot.m_mp.strFileName)) + .ToArray(); + if (bodySkins.Any(skin => maid.body0.GetMask(skin.SlotId))) { From dd662962024bb21f01125f9bec4cf521c5aca331 Mon Sep 17 00:00:00 2001 From: 90135 <101243352+InoryS@users.noreply.github.com> Date: Sun, 25 Jan 2026 17:12:08 +0800 Subject: [PATCH 2/2] When the condition is not met, skip the processing of shapekey, so other programs can use this shape key Introduces a static dictionary to track and skip redundant processing of shape keys when conditional slot disables are applied. This prevents repeated application of the same value and allows other programs to use the shape key when the condition is not met, improving compatibility and efficiency. --- ShapekeyMaster/HarmonyPatchers.cs | 44 ++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/ShapekeyMaster/HarmonyPatchers.cs b/ShapekeyMaster/HarmonyPatchers.cs index 1fc905c..67c0edd 100644 --- a/ShapekeyMaster/HarmonyPatchers.cs +++ b/ShapekeyMaster/HarmonyPatchers.cs @@ -23,6 +23,8 @@ internal class HarmonyPatchers private static float[] _previousBlends; + private static Dictionary _ignoreFlag = new Dictionary(); + [HarmonyPatch(typeof(TMorph), MethodType.Constructor, typeof(TBodySkin))] [HarmonyPostfix] private static void NotifyOfLoad(ref TMorph __instance) @@ -121,14 +123,50 @@ private static void VerifyBlendValuesBeforeSet(ref TMorph __instance) __instance.BlendValues[index] = 0; __instance.BlendValuesBackup[index] = 0; } - else if (shapeKeyEntry.ConditionalsToggle && __instance.bodyskin != null && __instance.bodyskin.body && __instance.bodyskin.body.maid && SlotChecker.CheckIfSlotDisableApplies(shapeKeyEntry, __instance.bodyskin.body.maid)) + else if (shapeKeyEntry.ConditionalsToggle && __instance.bodyskin != null && __instance.bodyskin.body && __instance.bodyskin.body.maid) { #if (DEBUG) ShapeKeyMaster.pluginLogger.LogDebug($"Applying Conditional State @ {__instance.bodyskin.body.maid.status.fullNameJpStyle} :: {__instance.Category} ::: {shapeKeyEntry.EntryName} :::: {shapeKeyEntry.ShapeKey}"); #endif - __instance.BlendValues[index] = shapeKeyEntry.DisabledDeform / 100; - __instance.BlendValuesBackup[index] = shapeKeyEntry.DisabledDeform / 100; + // When the condition is not met, skip the processing of shapekey, so other programs can use this shape key + if (SlotChecker.CheckIfSlotDisableApplies(shapeKeyEntry, __instance.bodyskin.body.maid)) + { + if (_ignoreFlag.ContainsKey(shapeKeyEntry) && _ignoreFlag[shapeKeyEntry] == true) + { + continue; + } + + __instance.BlendValues[index] = shapeKeyEntry.DisabledDeform / 100; + __instance.BlendValuesBackup[index] = shapeKeyEntry.DisabledDeform / 100; + + + // Check if values are approximately equal, because sometimes there will be problems with values not being applied + bool isBlendValueEqual = Math.Abs(__instance.BlendValues[index] - (shapeKeyEntry.DisabledDeform / 100)) < 0.01; + bool isBlendBackupEqual = Math.Abs(__instance.BlendValuesBackup[index] - (shapeKeyEntry.DisabledDeform / 100)) < 0.01; + + if (isBlendValueEqual && isBlendBackupEqual) + { + _ignoreFlag[shapeKeyEntry] = true; + } + } + else + { + _ignoreFlag[shapeKeyEntry] = false; + + if (shapeKeyEntry.AnimateWithExcitement && + YotogiLvls.Contains(SceneManager.GetActiveScene().name)) + { + var deform = shapeKeyEntry.CalculateExcitementDeformation(); + __instance.BlendValues[index] = deform / 100; + __instance.BlendValuesBackup[index] = deform / 100; + } + else + { + __instance.BlendValues[index] = shapeKeyEntry.Deform / 100; + __instance.BlendValuesBackup[index] = shapeKeyEntry.Deform / 100; + } + } } else if (shapeKeyEntry.AnimateWithExcitement && YotogiLvls.Contains(SceneManager.GetActiveScene().name)) {