using System; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("UKMod template")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("UKMod template")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("3d018c2f-f5bc-47be-a844-3e9888579d1f")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace PitchBlack; [BepInPlugin("com.nico.pitchblack", "PitchBlack", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.nico.pitchblack"; public const string PluginName = "PitchBlack"; public const string PluginVersion = "1.0.0"; public static ManualLogSource Log; public static Plugin Instance; public static ConfigEntry ModEnabled; public static ConfigEntry KillAmbientLight; public static ConfigEntry KillStaticLights; public static ConfigEntry ContinuouslyEnforceAmbient; public static ConfigEntry ContinuousSweep; public static ConfigEntry PostLoadSweepDuration; public static ConfigEntry PostLoadSweepInterval; public static ConfigEntry VerboseLogging; private Coroutine _sweepRoutine; private void Awake() { //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Instance = this; ModEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "ModEnabled", true, "Master toggle for PitchBlack."); KillAmbientLight = ((BaseUnityPlugin)this).Config.Bind("Lighting", "KillAmbientLight", true, "Sets scene ambient light to pure black on load."); KillStaticLights = ((BaseUnityPlugin)this).Config.Bind("Lighting", "KillStaticLights", true, "Disables non-moving Light components found in the scene (baked/static lights). Dynamic lights spawned during gameplay - muzzle flashes, explosions, coins, enemy attacks - are left untouched."); ContinuouslyEnforceAmbient = ((BaseUnityPlugin)this).Config.Bind("Lighting", "ContinuouslyEnforceAmbient", true, "Re-applies black ambient every frame in case the game resets RenderSettings mid-level (e.g. on checkpoint or arena trigger)."); ContinuousSweep = ((BaseUnityPlugin)this).Config.Bind("Lighting", "ContinuousSweep", true, "Keep sweeping for static lights for the entire level, not just the first few seconds after load. ULTRAKILL levels are one big scene, and later rooms often only enable their lights via trigger volumes as you walk in - this catches those. Turn off to use the old fixed-duration behavior below instead."); PostLoadSweepDuration = ((BaseUnityPlugin)this).Config.Bind("Lighting", "PostLoadSweepDuration", 3f, "Only used if ContinuousSweep is off. How many seconds after a scene load to keep sweeping for newly-spawned static lights. Set to 0 to only sweep once immediately on load."); PostLoadSweepInterval = ((BaseUnityPlugin)this).Config.Bind("Lighting", "PostLoadSweepInterval", 0.5f, "How often (in seconds) to re-sweep for static lights, whether during the post-load window or continuously."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "VerboseLogging", false, "Log every light that gets disabled, for troubleshooting."); SceneManager.sceneLoaded += OnSceneLoaded; Harmony val = new Harmony("com.nico.pitchblack"); val.PatchAll(); Log.LogInfo((object)"PitchBlack v1.0.0 loaded. It's dark in here."); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (ModEnabled.Value) { if (KillAmbientLight.Value) { ApplyBlackAmbient(); } if (_sweepRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_sweepRoutine); } if (KillStaticLights.Value) { _sweepRoutine = ((MonoBehaviour)this).StartCoroutine(SweepStaticLights()); } } } private void ApplyBlackAmbient() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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) RenderSettings.ambientMode = (AmbientMode)3; RenderSettings.ambientLight = Color.black; RenderSettings.ambientIntensity = 0f; RenderSettings.ambientSkyColor = Color.black; RenderSettings.ambientEquatorColor = Color.black; RenderSettings.ambientGroundColor = Color.black; RenderSettings.reflectionIntensity = 0f; } private IEnumerator SweepStaticLights() { float elapsed = 0f; KillStaticLightsOnce(); float interval = Mathf.Max(0.05f, PostLoadSweepInterval.Value); while (ContinuousSweep.Value || elapsed < PostLoadSweepDuration.Value) { yield return (object)new WaitForSeconds(interval); elapsed += interval; KillStaticLightsOnce(); } _sweepRoutine = null; } private void KillStaticLightsOnce() { //IL_009a: Unknown result type (might be due to invalid IL or missing references) Light[] array; try { array = Object.FindObjectsOfType(); } catch (Exception arg) { if (VerboseLogging.Value) { Log.LogWarning((object)$"FindObjectsOfType threw, skipping this sweep: {arg}"); } return; } Light[] array2 = array; foreach (Light val in array2) { try { if (!((Object)(object)val == (Object)null) && IsLikelyStatic(val)) { if (VerboseLogging.Value) { Log.LogInfo((object)$"Destroying static light: {((Object)((Component)val).gameObject).name} (type={val.type}, intensity={val.intensity})"); } Object.Destroy((Object)(object)val); } } catch (Exception arg2) { if (VerboseLogging.Value) { Log.LogWarning((object)$"Error handling a light, skipping it: {arg2}"); } } } } private bool IsLikelyStatic(Light light) { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) GameObject gameObject = ((Component)light).gameObject; if ((Object)(object)gameObject.GetComponentInParent() != (Object)null) { return false; } if ((Object)(object)gameObject.GetComponentInParent() != (Object)null) { return false; } string text = ((Object)gameObject).name.ToLowerInvariant(); if (text.Contains("muzzle") || text.Contains("flash") || text.Contains("explo") || text.Contains("projectile") || text.Contains("coin") || text.Contains("beam") || text.Contains("fire") || text.Contains("skull") || text.Contains("main")) { if (VerboseLogging.Value) { Log.LogInfo((object)$"Light {((Object)gameObject).name} (type={light.type}, intensity={light.intensity}) is not static and will not be destroyed."); } return false; } return true; } private void LateUpdate() { if (ModEnabled.Value && KillAmbientLight.Value && ContinuouslyEnforceAmbient.Value) { ApplyBlackAmbient(); } } }