using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("NoBloodMoonTint")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("4.0.0.0")] [assembly: AssemblyInformationalVersion("4.0.0+ac6b2f3da8c5bba8ebbe23e2f064499f301665bf")] [assembly: AssemblyProduct("NoBloodMoonTint")] [assembly: AssemblyTitle("NoBloodMoonTint")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("4.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace NoBloodMoonTint { [BepInPlugin("NoBloodMoonTint", "NoBloodMoonTint", "4.0.0")] public class Plugin : BasePlugin { internal static ManualLogSource PluginLog = null; internal static Harmony? HarmonyInstance; private static volatile bool _suppressHueEnabled = false; internal static KeyCode ToggleKey = (KeyCode)288; internal static KeyCode ScanKey = (KeyCode)287; internal static bool IsSuppressionEnabled => _suppressHueEnabled; internal static void SetSuppression(bool value) { _suppressHueEnabled = value; VolumeSuppress.OnSuppressionChanged(value); LogInfo($"[KEYBIND] Blood moon tint suppression set to: {value}"); } public override void Load() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) PluginLog = ((BasePlugin)this).Log; SessionFileLogger.Initialize("NoBloodMoonTint"); _suppressHueEnabled = false; HarmonyInstance = new Harmony("NoBloodMoonTint"); ConfigEntry val = ((BasePlugin)this).Config.Bind("Keybinds", "Toggle", "F7", "Key to toggle blood moon tint suppression (Unity KeyCode name)"); ConfigEntry val2 = ((BasePlugin)this).Config.Bind("Keybinds", "ScanVolumes", "F6", "Key to scan and diff current scene volume profiles"); if (Enum.TryParse(val.Value, ignoreCase: true, out KeyCode result)) { ToggleKey = result; } if (Enum.TryParse(val2.Value, ignoreCase: true, out KeyCode result2)) { ScanKey = result2; } ShaderColorHook.Install(HarmonyInstance); ClassInjector.RegisterTypeInIl2Cpp(); ((BasePlugin)this).AddComponent(); LogInfo($"[KEYBIND] Keybinds active — Scan:{ScanKey} Toggle:{ToggleKey}"); LogInfo("[VOLUME] Suppression will reapply on scene load only."); LogInfo("[VOLUME] Press F6 to scan and diff volume profiles while toggling blood moon on/off."); } internal static void LogInfo(string message) { PluginLog.LogInfo((object)message); SessionFileLogger.Info(message); } } public class KeybindListener : MonoBehaviour { private GUIStyle? _hudStyle; public KeybindListener(IntPtr ptr) : base(ptr) { } private void Update() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if ((int)Plugin.ScanKey != 0 && Input.GetKeyDown(Plugin.ScanKey)) { VolumeSuppress.ScanAndLog("manual-scan"); } else if ((int)Plugin.ToggleKey != 0 && Input.GetKeyDown(Plugin.ToggleKey)) { Plugin.SetSuppression(!Plugin.IsSuppressionEnabled); } } private void OnGUI() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) if (_hudStyle == null) { _hudStyle = new GUIStyle(); _hudStyle.fontSize = 11; _hudStyle.fontStyle = (FontStyle)1; } string text = (Plugin.IsSuppressionEnabled ? "NoBloodMoonTint: ON" : "NoBloodMoonTint: OFF"); Color textColor = (Plugin.IsSuppressionEnabled ? new Color(0.2f, 1f, 0.2f, 1f) : new Color(1f, 0.35f, 0.1f, 1f)); float num = (float)Screen.height - 20f - 10f; _hudStyle.normal.textColor = new Color(0f, 0f, 0f, 0.9f); GUI.Label(new Rect(11f, num + 1f, 200f, 20f), text, _hudStyle); _hudStyle.normal.textColor = textColor; GUI.Label(new Rect(10f, num, 200f, 20f), text, _hudStyle); } } internal static class VolumeSuppress { private sealed class VolumeState { internal float Weight; internal bool Enabled; } private const float TargetScenePostProcessWeight = 1f; private const float BrightnessLiftStops = 0.5f; private static readonly List _cached = new List(); private static readonly Dictionary _originalWeights = new Dictionary(); private static readonly Dictionary _originalEnabled = new Dictionary(); private static readonly Dictionary _originalPriorities = new Dictionary(); private static readonly Dictionary _lastScan = new Dictionary(); private static Color? _originalAmbientLight = null; private static bool _loggedGenericTargets = false; private static readonly Dictionary _origColorFilters = new Dictionary(); private static readonly Dictionary _origPostExposures = new Dictionary(); private static readonly Dictionary _origSplitShadows = new Dictionary(); private static readonly Dictionary _origSplitHighlights = new Dictionary(); private static readonly Dictionary _origTemperatures = new Dictionary(); private static readonly Dictionary _origTints = new Dictionary(); private static readonly Dictionary _origBloodMoonFogActive = new Dictionary(); private static readonly Dictionary _origCustomVignetteTint = new Dictionary(); private static readonly List _customVignetteMats = new List(); private static int _customVignetteScanCooldown = 0; internal static void OnSuppressionChanged(bool enabled) { if (!enabled) { RestoreVolumes(); } else { ApplySuppressedStateImmediate(); } } private static void ApplySuppressedStateImmediate() { RebuildCache(); ZeroBloodMoonVolumes(); } internal static void ScanAndLog(string reason) { Plugin.LogInfo("[VOLUME] Scanning all scene volumes (reason=" + reason + ")..."); try { Il2CppArrayBase val = Object.FindObjectsOfType(); int num = 0; Dictionary dictionary = new Dictionary(); foreach (Volume item in val) { string text = (((Object)(object)item.sharedProfile != (Object)null) ? ((Object)item.sharedProfile).name : ""); if (!ShouldSkipVolumeInLogs(text)) { string value = (((Object)(object)((Component)item).gameObject != (Object)null) ? ((Object)((Component)item).gameObject).name : ""); string key = $"{value}|{text}|{item.priority:F3}"; dictionary[key] = new VolumeState { Weight = item.weight, Enabled = ((Behaviour)item).enabled }; num++; Plugin.LogInfo($"[VOLUME] '{value}' profile='{text}' enabled={((Behaviour)item).enabled} weight={item.weight:F3} isGlobal={item.isGlobal} priority={item.priority}"); } } Plugin.LogInfo($"[VOLUME] Found {num} non-sound volumes total."); if (_lastScan.Count > 0) { int num2 = 0; foreach (KeyValuePair item2 in dictionary) { if (!_lastScan.TryGetValue(item2.Key, out VolumeState value2)) { Plugin.LogInfo($"[VOLUME_DIFF] Added: {item2.Key} enabled={item2.Value.Enabled} weight={item2.Value.Weight:F3}"); num2++; } else if (Math.Abs(value2.Weight - item2.Value.Weight) > 0.0001f || value2.Enabled != item2.Value.Enabled) { Plugin.LogInfo($"[VOLUME_DIFF] Changed: {item2.Key} enabled {value2.Enabled}->{item2.Value.Enabled}, weight {value2.Weight:F3}->{item2.Value.Weight:F3}"); num2++; } } foreach (KeyValuePair item3 in _lastScan) { if (!dictionary.ContainsKey(item3.Key)) { Plugin.LogInfo("[VOLUME_DIFF] Removed: " + item3.Key); num2++; } } if (num2 == 0) { Plugin.LogInfo("[VOLUME_DIFF] No differences from previous scan."); } } _lastScan.Clear(); foreach (KeyValuePair item4 in dictionary) { _lastScan[item4.Key] = item4.Value; } } catch (Exception ex) { Plugin.LogInfo("[VOLUME] ScanAndLog failed: " + ex.GetType().Name + " " + ex.Message); } } private static bool ShouldSkipVolumeInLogs(string profileName) { string text = (profileName ?? string.Empty).ToLowerInvariant(); return text.Contains("sound_profile") || text.StartsWith("mus "); } internal static void ZeroBloodMoonVolumes() { //IL_0506: Unknown result type (might be due to invalid IL or missing references) //IL_050b: Unknown result type (might be due to invalid IL or missing references) //IL_052c: Unknown result type (might be due to invalid IL or missing references) //IL_0520: Unknown result type (might be due to invalid IL or missing references) //IL_053c: Unknown result type (might be due to invalid IL or missing references) //IL_0543: Unknown result type (might be due to invalid IL or missing references) //IL_054a: Unknown result type (might be due to invalid IL or missing references) //IL_0551: Unknown result type (might be due to invalid IL or missing references) //IL_055d: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) try { foreach (Volume item in _cached) { if (!((Object)(object)item == (Object)null)) { if (!_originalWeights.ContainsKey(item)) { _originalWeights[item] = item.weight; } if (!_originalEnabled.ContainsKey(item)) { _originalEnabled[item] = ((Behaviour)item).enabled; } if (!_originalPriorities.ContainsKey(item)) { _originalPriorities[item] = item.priority; } float value; float num = (_originalPriorities.TryGetValue(item, out value) ? value : item.priority); if (Math.Abs(num) < 0.001f) { ((Behaviour)item).enabled = true; item.weight = 1f; ApplyBaselineBrightnessLift(item); } else if (item.isGlobal && num >= 9.5f) { ((Behaviour)item).enabled = false; item.weight = 0f; item.priority = 10f; } } } if (!_loggedGenericTargets) { _loggedGenericTargets = true; Plugin.LogInfo($"[SUPPRESS] Blood moon effect suppression active (brightness +{0.5f:F2} EV)."); } Color value3 = default(Color); foreach (Volume item2 in _cached) { if ((Object)(object)item2 == (Object)null || (Object)(object)item2.sharedProfile == (Object)null) { continue; } float value2; float num2 = (_originalPriorities.TryGetValue(item2, out value2) ? value2 : item2.priority); if (!item2.isGlobal || !(num2 >= 9.5f)) { continue; } try { string name = ((Object)item2.sharedProfile).name; Enumerator enumerator3 = item2.sharedProfile.components.GetEnumerator(); while (enumerator3.MoveNext()) { VolumeComponent current3 = enumerator3.Current; if ((Object)(object)current3 == (Object)null) { continue; } Type il2CppType = ((Object)current3).GetIl2CppType(); string text = ((il2CppType != null) ? ((MemberInfo)il2CppType).Name : null) ?? ((object)current3).GetType().Name; if (name.IndexOf("bloodmoon", StringComparison.OrdinalIgnoreCase) >= 0 && text.IndexOf("StunlockFogVolumeComponent", StringComparison.OrdinalIgnoreCase) >= 0) { string key = name + "|" + text; if (!_origBloodMoonFogActive.ContainsKey(key)) { _origBloodMoonFogActive[key] = current3.active; } current3.active = false; } ColorAdjustments val = ((Il2CppObjectBase)current3).TryCast(); if ((Object)(object)val != (Object)null) { string key2 = name + "|ColorAdjustments"; if (!_origColorFilters.ContainsKey(key2)) { _origColorFilters[key2] = ((VolumeParameter)(object)val.colorFilter).value; } ((VolumeParameter)(object)val.colorFilter).value = Color.white; continue; } SplitToning val2 = ((Il2CppObjectBase)current3).TryCast(); if ((Object)(object)val2 != (Object)null) { string key3 = name + "|SplitToning"; if (!_origSplitShadows.ContainsKey(key3)) { _origSplitShadows[key3] = ((VolumeParameter)(object)val2.shadows).value; } if (!_origSplitHighlights.ContainsKey(key3)) { _origSplitHighlights[key3] = ((VolumeParameter)(object)val2.highlights).value; } ((Color)(ref value3))..ctor(0.5f, 0.5f, 0.5f, 0f); ((VolumeParameter)(object)val2.shadows).value = value3; ((VolumeParameter)(object)val2.highlights).value = value3; continue; } WhiteBalance val3 = ((Il2CppObjectBase)current3).TryCast(); if ((Object)(object)val3 != (Object)null) { string key4 = name + "|WhiteBalance"; if (!_origTemperatures.ContainsKey(key4)) { _origTemperatures[key4] = ((VolumeParameter)(object)val3.temperature).value; } if (!_origTints.ContainsKey(key4)) { _origTints[key4] = ((VolumeParameter)(object)val3.tint).value; } ((VolumeParameter)(object)val3.temperature).value = 0f; ((VolumeParameter)(object)val3.tint).value = 0f; } } } catch { } } try { Color ambientLight = RenderSettings.ambientLight; if (!_originalAmbientLight.HasValue) { _originalAmbientLight = ambientLight; } if (IsWarmColor(ambientLight)) { Color ambientLight2 = default(Color); ((Color)(ref ambientLight2))..ctor(ambientLight.g, ambientLight.g, ambientLight.g, ambientLight.a); RenderSettings.ambientLight = ambientLight2; } } catch { } NeutralizeCustomVignetteTint(); } catch { } } private static void ApplyBaselineBrightnessLift(Volume v) { if ((Object)(object)((v != null) ? v.sharedProfile : null) == (Object)null) { return; } try { string name = ((Object)v.sharedProfile).name; Enumerator enumerator = v.sharedProfile.components.GetEnumerator(); while (enumerator.MoveNext()) { VolumeComponent current = enumerator.Current; if ((Object)(object)current == (Object)null) { continue; } ColorAdjustments val = ((Il2CppObjectBase)current).TryCast(); if ((Object)(object)val == (Object)null) { continue; } string key = name + "|ColorAdjustments.postExposure"; if (!_origPostExposures.ContainsKey(key)) { _origPostExposures[key] = ((VolumeParameter)(object)val.postExposure).value; } ((VolumeParameter)(object)val.postExposure).value = _origPostExposures[key] + 0.5f; break; } } catch { } } private static void NeutralizeCustomVignetteTint() { //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) try { if (--_customVignetteScanCooldown <= 0) { _customVignetteScanCooldown = 120; _customVignetteMats.Clear(); Il2CppArrayBase val = Resources.FindObjectsOfTypeAll(); foreach (Material item in val) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.shader == (Object)null) && ((Object)item.shader).name.Equals("Hidden/Shader/CustomVignette", StringComparison.OrdinalIgnoreCase) && item.HasProperty("_ColorTint")) { _customVignetteMats.Add(item); } } } foreach (Material customVignetteMat in _customVignetteMats) { if (!((Object)(object)customVignetteMat == (Object)null) && customVignetteMat.HasProperty("_ColorTint")) { if (!_origCustomVignetteTint.ContainsKey(customVignetteMat)) { _origCustomVignetteTint[customVignetteMat] = customVignetteMat.GetColor("_ColorTint"); } customVignetteMat.SetColor("_ColorTint", new Color(0f, 0f, 0f, 0f)); } } } catch { } } private static bool IsWarmColor(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) return c.r > 0.02f && c.r > c.g * 1.12f; } private static void RestoreVolumes() { //IL_0478: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair originalWeight in _originalWeights) { if ((Object)(object)originalWeight.Key != (Object)null) { originalWeight.Key.weight = originalWeight.Value; } } foreach (KeyValuePair item in _originalEnabled) { if ((Object)(object)item.Key != (Object)null) { ((Behaviour)item.Key).enabled = item.Value; } } foreach (KeyValuePair originalPriority in _originalPriorities) { if ((Object)(object)originalPriority.Key != (Object)null) { originalPriority.Key.priority = originalPriority.Value; } } try { Il2CppArrayBase val = Object.FindObjectsOfType(); foreach (Volume item2 in val) { if ((Object)(object)((item2 != null) ? item2.sharedProfile : null) == (Object)null) { continue; } string name = ((Object)item2.sharedProfile).name; Enumerator enumerator5 = item2.sharedProfile.components.GetEnumerator(); while (enumerator5.MoveNext()) { VolumeComponent current5 = enumerator5.Current; if ((Object)(object)current5 == (Object)null) { continue; } Type il2CppType = ((Object)current5).GetIl2CppType(); string text = ((il2CppType != null) ? ((MemberInfo)il2CppType).Name : null) ?? ((object)current5).GetType().Name; string key = name + "|" + text; if (_origBloodMoonFogActive.TryGetValue(key, out var value)) { current5.active = value; } ColorAdjustments val2 = ((Il2CppObjectBase)current5).TryCast(); if ((Object)(object)val2 != (Object)null) { string key2 = name + "|ColorAdjustments"; if (_origColorFilters.TryGetValue(key2, out var value2)) { ((VolumeParameter)(object)val2.colorFilter).value = value2; } string key3 = name + "|ColorAdjustments.postExposure"; if (_origPostExposures.TryGetValue(key3, out var value3)) { ((VolumeParameter)(object)val2.postExposure).value = value3; } continue; } SplitToning val3 = ((Il2CppObjectBase)current5).TryCast(); if ((Object)(object)val3 != (Object)null) { string key4 = name + "|SplitToning"; if (_origSplitShadows.TryGetValue(key4, out var value4)) { ((VolumeParameter)(object)val3.shadows).value = value4; } if (_origSplitHighlights.TryGetValue(key4, out var value5)) { ((VolumeParameter)(object)val3.highlights).value = value5; } continue; } WhiteBalance val4 = ((Il2CppObjectBase)current5).TryCast(); if ((Object)(object)val4 != (Object)null) { string key5 = name + "|WhiteBalance"; if (_origTemperatures.TryGetValue(key5, out var value6)) { ((VolumeParameter)(object)val4.temperature).value = value6; } if (_origTints.TryGetValue(key5, out var value7)) { ((VolumeParameter)(object)val4.tint).value = value7; } } } } } catch { } _origColorFilters.Clear(); _origPostExposures.Clear(); _origSplitShadows.Clear(); _origSplitHighlights.Clear(); _origTemperatures.Clear(); _origTints.Clear(); _origBloodMoonFogActive.Clear(); foreach (KeyValuePair item3 in _origCustomVignetteTint) { try { if ((Object)(object)item3.Key != (Object)null && item3.Key.HasProperty("_ColorTint")) { item3.Key.SetColor("_ColorTint", item3.Value); } } catch { } } _origCustomVignetteTint.Clear(); _customVignetteMats.Clear(); _customVignetteScanCooldown = 0; if (_originalAmbientLight.HasValue) { RenderSettings.ambientLight = _originalAmbientLight.Value; _originalAmbientLight = null; } _originalWeights.Clear(); _originalEnabled.Clear(); _originalPriorities.Clear(); _loggedGenericTargets = false; } private static void RebuildCache() { _cached.Clear(); Il2CppArrayBase val = Object.FindObjectsOfType(); foreach (Volume item in val) { if (!((Object)(object)item == (Object)null)) { _cached.Add(item); } } } } internal static class ShaderColorHook { private static readonly HashSet Logged = new HashSet(); private static readonly HashSet LoggedForcedNeutral = new HashSet(); internal static void Install(Harmony harmony) { TryPatch(harmony, typeof(Shader), "SetGlobalColor", new Type[2] { typeof(string), typeof(Color) }, "SetGlobalColorStringPrefix", "Shader.SetGlobalColor(string,Color)"); TryPatch(harmony, typeof(Shader), "SetGlobalColor", new Type[2] { typeof(int), typeof(Color) }, "SetGlobalColorIntPrefix", "Shader.SetGlobalColor(int,Color)"); TryPatch(harmony, typeof(Shader), "SetGlobalVector", new Type[2] { typeof(string), typeof(Vector4) }, "SetGlobalVectorStringPrefix", "Shader.SetGlobalVector(string,Vector4)"); TryPatch(harmony, typeof(Shader), "SetGlobalVector", new Type[2] { typeof(int), typeof(Vector4) }, "SetGlobalVectorIntPrefix", "Shader.SetGlobalVector(int,Vector4)"); TryPatch(harmony, typeof(Material), "SetColor", new Type[2] { typeof(string), typeof(Color) }, "SetMaterialColorStringPrefix", "Material.SetColor(string,Color)"); TryPatch(harmony, typeof(Material), "SetColor", new Type[2] { typeof(int), typeof(Color) }, "SetMaterialColorIntPrefix", "Material.SetColor(int,Color)"); TryPatch(harmony, typeof(Material), "SetVector", new Type[2] { typeof(string), typeof(Vector4) }, "SetMaterialVectorStringPrefix", "Material.SetVector(string,Vector4)"); TryPatch(harmony, typeof(Material), "SetVector", new Type[2] { typeof(int), typeof(Vector4) }, "SetMaterialVectorIntPrefix", "Material.SetVector(int,Vector4)"); } private static void TryPatch(Harmony harmony, Type type, string methodName, Type[] args, string prefixName, string label) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown try { MethodInfo method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | ((type == typeof(Shader)) ? BindingFlags.Static : BindingFlags.Instance), null, args, null); if (method == null) { Plugin.LogInfo("[HOOK] Method not found: " + label); return; } HarmonyMethod val = new HarmonyMethod(typeof(ShaderColorHook).GetMethod(prefixName, BindingFlags.Static | BindingFlags.NonPublic)); harmony.Patch((MethodBase)method, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.LogInfo("[HOOK] Hooked " + label); } catch (Exception ex) { Plugin.LogInfo($"[HOOK] Failed {label}: {ex.GetType().Name} {ex.Message}"); } } private static bool SetGlobalColorStringPrefix(string name, Color value) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) if (Logged.Add("global_color:" + name)) { Plugin.LogInfo($"[SHADER] SetGlobalColor name='{name}' r={value.r:F3} g={value.g:F3} b={value.b:F3} a={value.a:F3}"); } if (Plugin.IsSuppressionEnabled && IsBloodMoonColor(value) && IsLikelyMoonContext(name, null, null)) { Plugin.LogInfo("[SHADER] SUPPRESSED SetGlobalColor '" + name + "'"); return false; } return true; } private static bool SetGlobalColorIntPrefix(int nameID, Color value) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (Logged.Add($"global_color_id:{nameID}")) { Plugin.LogInfo($"[SHADER] SetGlobalColor nameID={nameID} r={value.r:F3} g={value.g:F3} b={value.b:F3} a={value.a:F3}"); } return true; } private static bool SetGlobalVectorStringPrefix(string name, Vector4 value) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) if (Logged.Add("global_vector:" + name)) { Plugin.LogInfo($"[SHADER] SetGlobalVector name='{name}' x={value.x:F3} y={value.y:F3} z={value.z:F3} w={value.w:F3}"); } return true; } private static bool SetGlobalVectorIntPrefix(int nameID, Vector4 value) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (Logged.Add($"global_vector_id:{nameID}")) { Plugin.LogInfo($"[SHADER] SetGlobalVector nameID={nameID} x={value.x:F3} y={value.y:F3} z={value.z:F3} w={value.w:F3}"); } return true; } private static bool SetMaterialColorStringPrefix(Material __instance, string name, ref Color value) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) string text = SafeName((__instance != null) ? ((Object)__instance).name : null); string text2 = SafeName(((Object)(object)((__instance != null) ? __instance.shader : null) != (Object)null) ? ((Object)__instance.shader).name : null); string item = $"mat_color:{text2}:{text}:{name}"; if (Logged.Add(item) && (IsBloodMoonColor(value) || IsLikelyMoonContext(name, text, text2))) { Plugin.LogInfo($"[MATERIAL] SetColor name='{name}' shader='{text2}' material='{text}' r={value.r:F3} g={value.g:F3} b={value.b:F3} a={value.a:F3}"); } if (Plugin.IsSuppressionEnabled && IsDarkForegroundPass(name, text, text2)) { value = new Color(0f, 0f, 0f, 0f); string item2 = $"forced_df:{text2}:{text}:{name}"; if (LoggedForcedNeutral.Add(item2)) { Plugin.LogInfo($"[MATERIAL] FORCED NEUTRAL SetColor name='{name}' shader='{text2}' material='{text}'"); } return true; } if (Plugin.IsSuppressionEnabled && IsBloodMoonColor(value) && IsLikelyMoonContext(name, text, text2)) { value = new Color(0f, 0f, 0f, 0f); string item3 = $"forced_generic:{text2}:{text}:{name}"; if (LoggedForcedNeutral.Add(item3)) { Plugin.LogInfo($"[MATERIAL] FORCED NEUTRAL (generic) SetColor name='{name}' shader='{text2}' material='{text}'"); } } return true; } private static bool SetMaterialColorIntPrefix(Material __instance, int nameID, ref Color value) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) string text = SafeName((__instance != null) ? ((Object)__instance).name : null); string text2 = SafeName(((Object)(object)((__instance != null) ? __instance.shader : null) != (Object)null) ? ((Object)__instance.shader).name : null); string item = $"mat_color_id:{text2}:{text}:{nameID}"; if (Logged.Add(item) && IsBloodMoonColor(value)) { Plugin.LogInfo($"[MATERIAL] SetColor nameID={nameID} shader='{text2}' material='{text}' r={value.r:F3} g={value.g:F3} b={value.b:F3} a={value.a:F3}"); } if (Plugin.IsSuppressionEnabled && IsDarkForegroundPass(null, text, text2) && IsBloodMoonColor(value)) { value = new Color(0f, 0f, 0f, 0f); string item2 = $"forced_df_id:{text2}:{text}:{nameID}"; if (LoggedForcedNeutral.Add(item2)) { Plugin.LogInfo($"[MATERIAL] FORCED NEUTRAL SetColor nameID={nameID} shader='{text2}' material='{text}'"); } } return true; } private static bool SetMaterialVectorStringPrefix(Material __instance, string name, Vector4 value) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) string text = SafeName((__instance != null) ? ((Object)__instance).name : null); string text2 = SafeName(((Object)(object)((__instance != null) ? __instance.shader : null) != (Object)null) ? ((Object)__instance.shader).name : null); string item = $"mat_vector:{text2}:{text}:{name}"; if (Logged.Add(item) && (IsBloodMoonVector(value) || IsLikelyMoonContext(name, text, text2))) { Plugin.LogInfo($"[MATERIAL] SetVector name='{name}' shader='{text2}' material='{text}' x={value.x:F3} y={value.y:F3} z={value.z:F3} w={value.w:F3}"); } return true; } private static bool SetMaterialVectorIntPrefix(Material __instance, int nameID, Vector4 value) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) string value2 = SafeName((__instance != null) ? ((Object)__instance).name : null); string value3 = SafeName(((Object)(object)((__instance != null) ? __instance.shader : null) != (Object)null) ? ((Object)__instance.shader).name : null); string item = $"mat_vector_id:{value3}:{value2}:{nameID}"; if (Logged.Add(item) && IsBloodMoonVector(value)) { Plugin.LogInfo($"[MATERIAL] SetVector nameID={nameID} shader='{value3}' material='{value2}' x={value.x:F3} y={value.y:F3} z={value.z:F3} w={value.w:F3}"); } return true; } private static bool IsBloodMoonColor(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) return c.a > 0.02f && c.r > 0.1f && c.r > c.g * 1.4f && c.r > c.b * 1.4f; } private static bool IsBloodMoonVector(Vector4 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) return v.x > 0.1f && v.x > v.y * 1.4f && v.x > v.z * 1.4f; } private static bool IsLikelyMoonContext(string? propertyName, string? materialName, string? shaderName) { string text = $"{propertyName} {materialName} {shaderName}".ToLowerInvariant(); return text.Contains("blood") || text.Contains("moon") || text.Contains("tint") || text.Contains("hue") || text.Contains("overlay") || text.Contains("post") || text.Contains("fog") || text.Contains("sky") || text.Contains("atmo") || text.Contains("vignette") || text.Contains("grade"); } private static bool IsDarkForegroundPass(string? propertyName, string materialName, string shaderName) { if (!shaderName.Equals("Hidden/Shader/DarkForeground", StringComparison.OrdinalIgnoreCase) && !materialName.Equals("Hidden/Shader/DarkForeground", StringComparison.OrdinalIgnoreCase)) { return false; } if (propertyName == null) { return true; } return propertyName.Equals("_ForegroundColor", StringComparison.OrdinalIgnoreCase) || propertyName.Equals("_TintColor", StringComparison.OrdinalIgnoreCase) || propertyName.Equals("_OverlayColor", StringComparison.OrdinalIgnoreCase); } private static string SafeName(string? value) { return string.IsNullOrWhiteSpace(value) ? "" : value; } } internal static class SessionFileLogger { private static readonly object Sync = new object(); private static StreamWriter? _writer; private static string? _logPath; internal static void Initialize(string pluginName) { string text = Path.Combine(Paths.BepInExRootPath, "logs"); Directory.CreateDirectory(text); _logPath = Path.Combine(text, pluginName + ".log"); RotateExistingFile(_logPath); _writer = new StreamWriter(_logPath, append: false) { AutoFlush = true }; Write("INFO", "Session logging started."); } internal static void Info(string message) { Write("INFO", message); } private static void Write(string level, string message) { lock (Sync) { if (_writer != null) { string value = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"); _writer.WriteLine($"[{value}] [{level}] {message}"); } } } private static void RotateExistingFile(string currentPath) { if (File.Exists(currentPath)) { string directoryName = Path.GetDirectoryName(currentPath); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(currentPath); string extension = Path.GetExtension(currentPath); string text = DateTime.Now.ToString("yyyyMMdd-HHmmss"); string destFileName = Path.Combine(directoryName, fileNameWithoutExtension + "." + text + extension); File.Move(currentPath, destFileName, overwrite: true); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "NoBloodMoonTint"; public const string PLUGIN_NAME = "NoBloodMoonTint"; public const string PLUGIN_VERSION = "4.0.0"; } }