Decompiled source of RepoOptimizerforPentium v1.2.0

RepoOptimizer.dll

Decompiled 38 minutes ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 RepoOptimizer
{
	[BepInPlugin("REPOOptimizer.Performance", "REPO Optimizer", "1.2.0")]
	public class Plugin : BaseUnityPlugin
	{
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
		private class MemStatus
		{
			public uint dwLength = (uint)Marshal.SizeOf(typeof(MemStatus));

			public uint dwMemoryLoad;

			public ulong ullTotalPhys;

			public ulong ullAvailPhys;

			public ulong ullTotalPageFile;

			public ulong ullAvailPageFile;

			public ulong ullTotalVirtual;

			public ulong ullAvailVirtual;

			public ulong ullAvailExtendedVirtual;
		}

		private static Plugin Instance;

		private static ConfigEntry<string> cPreset;

		private static ConfigEntry<bool> cEnabled;

		private static ConfigEntry<bool> cDebugLog;

		private static ConfigEntry<float> cScanInterval;

		private static ConfigEntry<float> cPauseAfterScene;

		private static ConfigEntry<string> cExcludeObjects;

		private static ConfigEntry<bool> cParticles;

		private static ConfigEntry<string> cParticleExclude;

		private static ConfigEntry<float> cParticleScanInterval;

		private static ConfigEntry<bool> cWaterParticles;

		private static ConfigEntry<bool> cHideWater;

		private static ConfigEntry<bool> cWaterOnLow;

		private static ConfigEntry<string> cWaterPartKw;

		private static ConfigEntry<string> cWaterKw;

		private static ConfigEntry<bool> cDistRenderers;

		private static ConfigEntry<bool> cDistLights;

		private static ConfigEntry<bool> cDistAudio;

		private static ConfigEntry<bool> cAnimCull;

		private static ConfigEntry<float> cRenderDist;

		private static ConfigEntry<float> cLightDist;

		private static ConfigEntry<float> cAudioDist;

		private static ConfigEntry<int> cChunk;

		private static ConfigEntry<string> cExcludeLayers;

		private static ConfigEntry<int> cShaderLevel;

		private static ConfigEntry<int> cGlobalLod;

		private static ConfigEntry<string> cShaderExclude;

		private static ConfigEntry<string> cPostTypes;

		private static ConfigEntry<bool> cLogShaders;

		private static ConfigEntry<bool> cPhysics;

		private static ConfigEntry<float> cSleepThreshold;

		private static ConfigEntry<int> cFpsLimit;

		private static ConfigEntry<int> cVsync;

		private static ConfigEntry<bool> cShowFps;

		private static ConfigEntry<KeyCode> cFpsKey;

		private static ConfigEntry<bool> cShadowsOff;

		private static ConfigEntry<bool> cAnisoOff;

		private static ConfigEntry<bool> cSimpleSkin;

		private static ConfigEntry<bool> cReflProbesOff;

		private static ConfigEntry<float> cShadowDist;

		private static ConfigEntry<float> cLodBias;

		private static ConfigEntry<float> cCameraFar;

		private static ConfigEntry<int> cTexLimit;

		private static ConfigEntry<int> cAntiAlias;

		private static ConfigEntry<int> cPixelLights;

		private static ConfigEntry<bool> cMemGuard;

		private static ConfigEntry<bool> cMemCleanup;

		private static ConfigEntry<int> cLowMemMb;

		private static ConfigEntry<string> cTempKw;

		private static ConfigEntry<bool> cCartHud;

		private static ConfigEntry<bool> cEnemyFix;

		private static ConfigEntry<float> cHudOffsetY;

		private static ConfigEntry<string> cHudLabel;

		private static ConfigEntry<KeyCode> cRenderKey;

		private static ConfigEntry<KeyCode> cMuteKey;

		private static ConfigEntry<bool> cRenderCtrl;

		private static ConfigEntry<bool> cMuteCtrl;

		private static ConfigEntry<string> cMuteTarget;

		private static ConfigEntry<string> cVoiceKw;

		private static ConfigEntry<KeyCode> cDumpKey;

		private static ConfigEntry<KeyCode> cInspectKey;

		private static ConfigEntry<bool> cSpawnLog;

		private static List<string> exObjects = new List<string>();

		private static List<string> exParticles = new List<string>();

		private static List<string> exShaders = new List<string>();

		private static List<string> postTypes = new List<string>();

		private static List<string> waterKw = new List<string>();

		private static List<string> waterPartKw = new List<string>();

		private static List<string> tempKw = new List<string>();

		private static List<string> voiceKw = new List<string>();

		private static readonly HashSet<int> exLayers = new HashSet<int>();

		private static readonly List<string> builtinEx = new List<string> { "backdrop", "skybox", "moon" };

		private static readonly Dictionary<int, bool> particleExCache = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> excludeCache = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> textShaderCache = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> waterCache = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> waterShaderCache = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> waterPartCache = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> voiceCache = new Dictionary<int, bool>();

		private static int camMask = -1;

		private static bool pressure;

		private static float spawnWin;

		private static int spawnCnt;

		private static float cartValue = -1f;

		private static readonly HashSet<ParticleSystem> killedParticles = new HashSet<ParticleSystem>();

		private Culler<Renderer> renderCuller;

		private Culler<Light> lightCuller;

		private Culler<AudioSource> audioCuller;

		private readonly HashSet<Rigidbody> sleepDone = new HashSet<Rigidbody>();

		private readonly HashSet<Animator> animDone = new HashSet<Animator>();

		private readonly List<Material> mats = new List<Material>();

		private readonly Dictionary<Material, Shader> originalShaders = new Dictionary<Material, Shader>();

		private readonly HashSet<Material> matSeen = new HashSet<Material>();

		private readonly Dictionary<string, Shader> shaderCache = new Dictionary<string, Shader>();

		private readonly HashSet<Renderer> waterHidden = new HashSet<Renderer>();

		private readonly Dictionary<Camera, int> camMasks = new Dictionary<Camera, int>();

		private readonly HashSet<AudioSource> mutedSources = new HashSet<AudioSource>();

		private readonly Dictionary<int, float> zeroSince = new Dictionary<int, float>();

		private readonly Dictionary<int, int> killTries = new Dictionary<int, int>();

		private int matIdx;

		private int appliedShaderLevel;

		private int scanCount;

		private int crumbLines;

		private bool warnedNoShader;

		private bool loggedShadersThisScene;

		private bool hotkeyBroken;

		private float scanTimer = 1.5f;

		private float particleTimer = 1f;

		private float pauseUntil;

		private float burstUntil;

		private float camRetry;

		private float frameMs = 16f;

		private float memTimer;

		private float lastCleanup = -999f;

		private float renderOffTimer;

		private float muteTimer;

		private float savedVolume = 1f;

		private bool renderOff;

		private bool muted;

		private Camera cam;

		private StreamWriter sw;

		private float cartTimer;

		private bool cartTypesSearched;

		private bool cartErrLogged;

		private Type cartType;

		private Type valuableType;

		private FieldInfo cartDirectField;

		private FieldInfo valuableValueField;

		private GUIStyle hudStyle;

		private float enemyTimer;

		private bool enemyTypesSearched;

		private bool enemyErrLogged;

		private bool enemyNotReadyLogged;

		private bool photonSearched;

		private Type healthType;

		private FieldInfo hpField;

		private FieldInfo deadField;

		private MethodInfo killMethod;

		private PropertyInfo masterProp;

		private const string CartTypeName = "PhysGrabCart";

		private const string ValuableTypeName = "ValuableObject";

		private static readonly string[] CartFieldNames = new string[1] { "haulCurrent" };

		private static readonly string[] ValuableFieldNames = new string[2] { "dollarValueCurrent", "dollarValueOriginal" };

		private const string HealthTypeName = "EnemyHealth";

		private static readonly string[] HpFieldNames = new string[2] { "healthCurrent", "currentHealth" };

		private static readonly string[] DeadFieldNames = new string[2] { "dead", "isDead" };

		private static readonly string[] KillMethodNames = new string[2] { "Death", "Die" };

		private const float ZeroHpGraceSeconds = 3f;

		private static float DistMul
		{
			get
			{
				if (!pressure || !cMemGuard.Value)
				{
					return 1f;
				}
				return 0.5f;
			}
		}

		private static bool WaterHideActive
		{
			get
			{
				if (cEnabled.Value)
				{
					if (!cHideWater.Value)
					{
						if (cWaterOnLow.Value && pressure)
						{
							return cMemGuard.Value;
						}
						return false;
					}
					return true;
				}
				return false;
			}
		}

		private void BindConfig()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_07d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_07db: Expected O, but got Unknown
			cPreset = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Preset", "Custom", new ConfigDescription("Ready-made setting sets. Safe = distance culling only (for troubleshooting), Low = weak PC, Potato = very weak PC. Pick one and save: the mod writes the values into the config below and switches back to Custom. Restart the game afterwards.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "Custom", "Safe", "Low", "Potato" }), Array.Empty<object>()));
			cEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for the whole mod.");
			cScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ScanIntervalSeconds", 5f, "How often the mod looks for new objects in the scene (seconds). Higher = lighter, but new objects are handled later.");
			cPauseAfterScene = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PauseAfterSceneChangeSeconds", 1.5f, "The mod does nothing for this long after a scene change (level load, extraction, shop). Protects against crashes during transitions.");
			cExcludeObjects = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ExcludeObjects", "player,valuable,camera,flashlight,truck,cart,extraction,enemy,map,money,haul,quota,display,screen", "Comma-separated words. Objects whose name (or root object name) contains any of them are never hidden by distance culling or shader replacement.");
			cDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLog", false, "Verbose log of the mod's steps (also written to BepInEx/config/REPO_Optimizer/session.log). For troubleshooting only.");
			cParticles = ((BaseUnityPlugin)this).Config.Bind<bool>("Particles", "RemoveAllParticles", true, "Remove all particle effects (blocks them from playing and disables their rendering).");
			cParticleExclude = ((BaseUnityPlugin)this).Config.Bind<string>("Particles", "Exclude", "enemy", "Comma-separated words. Particles on objects with these words in the name are kept.");
			cParticleScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Particles", "ScanIntervalSeconds", 1.5f, "How often to check for particles that start by themselves.");
			cWaterParticles = ((BaseUnityPlugin)this).Config.Bind<bool>("Water", "RemoveWaterParticles", true, "Remove water particles (splashes, bubbles, foam, ripples). The water itself stays.");
			cWaterPartKw = ((BaseUnityPlugin)this).Config.Bind<string>("Water", "ParticleKeywords", "water,splash,bubble,foam,ripple,droplet", "Words in the particle's name (or its three parents) that identify water particles. Use the Inspect key to find exact names.");
			cHideWater = ((BaseUnityPlugin)this).Config.Bind<bool>("Water", "HideWater", false, "Never render water (matched by keywords below).");
			cWaterOnLow = ((BaseUnityPlugin)this).Config.Bind<bool>("Water", "HideWaterOnLowMemory", true, "Do not render water while the low-memory mode is active.");
			cWaterKw = ((BaseUnityPlugin)this).Config.Bind<string>("Water", "Keywords", "water,liquid,pool", "Words in an object or shader name that identify water.");
			cDistRenderers = ((BaseUnityPlugin)this).Config.Bind<bool>("Distance", "CullRenderers", true, "Hide models farther than RenderDistance.");
			cRenderDist = ((BaseUnityPlugin)this).Config.Bind<float>("Distance", "RenderDistance", 60f, "Distance for models (meters).");
			cDistLights = ((BaseUnityPlugin)this).Config.Bind<bool>("Distance", "CullLights", true, "Turn off distant lights (directional lights are never touched).");
			cLightDist = ((BaseUnityPlugin)this).Config.Bind<float>("Distance", "LightDistance", 40f, "Distance for lights (meters).");
			cDistAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("Distance", "MuteFarAudio", true, "Mute distant 3D sounds (2D sounds and voice chat are never touched).");
			cAudioDist = ((BaseUnityPlugin)this).Config.Bind<float>("Distance", "AudioDistance", 50f, "Distance for sounds (meters).");
			cAnimCull = ((BaseUnityPlugin)this).Config.Bind<bool>("Distance", "AnimatorCulling", false, "Animators skip bone updates while not visible. Can break enemy hitboxes, so it is off by default.");
			cChunk = ((BaseUnityPlugin)this).Config.Bind<int>("Distance", "ObjectsPerFrame", 150, "How many objects of each type are checked per frame. Lower = lighter, but slower reaction.");
			cExcludeLayers = ((BaseUnityPlugin)this).Config.Bind<string>("Distance", "ExcludeLayers", "map", "Comma-separated Unity layer names whose objects are never hidden by distance (needed for the map). Layer names are listed in dump.txt.");
			cShaderLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Shaders", "Level", 1, "0 = off. 1 = disable post-processing only. 2 = also replace shaders with simple ones. 3 = almost everything unlit (picture becomes much simpler).");
			cShaderExclude = ((BaseUnityPlugin)this).Config.Bind<string>("Shaders", "ExcludeShaders", "ui/,textmeshpro,tmp_,sprites/,skybox,hidden/,particles/,unlit/,mobile/,legacy shaders/diffuse", "Shaders with these words in the name are never replaced.");
			cLogShaders = ((BaseUnityPlugin)this).Config.Bind<bool>("Shaders", "LogShaderList", false, "Print the list of shaders used in the scene to the log.");
			cGlobalLod = ((BaseUnityPlugin)this).Config.Bind<int>("Shaders", "GlobalMaximumLOD", 0, "Shader.globalMaximumLOD. 0 = do not touch. For example 200 or 300 simplifies shaders that support LOD.");
			cPostTypes = ((BaseUnityPlugin)this).Config.Bind<string>("Shaders", "PostProcessTypes", "PostProcessVolume,PostProcessLayer", "Names of post-processing components that are disabled when Level >= 1.");
			cPhysics = ((BaseUnityPlugin)this).Config.Bind<bool>("Physics", "SleepFaster", false, "Put idle rigidbodies to sleep faster. Saves CPU but may cause odd item behaviour. Off by default.");
			cSleepThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Physics", "SleepThreshold", 0.05f, "Sleep threshold (Unity default is 0.005). Higher = sleeps sooner.");
			cFpsLimit = ((BaseUnityPlugin)this).Config.Bind<int>("FPS", "FpsLimit", 0, "FPS limit. 0 = do not touch.");
			cVsync = ((BaseUnityPlugin)this).Config.Bind<int>("FPS", "VSync", -1, "-1 = do not touch, 0 = off, 1 = on.");
			cShowFps = ((BaseUnityPlugin)this).Config.Bind<bool>("FPS", "ShowCounter", true, "Show an FPS / frame time counter in the corner.");
			cFpsKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("FPS", "ToggleCounterKey", (KeyCode)290, "Key that shows / hides the counter.");
			cShadowsOff = ((BaseUnityPlugin)this).Config.Bind<bool>("Quality", "ShadowsOff", false, "Turn shadows off completely. One of the biggest FPS gains.");
			cShadowDist = ((BaseUnityPlugin)this).Config.Bind<float>("Quality", "ShadowDistance", 0f, "Shadow distance in meters. 0 = do not touch. For example 15-25.");
			cTexLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Quality", "TextureQualityDrop", 0, "Lower texture resolution: 0 = do not touch, 1 = half, 2 = quarter. Saves a lot of memory.");
			cAntiAlias = ((BaseUnityPlugin)this).Config.Bind<int>("Quality", "AntiAliasing", -1, "-1 = do not touch, 0 = off, 2/4/8 = level.");
			cAnisoOff = ((BaseUnityPlugin)this).Config.Bind<bool>("Quality", "AnisotropicOff", false, "Disable anisotropic texture filtering.");
			cLodBias = ((BaseUnityPlugin)this).Config.Bind<float>("Quality", "LodBias", 0f, "If above 0, caps QualitySettings.lodBias (lower = simpler models sooner). For example 0.5. 0 = do not touch.");
			cPixelLights = ((BaseUnityPlugin)this).Config.Bind<int>("Quality", "PixelLightCount", -1, "Max pixel lights per object. -1 = do not touch, for example 1 or 2.");
			cSimpleSkin = ((BaseUnityPlugin)this).Config.Bind<bool>("Quality", "OneBoneSkinning", false, "Simpler mesh animation (1 bone per vertex). Lighter, slightly rougher animation.");
			cReflProbesOff = ((BaseUnityPlugin)this).Config.Bind<bool>("Quality", "RealtimeReflectionsOff", false, "Disable realtime reflection probes.");
			cCameraFar = ((BaseUnityPlugin)this).Config.Bind<float>("Quality", "CameraFarClip", 0f, "Camera far clip distance in meters. 0 = do not touch. For example 150.");
			cMemGuard = ((BaseUnityPlugin)this).Config.Bind<bool>("MemoryGuard", "Enabled", true, "When free memory is low, switch to a lite mode: all particles blocked, distances halved, water and temporary effects not rendered.");
			cLowMemMb = ((BaseUnityPlugin)this).Config.Bind<int>("MemoryGuard", "LowMemoryMB", 900, "Free memory (MB) below which the lite mode turns on. It turns off again at +300 MB.");
			cMemCleanup = ((BaseUnityPlugin)this).Config.Bind<bool>("MemoryGuard", "CleanupOnLowMemory", true, "Unload unused assets when the lite mode turns on (a short hitch, at most once per 90 s).");
			cTempKw = ((BaseUnityPlugin)this).Config.Bind<string>("MemoryGuard", "TempEffectKeywords", "splash,ripple,gib,debris,chunk,spark,smoke,drip,droplet,blood,fx,effect,bubble,foam", "In lite mode, freshly spawned objects with these words in the name are not rendered.");
			cCartHud = ((BaseUnityPlugin)this).Config.Bind<bool>("Hud", "CartValue", true, "Show a line under the quota with the total value of everything in the cart.");
			cHudOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Hud", "OffsetY", 175f, "Vertical position of the line (pixels at 768p height, scaled).");
			cHudLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Hud", "Label", "In cart:", "Text of the line.");
			cEnemyFix = ((BaseUnityPlugin)this).Config.Bind<bool>("EnemyFix", "KillZeroHpEnemies", true, "If an enemy sits at 0 HP without dying (for example after being thrown into a pit), finish it. Host only.");
			cRenderKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Hotkeys", "RenderNothingKey", (KeyCode)106, "Toggles rendering off completely (nothing is drawn: no enemies, no level). Press again to restore.");
			cRenderCtrl = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotkeys", "RenderNothingNeedsCtrl", false, "Require Ctrl to be held (avoids triggering while typing in chat).");
			cMuteKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Mute", "MuteKey", (KeyCode)109, "Toggles muting everyone / everything you hear. Press again to restore.");
			cMuteCtrl = ((BaseUnityPlugin)this).Config.Bind<bool>("Mute", "MuteNeedsCtrl", false, "Require Ctrl to be held (avoids triggering while typing in chat).");
			cMuteTarget = ((BaseUnityPlugin)this).Config.Bind<string>("Mute", "Target", "Everything", new ConfigDescription("Everything = silence all game audio. VoicesOnly = only audio sources whose name matches VoiceKeywords.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Everything", "VoicesOnly" }), Array.Empty<object>()));
			cVoiceKw = ((BaseUnityPlugin)this).Config.Bind<string>("Mute", "VoiceKeywords", "voice,tts,speech", "Words in an audio source name (or its parents) that identify voice chat. Used only with Target = VoicesOnly.");
			cDumpKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "ClassDumpKey", (KeyCode)291, "Writes a list of game classes (enemy, health, cart, voice) to BepInEx/config/REPO_Optimizer/dump.txt.");
			cInspectKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Debug", "InspectKey", (KeyCode)292, "Aim at an object (within 8 m) and press: writes its renderers, shaders, texts and nearby lights to inspect.txt.");
			cSpawnLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "SpawnLog", false, "Log the names of spawned objects (up to 25 per second) to session.log. Needs DebugLog.");
		}

		private static List<string> Parse(string csv)
		{
			return (from s in (csv ?? "").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim().ToLowerInvariant() into s
				where s.Length > 0
				select s).ToList();
		}

		private static void ParseLists()
		{
			exObjects = Parse(cExcludeObjects.Value);
			exParticles = Parse(cParticleExclude.Value);
			exShaders = Parse(cShaderExclude.Value);
			postTypes = Parse(cPostTypes.Value);
			waterKw = Parse(cWaterKw.Value);
			waterPartKw = Parse(cWaterPartKw.Value);
			tempKw = Parse(cTempKw.Value);
			voiceKw = Parse(cVoiceKw.Value);
			ClearCaches();
			exLayers.Clear();
			List<string> list = Parse(cExcludeLayers.Value);
			for (int i = 0; i < 32; i++)
			{
				string text = LayerMask.LayerToName(i);
				if (!string.IsNullOrEmpty(text) && list.Contains(text.ToLowerInvariant()))
				{
					exLayers.Add(i);
				}
			}
		}

		private static void ClearCaches()
		{
			particleExCache.Clear();
			excludeCache.Clear();
			textShaderCache.Clear();
			waterCache.Clear();
			waterShaderCache.Clear();
			waterPartCache.Clear();
			voiceCache.Clear();
		}

		private static bool NameHas(Component c, List<string> list)
		{
			if (list.Count == 0 || (Object)(object)c == (Object)null)
			{
				return false;
			}
			string text = ((Object)c).name.ToLowerInvariant();
			string text2 = ((Object)c.transform.root).name.ToLowerInvariant();
			foreach (string item in list)
			{
				if (text.Contains(item) || text2.Contains(item))
				{
					return true;
				}
			}
			return false;
		}

		private static bool ListHas(string name, List<string> list)
		{
			string text = name.ToLowerInvariant();
			foreach (string item in list)
			{
				if (text.Contains(item))
				{
					return true;
				}
			}
			return false;
		}

		private static bool Excluded(Component c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)c).GetInstanceID();
			if (!excludeCache.TryGetValue(instanceID, out var value))
			{
				value = NameHas(c, exObjects) || NameHas(c, builtinEx);
				excludeCache[instanceID] = value;
			}
			return value;
		}

		private static bool IsTextRenderer(Renderer r)
		{
			try
			{
				Material sharedMaterial = r.sharedMaterial;
				if ((Object)(object)sharedMaterial == (Object)null)
				{
					return false;
				}
				Shader shader = sharedMaterial.shader;
				if ((Object)(object)shader == (Object)null)
				{
					return false;
				}
				int instanceID = ((Object)shader).GetInstanceID();
				if (!textShaderCache.TryGetValue(instanceID, out var value))
				{
					string name = ((Object)shader).name;
					value = name.IndexOf("TextMeshPro", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("TMP", StringComparison.OrdinalIgnoreCase) >= 0;
					textShaderCache[instanceID] = value;
				}
				return value;
			}
			catch
			{
				return false;
			}
		}

		private static bool SeenByMainCamera(Renderer r)
		{
			if (exLayers.Contains(((Component)r).gameObject.layer))
			{
				return false;
			}
			if (camMask == -1)
			{
				return true;
			}
			return (camMask & (1 << ((Component)r).gameObject.layer)) != 0;
		}

		private static bool IsWaterParticle(ParticleSystem ps)
		{
			if ((Object)(object)ps == (Object)null || waterPartKw.Count == 0)
			{
				return false;
			}
			int instanceID = ((Object)ps).GetInstanceID();
			if (!waterPartCache.TryGetValue(instanceID, out var value))
			{
				value = false;
				Transform val = ((Component)ps).transform;
				for (int i = 0; i < 4; i++)
				{
					if (!((Object)(object)val != (Object)null))
					{
						break;
					}
					if (value)
					{
						break;
					}
					value = ListHas(((Object)val).name, waterPartKw);
					val = val.parent;
				}
				waterPartCache[instanceID] = value;
			}
			return value;
		}

		private static bool IsWaterObject(Component c)
		{
			if ((Object)(object)c == (Object)null || waterKw.Count == 0)
			{
				return false;
			}
			int instanceID = ((Object)c).GetInstanceID();
			if (!waterCache.TryGetValue(instanceID, out var value))
			{
				value = ListHas(((Object)c).name, waterKw);
				waterCache[instanceID] = value;
			}
			return value;
		}

		private static bool IsWaterRenderer(Renderer r)
		{
			if (IsWaterObject((Component)(object)r))
			{
				return true;
			}
			try
			{
				Material sharedMaterial = r.sharedMaterial;
				if ((Object)(object)sharedMaterial == (Object)null || (Object)(object)sharedMaterial.shader == (Object)null)
				{
					return false;
				}
				int instanceID = ((Object)sharedMaterial.shader).GetInstanceID();
				if (!waterShaderCache.TryGetValue(instanceID, out var value))
				{
					value = ListHas(((Object)sharedMaterial.shader).name, waterKw);
					waterShaderCache[instanceID] = value;
				}
				return value;
			}
			catch
			{
				return false;
			}
		}

		private static bool IsVoiceSource(AudioSource a)
		{
			if ((Object)(object)a == (Object)null || voiceKw.Count == 0)
			{
				return false;
			}
			int instanceID = ((Object)a).GetInstanceID();
			if (!voiceCache.TryGetValue(instanceID, out var value))
			{
				value = false;
				Transform val = ((Component)a).transform;
				for (int i = 0; i < 4; i++)
				{
					if (!((Object)(object)val != (Object)null))
					{
						break;
					}
					if (value)
					{
						break;
					}
					value = ListHas(((Object)val).name, voiceKw);
					val = val.parent;
				}
				voiceCache[instanceID] = value;
			}
			return value;
		}

		private static string[] SplitNames(string s)
		{
			return (from x in (s ?? "").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select x.Trim() into x
				where x.Length > 0
				select x).ToArray();
		}

		private static FieldInfo FindField(Type t, string name)
		{
			Type type = t;
			while (type != null)
			{
				FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
				type = type.BaseType;
			}
			return null;
		}

		private static bool IsNumeric(Type t)
		{
			if (!(t == typeof(int)) && !(t == typeof(float)) && !(t == typeof(double)) && !(t == typeof(long)))
			{
				return t == typeof(short);
			}
			return true;
		}

		private static double ToDouble(object v)
		{
			try
			{
				return Convert.ToDouble(v, CultureInfo.InvariantCulture);
			}
			catch
			{
				return 0.0;
			}
		}

		private static string FormatMoney(float v)
		{
			return ((long)Math.Round(v)).ToString("N0", CultureInfo.InvariantCulture).Replace(',', ' ');
		}

		private static string OutDir()
		{
			string text = Path.Combine(Paths.ConfigPath, "REPO_Optimizer");
			Directory.CreateDirectory(text);
			return text;
		}

		private static void ApplyPresetIfNeeded()
		{
			string value = cPreset.Value;
			if (!(value == "Custom"))
			{
				cPreset.Value = "Custom";
				if (value == "Safe")
				{
					cParticles.Value = false;
					cWaterParticles.Value = false;
					cDistRenderers.Value = true;
					cRenderDist.Value = 70f;
					cDistLights.Value = false;
					cDistAudio.Value = false;
					cAnimCull.Value = false;
					cShaderLevel.Value = 0;
					cPhysics.Value = false;
					cShadowsOff.Value = false;
					cShadowDist.Value = 0f;
					cTexLimit.Value = 0;
					cAntiAlias.Value = -1;
					cAnisoOff.Value = false;
					cLodBias.Value = 0f;
					cPixelLights.Value = -1;
					cSimpleSkin.Value = false;
					cReflProbesOff.Value = false;
					cCameraFar.Value = 0f;
					cVsync.Value = -1;
					cMemGuard.Value = false;
					cHideWater.Value = false;
					SavePreset();
				}
				else
				{
					bool flag = value == "Potato";
					cParticles.Value = true;
					cDistRenderers.Value = true;
					cRenderDist.Value = (flag ? 35f : 50f);
					cDistLights.Value = true;
					cLightDist.Value = (flag ? 18f : 30f);
					cDistAudio.Value = true;
					cAudioDist.Value = (flag ? 30f : 40f);
					cAnimCull.Value = false;
					cChunk.Value = (flag ? 80 : 150);
					cScanInterval.Value = (flag ? 8f : 5f);
					cPauseAfterScene.Value = (flag ? 2f : 1.5f);
					cShaderLevel.Value = 1;
					cVsync.Value = 0;
					cMemGuard.Value = true;
					cLowMemMb.Value = (flag ? 1000 : 800);
					cShadowsOff.Value = flag;
					cShadowDist.Value = (flag ? 0f : 20f);
					cTexLimit.Value = (flag ? 1 : 0);
					cAntiAlias.Value = 0;
					cAnisoOff.Value = true;
					cLodBias.Value = 0f;
					cPixelLights.Value = 2;
					cSimpleSkin.Value = flag;
					cReflProbesOff.Value = true;
					cCameraFar.Value = (flag ? 150f : 0f);
					SavePreset();
				}
			}
		}

		private static void SavePreset()
		{
			try
			{
				if ((Object)(object)Instance != (Object)null)
				{
					((BaseUnityPlugin)Instance).Config.Save();
				}
			}
			catch
			{
			}
		}

		private static void ApplyQuality()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Invalid comparison between Unknown and I4
			try
			{
				if (cEnabled.Value)
				{
					if (cShadowsOff.Value && (int)QualitySettings.shadows != 0)
					{
						QualitySettings.shadows = (ShadowQuality)0;
					}
					if (cShadowDist.Value > 0f && QualitySettings.shadowDistance > cShadowDist.Value)
					{
						QualitySettings.shadowDistance = cShadowDist.Value;
					}
					if (cTexLimit.Value > 0 && QualitySettings.globalTextureMipmapLimit != cTexLimit.Value)
					{
						QualitySettings.globalTextureMipmapLimit = cTexLimit.Value;
					}
					if (cAntiAlias.Value >= 0 && QualitySettings.antiAliasing != cAntiAlias.Value)
					{
						QualitySettings.antiAliasing = cAntiAlias.Value;
					}
					if (cAnisoOff.Value && (int)QualitySettings.anisotropicFiltering != 0)
					{
						QualitySettings.anisotropicFiltering = (AnisotropicFiltering)0;
					}
					if (cLodBias.Value > 0f && QualitySettings.lodBias > cLodBias.Value)
					{
						QualitySettings.lodBias = cLodBias.Value;
					}
					if (cPixelLights.Value >= 0 && QualitySettings.pixelLightCount != cPixelLights.Value)
					{
						QualitySettings.pixelLightCount = cPixelLights.Value;
					}
					if (cSimpleSkin.Value && (int)QualitySettings.skinWeights != 1)
					{
						QualitySettings.skinWeights = (SkinWeights)1;
					}
					if (cReflProbesOff.Value && QualitySettings.realtimeReflectionProbes)
					{
						QualitySettings.realtimeReflectionProbes = false;
					}
				}
			}
			catch
			{
			}
		}

		private static void ApplyFpsSettings()
		{
			if (cFpsLimit.Value > 0)
			{
				Application.targetFrameRate = cFpsLimit.Value;
			}
			if (cVsync.Value >= 0)
			{
				QualitySettings.vSyncCount = Mathf.Clamp(cVsync.Value, 0, 1);
			}
		}

		private static void ApplyLod()
		{
			if (cGlobalLod.Value > 0)
			{
				Shader.globalMaximumLOD = cGlobalLod.Value;
			}
		}

		private void Awake()
		{
			Instance = this;
			BindConfig();
			ApplyPresetIfNeeded();
			ParseLists();
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				ApplyPresetIfNeeded();
				ParseLists();
				ApplyFpsSettings();
				ApplyLod();
				ApplyQuality();
			};
			renderCuller = new Culler<Renderer>((Renderer r) => !(r is ParticleSystemRenderer) && !Excluded((Component)(object)r) && SeenByMainCamera(r) && !IsTextRenderer(r), (Renderer r) => r.enabled, delegate(Renderer r, bool v)
			{
				r.enabled = v;
			}, delegate(Renderer r, Vector3 c)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				Bounds bounds = r.bounds;
				return ((Bounds)(ref bounds)).SqrDistance(c);
			});
			lightCuller = new Culler<Light>((Light l) => (int)l.type != 1 && !Excluded((Component)(object)l), (Light l) => ((Behaviour)l).enabled, delegate(Light l, bool v)
			{
				((Behaviour)l).enabled = v;
			}, delegate(Light l, Vector3 c)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)l).transform.position - c;
				return ((Vector3)(ref val)).sqrMagnitude;
			});
			audioCuller = new Culler<AudioSource>((AudioSource a) => a.spatialBlend > 0.01f && !Excluded((Component)(object)a), (AudioSource a) => !a.mute, delegate(AudioSource a, bool v)
			{
				a.mute = !v;
			}, delegate(AudioSource a, Vector3 c)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)a).transform.position - c;
				return ((Vector3)(ref val)).sqrMagnitude;
			});
			PatchHarmony();
			SceneManager.sceneLoaded += OnSceneLoaded;
			SceneManager.sceneUnloaded += OnSceneUnloaded;
			Application.quitting += OnQuitting;
			ApplyFpsSettings();
			ApplyLod();
			ApplyQuality();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Game " + Application.version + ", Unity " + Application.unityVersion));
			((BaseUnityPlugin)this).Logger.LogInfo((object)"REPO Optimizer loaded.");
		}

		private void PatchHarmony()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			try
			{
				Harmony val = new Harmony("REPOOptimizer.Performance");
				MethodInfo methodInfo = AccessTools.Method(typeof(ParticleSystem), "Play", new Type[1] { typeof(bool) }, (Type[])null);
				val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ParticlePatch).GetMethod("Prefix")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Harmony: ParticleSystem.Play patched.");
				try
				{
					HarmonyMethod val2 = new HarmonyMethod(typeof(SpawnPatch).GetMethod("Postfix"));
					Type typeFromHandle = typeof(Object);
					Type[][] obj = new Type[4][]
					{
						new Type[1] { typeFromHandle },
						new Type[3]
						{
							typeFromHandle,
							typeof(Vector3),
							typeof(Quaternion)
						},
						new Type[2]
						{
							typeFromHandle,
							typeof(Transform)
						},
						new Type[4]
						{
							typeFromHandle,
							typeof(Vector3),
							typeof(Quaternion),
							typeof(Transform)
						}
					};
					int num = 0;
					Type[][] array = obj;
					foreach (Type[] array2 in array)
					{
						MethodInfo methodInfo2 = AccessTools.Method(typeFromHandle, "Instantiate", array2, (Type[])null);
						if (methodInfo2 != null)
						{
							val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							num++;
						}
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Harmony: Instantiate overloads patched: " + num));
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Harmony Instantiate: " + ex.Message));
				}
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Harmony particle patch failed, using scanning only: " + ex2.Message));
			}
		}

		private void OnSceneLoaded(Scene s, LoadSceneMode m)
		{
			Crumb("scene event: " + ((Scene)(ref s)).name);
			renderCuller.RestoreAll();
			renderCuller.Reset();
			lightCuller.RestoreAll();
			lightCuller.Reset();
			audioCuller.RestoreAll();
			audioCuller.Reset();
			pauseUntil = Time.unscaledTime + Mathf.Max(0f, cPauseAfterScene.Value);
			scanCount = 0;
			cartValue = -1f;
			zeroSince.Clear();
			killTries.Clear();
			ApplyQuality();
			killedParticles.Clear();
			ClearCaches();
			sleepDone.Clear();
			animDone.Clear();
			mats.Clear();
			matSeen.Clear();
			originalShaders.Clear();
			matIdx = 0;
			appliedShaderLevel = 0;
			loggedShadersThisScene = false;
			cam = null;
			scanTimer = 0.5f;
			particleTimer = 0.3f;
			burstUntil = Time.unscaledTime + 25f;
		}

		private void OnSceneUnloaded(Scene s)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			OnSceneLoaded(s, (LoadSceneMode)0);
		}

		private void OnQuitting()
		{
			try
			{
				if (muted)
				{
					muted = false;
					RestoreAudio();
				}
				Crumb("quitting");
				if (sw != null)
				{
					sw.Close();
				}
			}
			catch
			{
			}
		}

		private Camera GetCam()
		{
			if ((Object)(object)cam != (Object)null && ((Behaviour)cam).isActiveAndEnabled)
			{
				if (!renderOff)
				{
					camMask = cam.cullingMask;
				}
				return cam;
			}
			if (Time.unscaledTime < camRetry)
			{
				return null;
			}
			camRetry = Time.unscaledTime + 1f;
			cam = Camera.main;
			if ((Object)(object)cam == (Object)null)
			{
				Camera[] allCameras = Camera.allCameras;
				foreach (Camera val in allCameras)
				{
					if ((Object)(object)val.targetTexture == (Object)null)
					{
						cam = val;
						break;
					}
				}
			}
			if ((Object)(object)cam != (Object)null && !renderOff)
			{
				camMask = cam.cullingMask;
			}
			return cam;
		}

		private void Crumb(string m)
		{
			if (!cDebugLog.Value)
			{
				return;
			}
			try
			{
				if (sw == null)
				{
					sw = new StreamWriter(new FileStream(Path.Combine(OutDir(), "session.log"), FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
					{
						AutoFlush = true
					};
				}
				if (++crumbLines > 4000)
				{
					crumbLines = 0;
					sw.Close();
					sw = new StreamWriter(new FileStream(Path.Combine(OutDir(), "session.log"), FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
					{
						AutoFlush = true
					};
					sw.WriteLine("--- rotated ---");
				}
				sw.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " " + m);
			}
			catch
			{
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[dbg] " + m));
		}

		private void Safe(string name, Action a)
		{
			try
			{
				Crumb(name);
				a();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)(name + ": " + ex.Message));
			}
		}

		private void Update()
		{
			try
			{
				UpdateInner();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Update: " + ex.Message));
			}
		}

		private void UpdateInner()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0421: Unknown result type (might be due to invalid IL or missing references)
			//IL_0426: Unknown result type (might be due to invalid IL or missing references)
			//IL_045f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0489: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b3: Unknown result type (might be due to invalid IL or missing references)
			frameMs = Mathf.Lerp(frameMs, Time.unscaledDeltaTime * 1000f, 0.05f);
			if (!hotkeyBroken)
			{
				try
				{
					if (Input.GetKeyDown(cFpsKey.Value))
					{
						cShowFps.Value = !cShowFps.Value;
					}
					if (Input.GetKeyDown(cDumpKey.Value))
					{
						DumpClasses();
					}
					if (Input.GetKeyDown(cInspectKey.Value))
					{
						InspectTarget();
					}
					if (Input.GetKeyDown(cRenderKey.Value) && (!cRenderCtrl.Value || Input.GetKey((KeyCode)306)))
					{
						ToggleRenderOff();
					}
					if (Input.GetKeyDown(cMuteKey.Value) && (!cMuteCtrl.Value || Input.GetKey((KeyCode)306)))
					{
						ToggleMute();
					}
				}
				catch
				{
					hotkeyBroken = true;
				}
			}
			if (renderOff)
			{
				renderOffTimer -= Time.unscaledDeltaTime;
				if (renderOffTimer <= 0f)
				{
					renderOffTimer = 0.5f;
					ApplyRenderOff();
				}
			}
			if (muted)
			{
				muteTimer -= Time.unscaledDeltaTime;
				if (muteTimer <= 0f)
				{
					muteTimer = 1f;
					ApplyMute();
				}
			}
			memTimer -= Time.unscaledDeltaTime;
			if (memTimer <= 0f)
			{
				memTimer = 1f;
				try
				{
					CheckMemory();
				}
				catch
				{
				}
				ApplyQuality();
			}
			if (!cEnabled.Value)
			{
				renderCuller.RestoreAll();
				lightCuller.RestoreAll();
				audioCuller.RestoreAll();
				RestoreShaders();
			}
			else
			{
				if (Time.unscaledTime < pauseUntil)
				{
					return;
				}
				enemyTimer -= Time.unscaledDeltaTime;
				if (enemyTimer <= 0f)
				{
					enemyTimer = 1f;
					if (cEnemyFix.Value)
					{
						try
						{
							CheckZeroHpEnemies();
						}
						catch (Exception ex)
						{
							if (!enemyErrLogged)
							{
								enemyErrLogged = true;
								((BaseUnityPlugin)this).Logger.LogWarning((object)("EnemyFix: " + ex.Message));
							}
						}
					}
				}
				cartTimer -= Time.unscaledDeltaTime;
				if (cartTimer <= 0f)
				{
					cartTimer = 2f;
					if (cCartHud.Value)
					{
						try
						{
							UpdateCartValue();
						}
						catch (Exception ex2)
						{
							if (!cartErrLogged)
							{
								cartErrLogged = true;
								((BaseUnityPlugin)this).Logger.LogWarning((object)("CartValue: " + ex2.Message));
							}
							cartValue = -1f;
						}
					}
				}
				if (!cDistRenderers.Value)
				{
					renderCuller.RestoreAll();
				}
				if (!cDistLights.Value)
				{
					lightCuller.RestoreAll();
				}
				if (!cDistAudio.Value)
				{
					audioCuller.RestoreAll();
				}
				particleTimer -= Time.unscaledDeltaTime;
				if (particleTimer <= 0f)
				{
					particleTimer = ((Time.unscaledTime < burstUntil) ? 0.5f : Mathf.Max(0.2f, cParticleScanInterval.Value));
					if (cParticles.Value || cWaterParticles.Value || WaterHideActive || (pressure && cMemGuard.Value))
					{
						ScanParticles();
					}
				}
				scanTimer -= Time.unscaledDeltaTime;
				if (scanTimer <= 0f)
				{
					scanTimer = ((Time.unscaledTime < burstUntil) ? 1.5f : Mathf.Max(1f, cScanInterval.Value));
					Scan();
				}
				Camera val = GetCam();
				if ((Object)(object)val != (Object)null)
				{
					if (cCameraFar.Value > 0f && val.farClipPlane > cCameraFar.Value)
					{
						val.farClipPlane = cCameraFar.Value;
					}
					Vector3 position = ((Component)val).transform.position;
					int num = Mathf.Max(10, cChunk.Value);
					if (Time.unscaledTime < burstUntil)
					{
						num *= 4;
					}
					if (cDistRenderers.Value)
					{
						renderCuller.Step(position, cRenderDist.Value * DistMul, num);
					}
					if (cDistLights.Value)
					{
						lightCuller.Step(position, cLightDist.Value * DistMul, num);
					}
					if (cDistAudio.Value)
					{
						audioCuller.Step(position, cAudioDist.Value * DistMul, num);
					}
				}
				ShaderStep();
			}
		}

		private void Scan()
		{
			Crumb("scan start");
			Renderer[] renderers = null;
			Safe("find renderers", delegate
			{
				renderers = Object.FindObjectsByType<Renderer>((FindObjectsSortMode)0);
			});
			if (renderers == null)
			{
				renderers = (Renderer[])(object)new Renderer[0];
			}
			scanCount++;
			if (cDistRenderers.Value)
			{
				Safe("rescan renderers", delegate
				{
					renderCuller.Rescan(renderers);
				});
			}
			if (cDistLights.Value)
			{
				Safe("rescan lights", delegate
				{
					lightCuller.Rescan(Object.FindObjectsByType<Light>((FindObjectsSortMode)0));
				});
			}
			if (cDistAudio.Value)
			{
				Safe("rescan audio", delegate
				{
					audioCuller.Rescan(Object.FindObjectsByType<AudioSource>((FindObjectsSortMode)0));
				});
			}
			if (cAnimCull.Value)
			{
				Safe("animators", delegate
				{
					Animator[] array = Object.FindObjectsByType<Animator>((FindObjectsSortMode)0);
					foreach (Animator val in array)
					{
						if (!((Object)(object)val == (Object)null) && !animDone.Contains(val))
						{
							val.cullingMode = (AnimatorCullingMode)1;
							animDone.Add(val);
						}
					}
				});
			}
			if (cPhysics.Value)
			{
				Safe("physics", delegate
				{
					sleepDone.RemoveWhere((Rigidbody r) => (Object)(object)r == (Object)null);
					Rigidbody[] array = Object.FindObjectsByType<Rigidbody>((FindObjectsSortMode)0);
					foreach (Rigidbody val in array)
					{
						if (!((Object)(object)val == (Object)null) && !sleepDone.Contains(val))
						{
							val.sleepThreshold = cSleepThreshold.Value;
							sleepDone.Add(val);
						}
					}
				});
			}
			Safe("water", delegate
			{
				ApplyWater(renderers);
			});
			if (cShaderLevel.Value >= 1 && (scanCount <= 3 || scanCount % 6 == 0))
			{
				Safe("postprocess", DisablePostProcessing);
			}
			if (cShaderLevel.Value >= 2 || cLogShaders.Value)
			{
				Safe("materials", delegate
				{
					CollectMaterials(renderers);
				});
			}
			if (cLogShaders.Value && !loggedShadersThisScene)
			{
				Safe("log shaders", LogShaders);
			}
			Safe("quality", ApplyQuality);
			Crumb("scan end");
		}

		private void ScanParticles()
		{
			ParticleSystem[] array = Object.FindObjectsByType<ParticleSystem>((FindObjectsSortMode)0);
			foreach (ParticleSystem val in array)
			{
				if (!((Object)(object)val == (Object)null) && !killedParticles.Contains(val) && ((pressure && cMemGuard.Value) || (WaterHideActive && IsWaterObject((Component)(object)val)) || (cWaterParticles.Value && IsWaterParticle(val)) || (cParticles.Value && !NameHas((Component)(object)val, exParticles))))
				{
					KillParticle(val);
					killedParticles.Add(val);
				}
			}
		}

		internal static void KillParticle(ParticleSystem ps)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ps.Stop(true, (ParticleSystemStopBehavior)0);
				MainModule main = ps.main;
				((MainModule)(ref main)).maxParticles = 0;
				EmissionModule emission = ps.emission;
				((EmissionModule)(ref emission)).enabled = false;
				ParticleSystemRenderer component = ((Component)ps).GetComponent<ParticleSystemRenderer>();
				if ((Object)(object)component != (Object)null)
				{
					((Renderer)component).enabled = false;
				}
			}
			catch
			{
			}
		}

		internal static bool ShouldBlockParticle(ParticleSystem ps)
		{
			try
			{
				if (!cEnabled.Value || (Object)(object)ps == (Object)null)
				{
					return false;
				}
				if (pressure && cMemGuard.Value)
				{
					return true;
				}
				if (cWaterParticles.Value && IsWaterParticle(ps))
				{
					return true;
				}
				if (WaterHideActive && IsWaterObject((Component)(object)ps))
				{
					return true;
				}
				if (!cParticles.Value)
				{
					return false;
				}
				if (exParticles.Count == 0)
				{
					return true;
				}
				int instanceID = ((Object)ps).GetInstanceID();
				if (!particleExCache.TryGetValue(instanceID, out var value))
				{
					value = NameHas((Component)(object)ps, exParticles);
					particleExCache[instanceID] = value;
				}
				return !value;
			}
			catch
			{
				return false;
			}
		}

		private void ApplyWater(Renderer[] rs)
		{
			if (WaterHideActive)
			{
				foreach (Renderer val in rs)
				{
					if (!((Object)(object)val == (Object)null) && val.enabled && !(val is ParticleSystemRenderer) && IsWaterRenderer(val))
					{
						val.enabled = false;
						waterHidden.Add(val);
					}
				}
			}
			else
			{
				if (waterHidden.Count <= 0)
				{
					return;
				}
				foreach (Renderer item in waterHidden)
				{
					if ((Object)(object)item != (Object)null)
					{
						item.enabled = true;
					}
				}
				waterHidden.Clear();
			}
		}

		internal static void OnSpawned(Object result)
		{
			try
			{
				bool flag = pressure && cMemGuard.Value;
				if (!flag && (!cSpawnLog.Value || !cDebugLog.Value))
				{
					return;
				}
				GameObject val = (GameObject)(object)((result is GameObject) ? result : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				string name = ((Object)val).name;
				if (cSpawnLog.Value && cDebugLog.Value && (Object)(object)Instance != (Object)null)
				{
					float unscaledTime = Time.unscaledTime;
					if (unscaledTime - spawnWin > 1f)
					{
						spawnWin = unscaledTime;
						spawnCnt = 0;
					}
					if (++spawnCnt <= 25)
					{
						Instance.Crumb("spawn: " + name + (flag ? " [lowmem]" : ""));
					}
				}
				if (!flag || (!ListHas(name, tempKw) && (!WaterHideActive || !ListHas(name, waterKw))))
				{
					return;
				}
				Renderer[] componentsInChildren = val.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val2 in componentsInChildren)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						val2.enabled = false;
					}
				}
			}
			catch
			{
			}
		}

		private void DisablePostProcessing()
		{
			if (postTypes.Count == 0)
			{
				return;
			}
			Behaviour[] array = Object.FindObjectsByType<Behaviour>((FindObjectsSortMode)0);
			foreach (Behaviour val in array)
			{
				if (!((Object)(object)val == (Object)null) && val.enabled)
				{
					string item = ((object)val).GetType().Name.ToLowerInvariant();
					if (postTypes.Contains(item))
					{
						val.enabled = false;
					}
				}
			}
		}

		private void CollectMaterials(Renderer[] renderers)
		{
			foreach (Renderer val in renderers)
			{
				if ((Object)(object)val == (Object)null || val is ParticleSystemRenderer || Excluded((Component)(object)val))
				{
					continue;
				}
				Material[] sharedMaterials = val.sharedMaterials;
				foreach (Material val2 in sharedMaterials)
				{
					if (!((Object)(object)val2 == (Object)null) && !matSeen.Contains(val2))
					{
						matSeen.Add(val2);
						mats.Add(val2);
					}
				}
			}
		}

		private void LogShaders()
		{
			loggedShadersThisScene = true;
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (Material mat in mats)
			{
				if (!((Object)(object)mat == (Object)null) && !((Object)(object)mat.shader == (Object)null))
				{
					string name = ((Object)mat.shader).name;
					dictionary[name] = ((!dictionary.ContainsKey(name)) ? 1 : (dictionary[name] + 1));
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"=== Shaders in scene (name : materials) ===");
			foreach (KeyValuePair<string, int> item in dictionary.OrderByDescending((KeyValuePair<string, int> k) => k.Value))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)(item.Key + " : " + item.Value));
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"===========================================");
		}

		private Shader FindShader(params string[] names)
		{
			foreach (string text in names)
			{
				if (!shaderCache.TryGetValue(text, out var value))
				{
					value = Shader.Find(text);
					shaderCache[text] = value;
				}
				if ((Object)(object)value != (Object)null)
				{
					return value;
				}
			}
			return null;
		}

		private void RestoreShaders()
		{
			if (originalShaders.Count == 0)
			{
				appliedShaderLevel = 0;
				return;
			}
			foreach (KeyValuePair<Material, Shader> originalShader in originalShaders)
			{
				if ((Object)(object)originalShader.Key != (Object)null && (Object)(object)originalShader.Value != (Object)null)
				{
					originalShader.Key.shader = originalShader.Value;
				}
			}
			originalShaders.Clear();
			matIdx = 0;
			appliedShaderLevel = 0;
		}

		private void ShaderStep()
		{
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			int value = cShaderLevel.Value;
			if (value < 2)
			{
				RestoreShaders();
				return;
			}
			if (appliedShaderLevel != value)
			{
				RestoreShaders();
				appliedShaderLevel = value;
			}
			if (mats.Count == 0)
			{
				return;
			}
			Shader val = ((value >= 3) ? FindShader("Unlit/Texture", "Mobile/Unlit (Supports Lightmap)") : FindShader("Legacy Shaders/Diffuse", "Mobile/Diffuse"));
			if ((Object)(object)val == (Object)null)
			{
				if (!warnedNoShader)
				{
					warnedNoShader = true;
					((BaseUnityPlugin)this).Logger.LogWarning((object)"No suitable simple shader found in the game build - shader replacement is not possible.");
				}
				return;
			}
			int num = Mathf.Max(10, cChunk.Value / 3);
			for (int i = 0; i < num; i++)
			{
				if (mats.Count <= 0)
				{
					break;
				}
				if (matIdx >= mats.Count)
				{
					matIdx = 0;
				}
				Material val2 = mats[matIdx++];
				if ((Object)(object)val2 == (Object)null || originalShaders.ContainsKey(val2) || (Object)(object)val2.shader == (Object)null || (Object)(object)val2.shader == (Object)(object)val)
				{
					continue;
				}
				string text = ((Object)val2.shader).name.ToLowerInvariant();
				bool flag = false;
				foreach (string exShader in exShaders)
				{
					if (text.Contains(exShader))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					continue;
				}
				try
				{
					Texture val3 = null;
					Color val4 = Color.white;
					bool flag2 = false;
					if (val2.HasProperty("_MainTex"))
					{
						val3 = val2.GetTexture("_MainTex");
					}
					if ((Object)(object)val3 == (Object)null && val2.HasProperty("_BaseMap"))
					{
						val3 = val2.GetTexture("_BaseMap");
					}
					if (val2.HasProperty("_Color"))
					{
						val4 = val2.GetColor("_Color");
						flag2 = true;
					}
					else if (val2.HasProperty("_BaseColor"))
					{
						val4 = val2.GetColor("_BaseColor");
						flag2 = true;
					}
					originalShaders[val2] = val2.shader;
					val2.shader = val;
					if ((Object)(object)val3 != (Object)null && val2.HasProperty("_MainTex"))
					{
						val2.SetTexture("_MainTex", val3);
					}
					if (flag2 && val2.HasProperty("_Color"))
					{
						val2.SetColor("_Color", val4);
					}
				}
				catch
				{
				}
			}
		}

		[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern bool GlobalMemoryStatusEx([In][Out] MemStatus lpBuffer);

		private void CheckMemory()
		{
			if (!cMemGuard.Value)
			{
				pressure = false;
				return;
			}
			MemStatus memStatus = new MemStatus();
			if (!GlobalMemoryStatusEx(memStatus))
			{
				return;
			}
			double num = (double)Math.Min(memStatus.ullAvailPhys, memStatus.ullAvailPageFile) / 1048576.0;
			bool flag = pressure;
			if (!pressure && num < (double)cLowMemMb.Value)
			{
				pressure = true;
			}
			else if (pressure && num > (double)(cLowMemMb.Value + 300))
			{
				pressure = false;
			}
			if (pressure != flag)
			{
				string text = (pressure ? "LOW MEMORY: lite mode ON" : "memory OK: lite mode off") + " (free " + num.ToString("0") + " MB)";
				((BaseUnityPlugin)this).Logger.LogWarning((object)text);
				Crumb(text);
				if (pressure && cMemCleanup.Value && Time.unscaledTime - lastCleanup > 90f)
				{
					lastCleanup = Time.unscaledTime;
					((MonoBehaviour)this).StartCoroutine(CleanupRoutine());
				}
			}
		}

		private IEnumerator CleanupRoutine()
		{
			Crumb("cleanup: UnloadUnusedAssets");
			yield return Resources.UnloadUnusedAssets();
			GC.Collect();
			Crumb("cleanup done");
		}

		private void FindCartTypes()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (!assembly.GetName().Name.StartsWith("Assembly-CSharp"))
				{
					continue;
				}
				Type[] array;
				try
				{
					array = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					array = ex.Types.Where((Type t) => t != null).ToArray();
				}
				Type[] array2 = array;
				foreach (Type type in array2)
				{
					if (cartType == null && type.Name == "PhysGrabCart")
					{
						cartType = type;
					}
					if (valuableType == null && type.Name == "ValuableObject")
					{
						valuableType = type;
					}
				}
			}
			if (cartType != null)
			{
				string[] cartFieldNames = CartFieldNames;
				foreach (string name in cartFieldNames)
				{
					FieldInfo fieldInfo = FindField(cartType, name);
					if (fieldInfo != null && IsNumeric(fieldInfo.FieldType))
					{
						cartDirectField = fieldInfo;
						break;
					}
				}
			}
			if (valuableType != null)
			{
				string[] cartFieldNames = ValuableFieldNames;
				foreach (string name2 in cartFieldNames)
				{
					FieldInfo fieldInfo2 = FindField(valuableType, name2);
					if (fieldInfo2 != null && IsNumeric(fieldInfo2.FieldType))
					{
						valuableValueField = fieldInfo2;
						break;
					}
				}
			}
			string text = "Cart HUD: cart=" + ((cartType != null) ? cartType.Name : "NOT FOUND") + ", direct field=" + ((cartDirectField != null) ? cartDirectField.Name : "none") + ", valuable=" + ((valuableType != null) ? valuableType.Name : "NOT FOUND") + ", value field=" + ((valuableValueField != null) ? valuableValueField.Name : "none");
			((BaseUnityPlugin)this).Logger.LogInfo((object)text);
		}

		private void UpdateCartValue()
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			if (!cartTypesSearched)
			{
				cartTypesSearched = true;
				FindCartTypes();
			}
			if (cartType == null)
			{
				cartValue = -1f;
				return;
			}
			Object[] array = Object.FindObjectsByType(cartType, (FindObjectsSortMode)0);
			if (array.Length == 0)
			{
				cartValue = -1f;
				return;
			}
			Object[] array2;
			if (cartDirectField != null)
			{
				double num = 0.0;
				array2 = array;
				foreach (Object val in array2)
				{
					if (!(val == (Object)null))
					{
						num += ToDouble(cartDirectField.GetValue(val));
					}
				}
				cartValue = (float)num;
				return;
			}
			if (valuableType == null || valuableValueField == null)
			{
				cartValue = -1f;
				return;
			}
			List<Bounds> list = new List<Bounds>();
			array2 = array;
			foreach (Object obj in array2)
			{
				Component val2 = (Component)(object)((obj is Component) ? obj : null);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				bool flag = false;
				Bounds item = default(Bounds);
				Collider[] componentsInChildren = val2.GetComponentsInChildren<Collider>(false);
				foreach (Collider val3 in componentsInChildren)
				{
					if (!((Object)(object)val3 == (Object)null) && val3.enabled)
					{
						if (!flag)
						{
							item = val3.bounds;
							flag = true;
						}
						else
						{
							((Bounds)(ref item)).Encapsulate(val3.bounds);
						}
					}
				}
				if (flag)
				{
					((Bounds)(ref item)).Expand(0.2f);
					list.Add(item);
				}
			}
			double num2 = 0.0;
			if (list.Count > 0)
			{
				array2 = Object.FindObjectsByType(valuableType, (FindObjectsSortMode)0);
				foreach (Object obj2 in array2)
				{
					Component val4 = (Component)(object)((obj2 is Component) ? obj2 : null);
					if ((Object)(object)val4 == (Object)null)
					{
						continue;
					}
					Vector3 position = val4.transform.position;
					foreach (Bounds item2 in list)
					{
						Bounds current = item2;
						if (((Bounds)(ref current)).Contains(position))
						{
							num2 += ToDouble(valuableValueField.GetValue(val4));
							break;
						}
					}
				}
			}
			cartValue = (float)num2;
		}

		private bool IsHost()
		{
			if (!photonSearched)
			{
				photonSearched = true;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					Type type = null;
					try
					{
						type = assembly.GetType("Photon.Pun.PhotonNetwork", throwOnError: false);
					}
					catch
					{
					}
					if (!(type == null))
					{
						masterProp = type.GetProperty("IsMasterClient", BindingFlags.Static | BindingFlags.Public);
						break;
					}
				}
			}
			if (masterProp == null)
			{
				return false;
			}
			try
			{
				return (bool)masterProp.GetValue(null, null);
			}
			catch
			{
				return false;
			}
		}

		private void FindEnemyTypes()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (!assembly.GetName().Name.StartsWith("Assembly-CSharp"))
				{
					continue;
				}
				Type[] array;
				try
				{
					array = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					array = ex.Types.Where((Type t) => t != null).ToArray();
				}
				Type[] array2 = array;
				foreach (Type type in array2)
				{
					if (type.Name == "EnemyHealth")
					{
						healthType = type;
						break;
					}
				}
				if (healthType != null)
				{
					break;
				}
			}
			if (healthType != null)
			{
				string[] hpFieldNames = HpFieldNames;
				foreach (string name in hpFieldNames)
				{
					FieldInfo fieldInfo = FindField(healthType, name);
					if (fieldInfo != null && IsNumeric(fieldInfo.FieldType))
					{
						hpField = fieldInfo;
						break;
					}
				}
				hpFieldNames = DeadFieldNames;
				foreach (string name2 in hpFieldNames)
				{
					FieldInfo fieldInfo2 = FindField(healthType, name2);
					if (fieldInfo2 != null && fieldInfo2.FieldType == typeof(bool))
					{
						deadField = fieldInfo2;
						break;
					}
				}
				hpFieldNames = KillMethodNames;
				foreach (string n in hpFieldNames)
				{
					MethodInfo[] array3 = (from m in healthType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
						where m.Name == n
						orderby m.GetParameters().Length
						select m).ToArray();
					if (array3.Length != 0)
					{
						killMethod = array3[0];
						break;
					}
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("EnemyFix: class=" + ((healthType != null) ? healthType.Name : "NOT FOUND") + ", hp=" + ((hpField != null) ? hpField.Name : "none") + ", dead flag=" + ((deadField != null) ? deadField.Name : "none") + ", kill method=" + ((killMethod != null) ? killMethod.Name : "none")));
		}

		private void CheckZeroHpEnemies()
		{
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			if (!IsHost())
			{
				return;
			}
			if (!enemyTypesSearched)
			{
				enemyTypesSearched = true;
				FindEnemyTypes();
			}
			if (healthType == null || hpField == null || deadField == null || killMethod == null)
			{
				if (!enemyNotReadyLogged)
				{
					enemyNotReadyLogged = true;
					((BaseUnityPlugin)this).Logger.LogWarning((object)"EnemyFix inactive: the game's enemy health members were not found (the game may have changed).");
				}
				return;
			}
			float unscaledTime = Time.unscaledTime;
			Object[] array = Object.FindObjectsByType(healthType, (FindObjectsSortMode)0);
			foreach (Object obj in array)
			{
				Component val = (Component)(object)((obj is Component) ? obj : null);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)val).GetInstanceID();
				bool flag = false;
				try
				{
					flag = (bool)deadField.GetValue(val);
				}
				catch
				{
				}
				double num = ToDouble(hpField.GetValue(val));
				float value;
				if (flag || num > 0.0)
				{
					zeroSince.Remove(instanceID);
				}
				else if (!zeroSince.TryGetValue(instanceID, out value))
				{
					zeroSince[instanceID] = unscaledTime;
				}
				else
				{
					if (unscaledTime - value < 3f)
					{
						continue;
					}
					killTries.TryGetValue(instanceID, out var value2);
					if (value2 >= 3)
					{
						continue;
					}
					killTries[instanceID] = value2 + 1;
					zeroSince[instanceID] = unscaledTime;
					ParameterInfo[] parameters = killMethod.GetParameters();
					object[] array2 = new object[parameters.Length];
					for (int j = 0; j < parameters.Length; j++)
					{
						Type parameterType = parameters[j].ParameterType;
						if (parameterType == typeof(Vector3))
						{
							array2[j] = Vector3.zero;
						}
						else if (parameterType.IsValueType)
						{
							array2[j] = Activator.CreateInstance(parameterType);
						}
						else
						{
							array2[j] = null;
						}
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)("EnemyFix: finishing '" + ((Object)val.transform.root).name + "' (HP=" + num + ", attempt " + (value2 + 1) + ")"));
					killMethod.Invoke(val, array2);
				}
			}
		}

		private void ToggleRenderOff()
		{
			try
			{
				renderOff = !renderOff;
				if (renderOff)
				{
					ApplyRenderOff();
				}
				else
				{
					RestoreRender();
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Rendering " + (renderOff ? "OFF" : "on")));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("ToggleRenderOff: " + ex.Message));
			}
		}

		private void ApplyRenderOff()
		{
			Camera[] allCameras = Camera.allCameras;
			foreach (Camera val in allCameras)
			{
				if (!((Object)(object)val == (Object)null))
				{
					if (!camMasks.ContainsKey(val))
					{
						camMasks[val] = val.cullingMask;
					}
					val.cullingMask = 0;
				}
			}
		}

		private void RestoreRender()
		{
			foreach (KeyValuePair<Camera, int> camMask in camMasks)
			{
				if ((Object)(object)camMask.Key != (Object)null)
				{
					camMask.Key.cullingMask = camMask.Value;
				}
			}
			camMasks.Clear();
		}

		private void ToggleMute()
		{
			try
			{
				muted = !muted;
				if (muted)
				{
					if (AudioListener.volume > 0.001f)
					{
						savedVolume = AudioListener.volume;
					}
					muteTimer = 0f;
					ApplyMute();
				}
				else
				{
					RestoreAudio();
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Mute " + (muted ? "ON" : "off") + " (" + cMuteTarget.Value + ")"));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("ToggleMute: " + ex.Message));
			}
		}

		private void ApplyMute()
		{
			if (cMuteTarget.Value == "VoicesOnly")
			{
				AudioSource[] array = Object.FindObjectsByType<AudioSource>((FindObjectsSortMode)0);
				foreach (AudioSource val in array)
				{
					if (!((Object)(object)val == (Object)null) && !val.mute && !mutedSources.Contains(val) && IsVoiceSource(val))
					{
						val.mute = true;
						mutedSources.Add(val);
					}
				}
			}
			else if (AudioListener.volume != 0f)
			{
				AudioListener.volume = 0f;
			}
		}

		private void RestoreAudio()
		{
			AudioListener.volume = savedVolume;
			foreach (AudioSource mutedSource in mutedSources)
			{
				if ((Object)(object)mutedSource != (Object)null)
				{
					mutedSource.mute = false;
				}
			}
			mutedSources.Clear();
		}

		private void OnGUI()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected O, but got Unknown
			float num = 34f;
			if (renderOff)
			{
				GUI.Label(new Rect(10f, num, 600f, 24f), "Rendering is OFF - press " + ((object)cRenderKey.Value/*cast due to .constrained prefix*/).ToString() + " to restore");
				num += 22f;
			}
			if (muted)
			{
				GUI.Label(new Rect(10f, num, 600f, 24f), "Muted - press " + ((object)cMuteKey.Value/*cast due to .constrained prefix*/).ToString() + " to unmute");
			}
			if (cShowFps.Value)
			{
				float num2 = 1000f / Mathf.Max(0.1f, frameMs);
				GUI.Label(new Rect(10f, 10f, 300f, 24f), $"FPS {num2:0}  ({frameMs:0.0} ms)");
			}
			if (cCartHud.Value && cartValue >= 0f)
			{
				float num3 = (float)Screen.height / 768f;
				if (hudStyle == null)
				{
					hudStyle = new GUIStyle(GUI.skin.label);
					hudStyle.alignment = (TextAnchor)2;
					hudStyle.fontStyle = (FontStyle)1;
				}
				hudStyle.fontSize = Mathf.Max(12, Mathf.RoundToInt(22f * num3));
				string text = cHudLabel.Value + " $" + FormatMoney(cartValue);
				Rect val = default(Rect);
				((Rect)(ref val))..ctor((float)Screen.width - 582f * num3, cHudOffsetY.Value * num3, 560f * num3, 40f * num3);
				hudStyle.normal.textColor = Color.black;
				GUI.Label(new Rect(((Rect)(ref val)).x + 2f, ((Rect)(ref val)).y + 2f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), text, hudStyle);
				hudStyle.normal.textColor = new Color(1f, 0.85f, 0.3f);
				GUI.Label(val, text, hudStyle);
			}
		}

		private void InspectTarget()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0473: Unknown result type (might be due to invalid IL or missing references)
			//IL_047a: Unknown result type (might be due to invalid IL or missing references)
			//IL_047f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0484: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_0590: Unknown result type (might be due to invalid IL or missing references)
			//IL_0595: Unknown result type (might be due to invalid IL or missing references)
			//IL_059a: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05dc: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Camera val = GetCam();
				if ((Object)(object)val == (Object)null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Inspect: no camera found");
					return;
				}
				RaycastHit[] array = Physics.RaycastAll(((Component)val).transform.position, ((Component)val).transform.forward, 8f, -1, (QueryTriggerInteraction)1);
				Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("=== Inspect " + DateTime.Now.ToString("s") + " ===");
				stringBuilder.AppendLine("Quality: pixelLights=" + QualitySettings.pixelLightCount + " shadows=" + ((object)QualitySettings.shadows/*cast due to .constrained prefix*/).ToString() + " texLimit=" + QualitySettings.globalTextureMipmapLimit + " lodBias=" + QualitySettings.lodBias + " globalLOD=" + Shader.globalMaximumLOD);
				if (array.Length == 0)
				{
					stringBuilder.AppendLine("The ray hit nothing. Aim at an object closer than 8 m.");
				}
				else
				{
					RaycastHit val2 = array[0];
					RaycastHit[] array2 = array;
					for (int num = 0; num < array2.Length; num++)
					{
						RaycastHit val3 = array2[num];
						if (!((Object)((Component)((RaycastHit)(ref val3)).collider).transform.root).name.ToLowerInvariant().Contains("player"))
						{
							val2 = val3;
							break;
						}
					}
					Transform root = ((Component)((RaycastHit)(ref val2)).collider).transform.root;
					Vector3 pt = ((RaycastHit)(ref val2)).point;
					stringBuilder.AppendLine("HIT '" + ((Object)((RaycastHit)(ref val2)).collider).name + "' root '" + ((Object)root).name + "' layer " + LayerMask.LayerToName(((Component)((RaycastHit)(ref val2)).collider).gameObject.layer) + " dist " + ((RaycastHit)(ref val2)).distance.ToString("0.0"));
					foreach (Renderer item in (from r in ((Component)root).GetComponentsInChildren<Renderer>(true)
						where (Object)(object)r != (Object)null
						select r).OrderBy(delegate(Renderer r)
					{
						//IL_0001: Unknown result type (might be due to invalid IL or missing references)
						//IL_0006: Unknown result type (might be due to invalid IL or missing references)
						//IL_000a: Unknown result type (might be due to invalid IL or missing references)
						Bounds bounds = r.bounds;
						return ((Bounds)(ref bounds)).SqrDistance(pt);
					}).Take(40))
					{
						List<string> list = new List<string>();
						Material[] sharedMaterials = item.sharedMaterials;
						foreach (Material val4 in sharedMaterials)
						{
							if ((Object)(object)val4 != (Object)null && (Object)(object)val4.shader != (Object)null)
							{
								list.Add(((Object)val4.shader).name);
							}
						}
						stringBuilder.AppendLine("    R " + ((object)item).GetType().Name + " '" + ((Object)item).name + "' enabled=" + item.enabled + " active=" + ((Component)item).gameObject.activeInHierarchy + " layer=" + LayerMask.LayerToName(((Component)item).gameObject.layer) + " hiddenByMod=" + renderCuller.IsHidden(item) + " excluded=" + Excluded((Component)(object)item) + " isText=" + IsTextRenderer(item) + " shaders=[" + string.Join("; ", list.ToArray()) + "]");
					}
					Component[] componentsInChildren = ((Component)root).GetComponentsInChildren<Component>(true);
					Vector3 val6;
					foreach (Component val5 in componentsInChildren)
					{
						if ((Object)(object)val5 == (Object)null)
						{
							continue;
						}
						string name = ((object)val5).GetType().Name;
						if (name.IndexOf("TextMeshPro", StringComparison.OrdinalIgnoreCase) < 0)
						{
							continue;
						}
						val6 = val5.transform.position - pt;
						if (((Vector3)(ref val6)).sqrMagnitude > 64f)
						{
							continue;
						}
						string text = "";
						try
						{
							PropertyInfo property = ((object)val5).GetType().GetProperty("text");
							if (property != null)
							{
								text = Convert.ToString(property.GetValue(val5, null));
							}
						}
						catch
						{
						}
						Behaviour val7 = (Behaviour)(object)((val5 is Behaviour) ? val5 : null);
						stringBuilder.AppendLine("    TEXT " + name + " '" + ((Object)val5).name + "' enabled=" + (((Object)(object)val7 != (Object)null) ? val7.enabled.ToString() : "?") + " text='" + text + "'");
					}
					Light[] array3 = Object.FindObjectsByType<Light>((FindObjectsSortMode)0);
					foreach (Light val8 in array3)
					{
						if (!((Object)(object)val8 == (Object)null))
						{
							val6 = ((Component)val8).transform.position - pt;
							float magnitude = ((Vector3)(ref val6)).magnitude;
							if (!(magnitude > 12f))
							{
								stringBuilder.AppendLine("    LIGHT '" + ((Object)val8).name + "' type=" + ((object)val8.type/*cast due to .constrained prefix*/).ToString() + " enabled=" + ((Behaviour)val8).enabled + " active=" + ((Component)val8).gameObject.activeInHierarchy + " hiddenByMod=" + lightCuller.IsHidden(val8) + " range=" + val8.range.ToString("0.0") + " intensity=" + val8.intensity.ToString("0.0") + " dist=" + magnitude.ToString("0.0"));
							}
						}
					}
				}
				string text2 = Path.Combine(OutDir(), "inspect.txt");
				File.AppendAllText(text2, stringBuilder.ToString() + "\n");
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Inspect written: " + text2));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("InspectTarget: " + ex.Message));
			}
		}

		private void DumpClasses()
		{
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("=== Dump " + DateTime.Now.ToString("s") + " ===");
				stringBuilder.AppendLine("Unity layers:");
				for (int i = 0; i < 32; i++)
				{
					string text = LayerMask.LayerToName(i);
					if (!string.IsNullOrEmpty(text))
					{
						stringBuilder.AppendLine("    " + i + " = " + text);
					}
				}
				stringBuilder.AppendLine("Main camera mask: " + camMask);
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					string name = assembly.GetName().Name;
					if (!name.StartsWith("Assembly-CSharp"))
					{
						continue;
					}
					Type[] array;
					try
					{
						array = assembly.GetTypes();
					}
					catch (ReflectionTypeLoadException ex)
					{
						array = ex.Types.Where((Type t) => t != null).ToArray();
					}
					stringBuilder.AppendLine("Assembly " + name + ", types: " + array.Length);
					Type[] array2 = array;
					foreach (Type type in array2)
					{
						string text2 = type.Name.ToLowerInvariant();
						if (!text2.Contains("mouth") && !text2.Contains("voice") && !text2.Contains("speak") && !text2.Contains("cart") && !text2.Contains("enemy") && !text2.Contains("valuable") && !text2.Contains("orb") && !text2.Contains("health") && !text2.Contains("stun") && !text2.Contains("death"))
						{
							continue;
						}
						stringBuilder.AppendLine("TYPE " + type.FullName + " : " + ((type.BaseType != null) ? type.BaseType.Name : ""));
						if (!text2.Contains("mouth") && !text2.Contains("voice") && !text2.Contains("cart") && !text2.Contains("health") && !text2.Contains("stun") && !text2.Contains("valuable") && !text2.Contains("death") && !(text2 == "enemy") && !(text2 == "enemyparent"))
						{
							continue;
						}
						FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (FieldInfo fieldInfo in fields)
						{
							stringBuilder.AppendLine("    field  " + fieldInfo.FieldType.Name + " " + fieldInfo.Name);
						}
						MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (MethodInfo methodInfo in methods)
						{
							stringBuilder.AppendLine("    method " + methodInfo.ReturnType.Name + " " + methodInfo.Name + "(" + string.Join(", ", (from x in methodInfo.GetParameters()
								select x.ParameterType.Name + " " + x.Name).ToArray()) + ")");
						}
					}
				}
				string text3 = Path.Combine(OutDir(), "dump.txt");
				File.WriteAllText(text3, stringBuilder.ToString());
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Dump written: " + text3));
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("DumpClasses: " + ex2.Message));
			}
		}
	}
	internal static class ParticlePatch
	{
		public static bool Prefix(ParticleSystem __instance)
		{
			if (Plugin.ShouldBlockParticle(__instance))
			{
				return false;
			}
			return true;
		}
	}
	internal static class SpawnPatch
	{
		public static void Postfix(Object __result)
		{
			Plugin.OnSpawned(__result);
		}
	}
	internal class Culler<T> where T : Component
	{
		private readonly List<T> list = new List<T>();

		private readonly HashSet<T> hidden = new HashSet<T>();

		private readonly Func<T, bool> include;

		private readonly Func<T, bool> isOn;

		private readonly Action<T, bool> setOn;

		private readonly Func<T, Vector3, float> sqrDist;

		private int idx;

		public Culler(Func<T, bool> include, Func<T, bool> isOn, Action<T, bool> setOn, Func<T, Vector3, float> sqrDist)
		{
			this.include = include;
			this.isOn = isOn;
			this.setOn = setOn;
			this.sqrDist = sqrDist;
		}

		public void Rescan(IEnumerable<T> source)
		{
			list.Clear();
			foreach (T item in source)
			{
				if ((Object)(object)item != (Object)null && include(item))
				{
					list.Add(item);
				}
			}
			hidden.RemoveWhere((T h) => (Object)(object)h == (Object)null);
		}

		public void Step(Vector3 cam, float max, int budget)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			int count = list.Count;
			if (count == 0)
			{
				return;
			}
			float num = max * max;
			float num2 = max * 0.9f * (max * 0.9f);
			for (int i = 0; i < budget && i < count; i++)
			{
				if (idx >= count)
				{
					idx = 0;
				}
				T val = list[idx++];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				float num3 = sqrDist(val, cam);
				if (hidden.Contains(val))
				{
					if (num3 < num2)
					{
						setOn(val, arg2: true);
						hidden.Remove(val);
					}
				}
				else if (isOn(val) && num3 > num)
				{
					setOn(val, arg2: false);
					hidden.Add(val);
				}
			}
		}

		public bool IsHidden(T o)
		{
			return hidden.Contains(o);
		}

		public void RestoreAll()
		{
			if (hidden.Count == 0)
			{
				return;
			}
			foreach (T item in hidden)
			{
				if ((Object)(object)item != (Object)null)
				{
					setOn(item, arg2: true);
				}
			}
			hidden.Clear();
		}

		public void Reset()
		{
			list.Clear();
			hidden.Clear();
			idx = 0;
		}
	}
}