Decompiled source of MistlandsControl v1.0.1

BepInEx/plugins/dannanboi_MistlandsControl.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("MistlandsControl")]
[assembly: AssemblyDescription("Configurable Mistlands resources, cultivation, Wisplight upgrades, and Wisp Tower.")]
[assembly: AssemblyCompany("Dannan Boi")]
[assembly: AssemblyProduct("MistlandsControl")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyVersion("1.0.1.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 MistlandsControl
{
	internal static class ConfigLayout
	{
		internal static void OrderWisplight(string path)
		{
			if (!File.Exists(path))
			{
				return;
			}
			List<string> list = new List<string>(File.ReadAllLines(path));
			int num = list.FindIndex((string line) => line.Trim() == "[5 - Wisplight]");
			if (num < 0)
			{
				return;
			}
			int num2;
			for (num2 = num + 1; num2 < list.Count && !list[num2].TrimStart().StartsWith("[", StringComparison.Ordinal); num2++)
			{
			}
			string[] array = new string[7] { "Enable Section", "Level 2 Title", "Level 2 Description", "Level 2 Recipe", "Level 3 Title", "Level 3 Description", "Level 3 Recipe" };
			List<KeyValuePair<int, List<string>>> list2 = new List<KeyValuePair<int, List<string>>>();
			List<string> list3 = new List<string>();
			for (int num3 = num + 1; num3 < num2; num3++)
			{
				string text = list[num3];
				string text2 = text.TrimStart();
				list3.Add(text);
				int num4 = text.IndexOf('=');
				if (num4 >= 0 && !text2.StartsWith("#", StringComparison.Ordinal))
				{
					int num5 = Array.IndexOf(array, text.Substring(0, num4).Trim());
					list2.Add(new KeyValuePair<int, List<string>>((num5 < 0) ? array.Length : num5, list3));
					list3 = new List<string>();
				}
			}
			bool flag = true;
			for (int num6 = 1; num6 < list2.Count; num6++)
			{
				if (list2[num6 - 1].Key > list2[num6].Key)
				{
					flag = false;
				}
			}
			if (flag)
			{
				return;
			}
			list2.Sort((KeyValuePair<int, List<string>> left, KeyValuePair<int, List<string>> right) => left.Key.CompareTo(right.Key));
			List<string> list4 = new List<string>();
			foreach (KeyValuePair<int, List<string>> item in list2)
			{
				list4.AddRange(item.Value);
			}
			list4.AddRange(list3);
			list.RemoveRange(num + 1, num2 - num - 1);
			list.InsertRange(num + 1, list4);
			File.WriteAllLines(path, list);
		}
	}
	internal static class FarmingRules
	{
		[ThreadStatic]
		private static bool cultivating;

		internal static bool Begin(Player player, GameObject ghost)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Invalid comparison between Unknown and I4
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Invalid comparison between Unknown and I4
			bool result = cultivating;
			LiveConfig instance = LiveConfig.Instance;
			cultivating = false;
			if (instance == null || !instance.Ready || !instance.Current.Enabled || !instance.Current.FarmingSection || !instance.Current.Farming || !Object.op_Implicit((Object)(object)player) || player != Player.m_localPlayer || !Object.op_Implicit((Object)(object)ghost))
			{
				return result;
			}
			TerrainOp component = ghost.GetComponent<TerrainOp>();
			if (Object.op_Implicit((Object)(object)component))
			{
				cultivating = component.m_settings != null && component.m_settings.m_paintCleared && (int)component.m_settings.m_paintType == 1;
			}
			else
			{
				TerrainModifier component2 = ghost.GetComponent<TerrainModifier>();
				cultivating = Object.op_Implicit((Object)(object)component2) && component2.m_paintCleared && (int)component2.m_paintType == 1;
			}
			return result;
		}

		internal static void End(bool previous)
		{
			cultivating = previous;
		}

		internal static void Clear()
		{
			cultivating = false;
		}

		internal static float PlacementMask(Heightmap heightmap, Vector3 position, float nativeMask)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			if (cultivating && nativeMask < 0.25f && Object.op_Implicit((Object)(object)heightmap) && (int)heightmap.GetBiome(position, 0.02f, false) == 512)
			{
				return 0.25f;
			}
			return nativeMask;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacementGhost", new Type[] { typeof(bool) })]
	internal static class CultivatePlacement
	{
		private static void Prefix(Player __instance, GameObject ___m_placementGhost, out bool __state)
		{
			__state = FarmingRules.Begin(__instance, ___m_placementGhost);
		}

		private static void Postfix(bool __state)
		{
			FarmingRules.End(__state);
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			FarmingRules.End(__state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Heightmap), "GetVegetationMask", new Type[] { typeof(Vector3) })]
	internal static class CultivableMistlandsGround
	{
		private static void Postfix(Heightmap __instance, Vector3 __0, ref float __result)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			__result = FarmingRules.PlacementMask(__instance, __0, __result);
		}
	}
	internal static class FarmingPaint
	{
		private static readonly FieldInfo CompilerMap = AccessTools.Field(typeof(TerrainComp), "m_hmap");

		private static readonly FieldInfo CompilerWidth = AccessTools.Field(typeof(TerrainComp), "m_width");

		private static readonly MethodInfo Poke = AccessTools.Method(typeof(Heightmap), "Poke", new Type[2]
		{
			typeof(int),
			typeof(bool)
		}, (Type[])null) ?? AccessTools.Method(typeof(Heightmap), "Poke", new Type[1] { typeof(bool) }, (Type[])null);

		private static readonly MethodInfo StoreMethod = typeof(FarmingPaint).GetMethod("Store", BindingFlags.Static | BindingFlags.NonPublic);

		private static readonly MethodInfo PixelMethod = typeof(FarmingPaint).GetMethod("Pixel", BindingFlags.Static | BindingFlags.NonPublic);

		private static readonly MethodInfo RenderMethod = typeof(FarmingPaint).GetMethod("RenderPixel", BindingFlags.Static | BindingFlags.NonPublic);

		private static bool wasEnabled;

		internal static bool Enabled
		{
			get
			{
				if (LiveConfig.Instance != null && LiveConfig.Instance.Ready && LiveConfig.Instance.Current.Enabled && LiveConfig.Instance.Current.FarmingSection)
				{
					return LiveConfig.Instance.Current.Farming;
				}
				return false;
			}
		}

		internal static Color VisibleCultivation(Heightmap map, int x, int y, Color color, PaintType type)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Invalid comparison between Unknown and I4
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled || !Object.op_Implicit((Object)(object)map) || (int)type != 1 || color.g <= 0.5f || color.a >= 1f)
			{
				return color;
			}
			Vector3 position = ((Component)map).transform.position;
			position.x += ((float)(x - map.m_width / 2) - 0.5f) * map.m_scale;
			position.z += ((float)(y - map.m_width / 2) - 0.5f) * map.m_scale;
			if ((int)map.GetBiome(position, 0.02f, false) == 512)
			{
				color.a = 1f;
			}
			return color;
		}

		internal static void Store(Color[] mask, int index, Color color, Heightmap map, int width, PaintType type)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			mask[index] = VisibleCultivation(map, index % (width + 1), index / (width + 1), color, type);
		}

		internal static void Pixel(Texture2D texture, int x, int y, Color color, Heightmap map, PaintType type)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			texture.SetPixel(x, y, VisibleCultivation(map, x, y, color, type));
		}

		internal static void RenderPixel(Texture2D texture, int x, int y, Color color, Heightmap map)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			Pixel(texture, x, y, color, map, (PaintType)1);
		}

		internal static void RefreshLoadedTerrain()
		{
			bool enabled = Enabled;
			if (enabled == wasEnabled)
			{
				return;
			}
			wasEnabled = enabled;
			object[] parameters = ((Poke.GetParameters().Length != 2) ? new object[1] { false } : new object[2] { 2, false });
			foreach (Heightmap allHeightmap in Heightmap.GetAllHeightmaps())
			{
				if (Object.op_Implicit((Object)(object)allHeightmap))
				{
					Poke.Invoke(allHeightmap, parameters);
				}
			}
		}

		internal static void ValidateApi()
		{
			if (CompilerMap?.FieldType != typeof(Heightmap) || CompilerWidth?.FieldType != typeof(int) || Poke == null)
			{
				throw new MissingMemberException("Mistlands cultivation painting is incompatible with this Valheim build.");
			}
		}

		private static void AddType(List<CodeInstruction> output, MethodBase method)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			ParameterInfo[] parameters = method.GetParameters();
			for (int i = 0; i < parameters.Length; i++)
			{
				if (parameters[i].ParameterType == typeof(PaintType))
				{
					output.Add(new CodeInstruction(OpCodes.Ldarg, (object)(short)(i + 1)));
					return;
				}
				if (parameters[i].ParameterType == typeof(Settings))
				{
					output.Add(new CodeInstruction(OpCodes.Ldarg, (object)(short)(i + 1)));
					output.Add(new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Settings), "m_paintType")));
					return;
				}
			}
			throw new MissingMemberException("Native terrain paint type was not found.");
		}

		internal static IEnumerable<CodeInstruction> Rewrite(IEnumerable<CodeInstruction> instructions, MethodBase method, bool compiled, bool rendering)
		{
			//IL_00b1: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			List<CodeInstruction> list2 = new List<CodeInstruction>();
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				bool num2 = compiled && item.opcode == OpCodes.Stelem && object.Equals(item.operand, typeof(Color));
				bool flag = !compiled && item.operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(Texture2D) && methodInfo.Name == "SetPixel" && methodInfo.GetParameters().Length == 3;
				if (!num2 && !flag)
				{
					list2.Add(item);
					continue;
				}
				CodeInstruction val = new CodeInstruction(item)
				{
					opcode = OpCodes.Ldarg_0,
					operand = null
				};
				if (rendering)
				{
					ParameterInfo[] parameters = method.GetParameters();
					int num3 = -1;
					for (int i = 0; i < parameters.Length; i++)
					{
						if (parameters[i].ParameterType == typeof(Heightmap))
						{
							num3 = i + 1;
						}
					}
					if (num3 < 0)
					{
						throw new MissingMemberException("Native terrain render heightmap argument was not found.");
					}
					val.opcode = OpCodes.Ldarg;
					val.operand = (short)num3;
				}
				list2.Add(val);
				if (compiled)
				{
					list2.Add(new CodeInstruction(OpCodes.Ldfld, (object)CompilerMap));
					list2.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					list2.Add(new CodeInstruction(OpCodes.Ldfld, (object)CompilerWidth));
				}
				if (!rendering)
				{
					AddType(list2, method);
				}
				list2.Add(new CodeInstruction(OpCodes.Call, (object)(compiled ? StoreMethod : (rendering ? RenderMethod : PixelMethod))));
				num++;
			}
			if (num != 1)
			{
				throw new InvalidOperationException("Native " + method.DeclaringType.Name + "." + method.Name + " paint write changed; expected one, found " + num + ".");
			}
			return list2;
		}
	}
	[HarmonyPatch(typeof(TerrainComp), "PaintCleared")]
	internal static class CompiledCultivationPaint
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			return FarmingPaint.Rewrite(instructions, __originalMethod, compiled: true, rendering: false);
		}
	}
	[HarmonyPatch(typeof(Heightmap), "PaintCleared")]
	internal static class LegacyCultivationPaint
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			return FarmingPaint.Rewrite(instructions, __originalMethod, compiled: false, rendering: false);
		}
	}
	[HarmonyPatch(typeof(TerrainComp), "ApplyToHeightmap")]
	internal static class SavedCultivationPaint
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			return FarmingPaint.Rewrite(instructions, __originalMethod, compiled: false, rendering: true);
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? ReadOnly;

		public int? Order;

		public bool? ShowRangeAsPercent;
	}
	internal sealed class LiveConfig : IDisposable
	{
		internal sealed class SaveScope
		{
			private LiveConfig owner;

			internal SaveScope(LiveConfig value)
			{
				owner = value;
			}

			internal void Finish()
			{
				if (owner == null)
				{
					return;
				}
				LiveConfig liveConfig = owner;
				owner = null;
				try
				{
					liveConfig.ApplyValues(liveConfig.Current);
				}
				finally
				{
					liveConfig.applying--;
				}
			}
		}

		internal sealed class ReloadScope
		{
			private LiveConfig owner;

			private readonly Settings before;

			internal ReloadScope(LiveConfig value, Settings previous)
			{
				owner = value;
				before = previous;
			}

			internal void Finish(Exception error)
			{
				if (owner == null)
				{
					return;
				}
				LiveConfig liveConfig = owner;
				owner = null;
				try
				{
					try
					{
						liveConfig.Local = ((error == null) ? liveConfig.Capture() : before);
					}
					catch (FormatException ex)
					{
						liveConfig.Local = before;
						Plugin.Warn("Config reload not applied: " + ex.Message);
					}
					liveConfig.ApplyValues(liveConfig.Current);
				}
				finally
				{
					liveConfig.applying--;
				}
				liveConfig.Refresh?.Invoke();
				if (!liveConfig.Client && !liveConfig.Local.Same(before))
				{
					liveConfig.AuthorityChanged?.Invoke();
				}
			}
		}

		internal static LiveConfig Instance;

		internal readonly ConfigFile File;

		internal readonly ConfigEntry<bool> Enabled;

		internal readonly ConfigEntry<bool> Marble;

		internal readonly ConfigEntry<bool> Farming;

		internal readonly ConfigEntry<bool> Wisplight;

		internal readonly ConfigEntry<bool> WispTower;

		internal readonly ConfigEntry<bool> MarbleSection;

		internal readonly ConfigEntry<bool> FarmingSection;

		internal readonly ConfigEntry<bool> DemistingSection;

		internal readonly ConfigEntry<float> MarbleYield;

		internal readonly ConfigEntry<float> TowerMultiplier;

		internal readonly ConfigEntry<float> LightIntensity;

		internal readonly ConfigEntry<string> TowerRecipe;

		internal readonly ConfigEntry<string> Upgrade2;

		internal readonly ConfigEntry<string> Upgrade3;

		internal readonly ConfigEntry<string> Title2;

		internal readonly ConfigEntry<string> Description2;

		internal readonly ConfigEntry<string> Title3;

		internal readonly ConfigEntry<string> Description3;

		internal readonly ConfigurationManagerAttributes SettingsTag = new ConfigurationManagerAttributes();

		internal Settings Local;

		internal Settings Remote;

		private int applying;

		private readonly bool autoSave;

		private bool disposed;

		private readonly FileInfo configInfo;

		private DateTime observedWrite;

		private long observedLength;

		private float nextPoll;

		private float reloadAt;

		private bool filePending;

		private string fileError;

		private readonly List<ConfigurationManagerAttributes> tags = new List<ConfigurationManagerAttributes>();

		internal bool Client { get; private set; }

		internal bool Ready
		{
			get
			{
				if (Client)
				{
					return Remote != null;
				}
				return true;
			}
		}

		internal Settings Current
		{
			get
			{
				if (!Client || Remote == null)
				{
					return Local;
				}
				return Remote;
			}
		}

		internal event Action AuthorityChanged;

		internal event Action Refresh;

		private ConfigDescription Describe(string text, int order, AcceptableValueBase acceptable = null)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
			{
				Order = order,
				ShowRangeAsPercent = false
			};
			tags.Add(configurationManagerAttributes);
			return new ConfigDescription(text, acceptable, new object[1] { configurationManagerAttributes });
		}

		internal LiveConfig(ConfigFile file)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			File = file;
			configInfo = new FileInfo(file.ConfigFilePath);
			autoSave = file.SaveOnConfigSet;
			file.SaveOnConfigSet = false;
			Enabled = file.Bind<bool>("1 - Settings", "Enable Mod", true, new ConfigDescription("Enable MistlandsControl. The server/host controls this toggle in multiplayer; offline play uses local settings. All config-manager changes apply immediately. Saved config-file edits reload automatically after a short debounce; no restart is required.", (AcceptableValueBase)null, new object[1] { SettingsTag }));
			MarbleSection = file.Bind<bool>("2 - Black Marble", "Enable Section", true, Describe("Enable Black Marble features. Disabling removes this section's gameplay patches, including mining conversion and yield scaling. Server-synced and locked in multiplayer.", 100));
			Marble = file.Bind<bool>("2 - Black Marble", "Mine from any stone", true, Describe("Use the native Ashlands Grausten/Stone mining drop table for natural Mistlands cliffs and Stone-only rocks, replacing Grausten with Black Marble. Other biomes and ore deposits are unchanged. Amounts, weights, chances, and world resource scaling follow the Ashlands table. False restores native Stone drops; Black Marble Yield still applies to natural marble sources while this section is enabled. Server-synced and locked in multiplayer.", 90));
			MarbleYield = file.Bind<float>("2 - Black Marble", "Black Marble Yield", 1f, Describe("Multiply Black Marble from natural mining/destruction, including Dvergr structures, giant bones, and added rock/cliff drops. Slider range 1–10; default 1 keeps native amounts. Fractional results use probabilistic rounding. Stone/other materials and player-built resource refunds are unchanged. Server-synced and locked in multiplayer.", 80, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f)));
			FarmingSection = file.Bind<bool>("3 - Cultivate", "Enable Section", true, Describe("Enable Cultivate features. Disabling removes cultivation placement and paint patches. Existing cultivation remains saved. Server-synced and locked in multiplayer.", 100));
			Farming = file.Bind<bool>("3 - Cultivate", "Allow cultivation everywhere", true, Describe("Allow the Cultivator's Cultivate action on Mistlands ground normally blocked by the vegetation-ground restriction. Other placement checks and plant growth rules remain native. False restores the restriction for new cultivation; existing cultivated terrain is preserved. Server-synced and locked in multiplayer.", 90));
			DemistingSection = file.Bind<bool>("4 - De-Misting Items", "Enable Section", true, Describe("Enable De-Misting Items features. Disabling bypasses building and enhanced clearing; saved towers still load at Wisp Torch radius. Minimal saved-prefab/refund definitions remain available. Server-synced and locked in multiplayer.", 100));
			WispTower = file.Bind<bool>("4 - De-Misting Items", "Wisp Tower", true, Describe("Enable the Wisp Tower in the Hammer menu: three Small Black Marble columns topped with the large Dvergr mist tower, built as one piece with its own snap points and stone effects. No crafting station is required; can snap to building foundations. Disabling prevents new builds and reduces existing towers to Wisp Torch radius without deleting them. Server-synced and locked in multiplayer.", 90));
			TowerRecipe = file.Bind<string>("4 - De-Misting Items", "Wisp Tower Recipe", "Wisp[5],Eitr[10],BlackMarble[30]", Describe("Wisp Tower materials using exact prefab names: Item1[amount],Item2[amount]. Positive whole amounts; duplicate items are not allowed. Server-synced and locked in multiplayer.", 80));
			TowerMultiplier = file.Bind<float>("4 - De-Misting Items", "Demist Multiplier", 5f, Describe("Wisp Tower clearing radius as a multiplier of the native Wisp Torch radius. Range 1–10. Applies to existing towers live. Server-synced and locked in multiplayer.", 70, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f)));
			LightIntensity = file.Bind<float>("4 - De-Misting Items", "Light Intensity", 1f, Describe("Wisp Tower light intensity relative to its native brightness. Slider range 0–3, default 1. 0 turns off emitted light; decimals such as 2.1 are supported. Applies live to existing towers and is independent of Demist Multiplier. Native light range, flicker/fades, and accessibility settings remain unchanged. Disabling this section, Wisp Tower, or the mod restores native brightness. Server-synced and locked in multiplayer.", 60, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f)));
			Wisplight = file.Bind<bool>("5 - Wisplight", "Enable Section", true, Describe("Enable the Energized and Ancient Wisplight items. Level 2 clears mist at twice the native radius (+100%); level 3 at three times the native radius (+200%). Disabling stops effect hooks, radius/icon polling, and crafting, restores native radius, and retains saved item identities. Server-synced and locked in multiplayer.", 100));
			Title2 = file.Bind<string>("5 - Wisplight", "Level 2 Title", "Energized Wisplight", Describe("Title of dbmc_wisp2. Server-synced and locked in multiplayer.", 90));
			Description2 = file.Bind<string>("5 - Wisplight", "Level 2 Description", "An energized wisplight that pushes the mist back twice as far.", Describe("Description of dbmc_wisp2. Server-synced and locked in multiplayer.", 80));
			Upgrade2 = file.Bind<string>("5 - Wisplight", "Level 2 Recipe", "Demister[1],Wisp[1],Silver[2]", Describe("Materials for crafting dbmc_wisp2. A regular Wisplight (Demister) is always required; one is added if omitted. Exact prefab names using Item1[amount],Item2[amount]. Positive whole amounts; duplicate items are not allowed. Server-synced and locked in multiplayer.", 70));
			Title3 = file.Bind<string>("5 - Wisplight", "Level 3 Title", "Ancient Wisplight", Describe("Title of dbmc_wisp3. Server-synced and locked in multiplayer.", 60));
			Description3 = file.Bind<string>("5 - Wisplight", "Level 3 Description", "An ancient wisplight that pushes the mist back three times as far.", Describe("Description of dbmc_wisp3. Server-synced and locked in multiplayer.", 50));
			Upgrade3 = file.Bind<string>("5 - Wisplight", "Level 3 Recipe", "dbmc_wisp2[1],Wisp[2],Silver[4]", Describe("Materials for crafting dbmc_wisp3. dbmc_wisp2 is always required; one is added if omitted. Uses Item1[amount],Item2[amount]. Independent of the level 2 recipe. Server-synced and locked in multiplayer.", 40));
			try
			{
				Local = Capture();
			}
			catch (FormatException ex)
			{
				Plugin.Warn("Invalid settings; using safe section defaults: " + ex.Message);
				Local = new Settings(Enabled.Value, Marble.Value, Farming.Value, wisplight: false, "Demister[1],Wisp[1],Silver[2]", "dbmc_wisp2[1],Wisp[2],Silver[4]", "Energized Wisplight", "An energized wisplight that pushes the mist back twice as far.", "Ancient Wisplight", "An ancient wisplight that pushes the mist back three times as far.", wispTower: false, MarbleSection.Value, FarmingSection.Value, DemistingSection.Value);
				ApplyValues(Local);
				Wisplight.Value = false;
			}
			file.Save();
			ConfigLayout.OrderWisplight(file.ConfigFilePath);
			RememberFile();
			file.SettingChanged += Changed;
			Instance = this;
		}

		internal void RememberFile()
		{
			configInfo.Refresh();
			if (configInfo.Exists)
			{
				observedWrite = configInfo.LastWriteTimeUtc;
				observedLength = configInfo.Length;
				filePending = false;
			}
		}

		internal void PollFile(float now)
		{
			if (disposed || now < nextPoll)
			{
				return;
			}
			nextPoll = now + 0.25f;
			try
			{
				configInfo.Refresh();
				if (!configInfo.Exists)
				{
					return;
				}
				if (configInfo.LastWriteTimeUtc != observedWrite || configInfo.Length != observedLength)
				{
					observedWrite = configInfo.LastWriteTimeUtc;
					observedLength = configInfo.Length;
					filePending = true;
					reloadAt = now + 0.25f;
				}
				else
				{
					if (!filePending || now < reloadAt)
					{
						return;
					}
					ReloadScope reloadScope = BeforeReload();
					Exception error = null;
					try
					{
						File.Reload();
					}
					catch (Exception ex)
					{
						error = ex;
						throw;
					}
					finally
					{
						reloadScope?.Finish(error);
					}
					RememberFile();
					fileError = null;
				}
			}
			catch (Exception ex2)
			{
				if (fileError != ex2.Message)
				{
					Plugin.Warn("Config file reload not applied: " + ex2.Message);
				}
				fileError = ex2.Message;
				reloadAt = now + 1f;
			}
		}

		private Settings Capture()
		{
			return new Settings(Enabled.Value, Marble.Value, Farming.Value, Wisplight.Value, Upgrade2.Value, Upgrade3.Value, Title2.Value, Description2.Value, Title3.Value, Description3.Value, WispTower.Value, MarbleSection.Value, FarmingSection.Value, DemistingSection.Value, MarbleYield.Value, TowerRecipe.Value, TowerMultiplier.Value, LightIntensity.Value);
		}

		private void ApplyValues(Settings values)
		{
			Enabled.Value = values.Enabled;
			Marble.Value = values.MarbleDrops;
			Farming.Value = values.Farming;
			Wisplight.Value = values.Wisplight;
			Upgrade2.Value = values.Upgrade2;
			Upgrade3.Value = values.Upgrade3;
			Title2.Value = values.Title2;
			Description2.Value = values.Description2;
			Title3.Value = values.Title3;
			Description3.Value = values.Description3;
			WispTower.Value = values.WispTower;
			MarbleSection.Value = values.MarbleSection;
			FarmingSection.Value = values.FarmingSection;
			DemistingSection.Value = values.DemistingSection;
			MarbleYield.Value = values.MarbleYield;
			TowerRecipe.Value = values.TowerRecipe;
			TowerMultiplier.Value = values.TowerMultiplier;
			LightIntensity.Value = values.LightIntensity;
		}

		private void Overlay()
		{
			applying++;
			try
			{
				ApplyValues(Current);
			}
			finally
			{
				applying--;
			}
			SettingsTag.ReadOnly = Client;
			foreach (ConfigurationManagerAttributes tag in tags)
			{
				tag.ReadOnly = Client;
			}
			this.Refresh?.Invoke();
		}

		internal void SetClient(bool client)
		{
			if (Client != client)
			{
				Client = client;
				Remote = null;
				Overlay();
			}
		}

		internal void WaitForServer()
		{
			if (Client)
			{
				Remote = null;
				Overlay();
			}
		}

		internal void Receive(Settings values)
		{
			if (Client && values != null)
			{
				Remote = values;
				Overlay();
			}
		}

		private void Changed(object sender, SettingChangedEventArgs args)
		{
			if (applying != 0 || disposed)
			{
				return;
			}
			if (Client)
			{
				Overlay();
				return;
			}
			Settings local = Local;
			Settings local2;
			try
			{
				local2 = Capture();
			}
			catch (FormatException ex)
			{
				Plugin.Warn("Config edit not applied: " + ex.Message);
				Overlay();
				return;
			}
			Local = local2;
			Overlay();
			if (!Client && !Local.Same(local))
			{
				this.AuthorityChanged?.Invoke();
			}
			try
			{
				File.Save();
				RememberFile();
			}
			catch (Exception ex2)
			{
				Plugin.Warn("Could not save local config: " + ex2.Message);
			}
		}

		internal SaveScope BeforeSave()
		{
			if (applying != 0 || !Client || Remote == null)
			{
				return null;
			}
			applying++;
			try
			{
				ApplyValues(Local);
				return new SaveScope(this);
			}
			catch
			{
				applying--;
				throw;
			}
		}

		internal ReloadScope BeforeReload()
		{
			if (applying != 0)
			{
				return null;
			}
			applying++;
			try
			{
				ApplyValues(Local);
				return new ReloadScope(this, Local);
			}
			catch
			{
				applying--;
				throw;
			}
		}

		public void Dispose()
		{
			if (!disposed)
			{
				disposed = true;
				File.SettingChanged -= Changed;
				Client = false;
				Remote = null;
				Overlay();
				File.SaveOnConfigSet = autoSave;
				if (Instance == this)
				{
					Instance = null;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ConfigFile), "Save")]
	internal static class SaveLocalPreferences
	{
		private static void Prefix(ConfigFile __instance, out LiveConfig.SaveScope __state)
		{
			LiveConfig instance = LiveConfig.Instance;
			__state = ((instance != null && instance.File == __instance) ? instance.BeforeSave() : null);
		}

		private static void Postfix(ConfigFile __instance, LiveConfig.SaveScope __state)
		{
			__state?.Finish();
			LiveConfig instance = LiveConfig.Instance;
			if (instance != null && instance.File == __instance)
			{
				ConfigLayout.OrderWisplight(__instance.ConfigFilePath);
				instance.RememberFile();
			}
		}

		private static Exception Finalizer(Exception __exception, LiveConfig.SaveScope __state)
		{
			__state?.Finish();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(ConfigFile), "Reload")]
	internal static class ReloadLocalPreferences
	{
		private static void Prefix(ConfigFile __instance, out LiveConfig.ReloadScope __state)
		{
			LiveConfig instance = LiveConfig.Instance;
			__state = ((instance != null && instance.File == __instance) ? instance.BeforeReload() : null);
		}

		private static void Postfix(LiveConfig.ReloadScope __state)
		{
			__state?.Finish(null);
		}

		private static Exception Finalizer(Exception __exception, LiveConfig.ReloadScope __state)
		{
			__state?.Finish(__exception);
			return __exception;
		}
	}
	internal static class MarbleYield
	{
		private static ZNetScene scene;

		private static GameObject marble;

		private static readonly MethodInfo ScaleList = typeof(MarbleYield).GetMethod("ScaleRoll", BindingFlags.Static | BindingFlags.NonPublic);

		private static readonly MethodInfo ScaleAmount = typeof(MarbleYield).GetMethod("ScalePiece", BindingFlags.Static | BindingFlags.NonPublic);

		private static float Multiplier
		{
			get
			{
				LiveConfig instance = LiveConfig.Instance;
				if (instance == null || !instance.Ready || !instance.Current.Enabled || !instance.Current.MarbleSection)
				{
					return 1f;
				}
				return instance.Current.MarbleYield;
			}
		}

		private static bool IsMarble(GameObject item)
		{
			if (!Object.op_Implicit((Object)(object)item))
			{
				return false;
			}
			if (scene != ZNetScene.instance)
			{
				scene = ZNetScene.instance;
				marble = null;
			}
			if (!Object.op_Implicit((Object)(object)marble))
			{
				ZNetScene obj = scene;
				object obj2 = ((obj != null) ? obj.GetPrefab("BlackMarble") : null);
				if (obj2 == null)
				{
					ObjectDB instance = ObjectDB.instance;
					obj2 = ((instance != null) ? instance.GetItemPrefab("BlackMarble") : null);
				}
				marble = (GameObject)obj2;
			}
			if (item != marble)
			{
				return DropPolicy.Normalize(((Object)item).name) == "BlackMarble";
			}
			return true;
		}

		internal static int Count(int native, float multiplier)
		{
			if (native <= 0 || multiplier == 1f)
			{
				return native;
			}
			double num = (double)native * (double)multiplier;
			if (num >= 2147483647.0)
			{
				return int.MaxValue;
			}
			int num2 = (int)Math.Floor(num);
			double num3 = num - (double)num2;
			return num2 + ((num3 > 0.0 && (double)Random.value < num3) ? 1 : 0);
		}

		internal static List<GameObject> ScaleRoll(List<GameObject> drops, Component source)
		{
			float multiplier = Multiplier;
			if (multiplier == 1f || drops == null || drops.Count == 0 || !Object.op_Implicit((Object)(object)source))
			{
				return drops;
			}
			ZNetView component = source.GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner())
			{
				return drops;
			}
			Piece componentInParent = source.GetComponentInParent<Piece>();
			if (Object.op_Implicit((Object)(object)componentInParent) && componentInParent.IsPlacedByPlayer())
			{
				return drops;
			}
			int num = 0;
			GameObject item = null;
			foreach (GameObject drop in drops)
			{
				if (IsMarble(drop))
				{
					num++;
					item = drop;
				}
			}
			if (num == 0)
			{
				return drops;
			}
			int num2 = Count(num, multiplier);
			if (num2 == num)
			{
				return drops;
			}
			List<GameObject> list = new List<GameObject>(checked(drops.Count - num + num2));
			bool flag = false;
			foreach (GameObject drop2 in drops)
			{
				if (!IsMarble(drop2))
				{
					list.Add(drop2);
				}
				else if (!flag)
				{
					for (int i = 0; i < num2; i++)
					{
						list.Add(item);
					}
					flag = true;
				}
			}
			return list;
		}

		internal static int ScalePiece(Piece source, GameObject item, int amount)
		{
			float multiplier = Multiplier;
			if (multiplier == 1f || amount <= 0 || !Object.op_Implicit((Object)(object)source) || source.IsPlacedByPlayer() || !IsMarble(item))
			{
				return amount;
			}
			ZNetView component = ((Component)source).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner())
			{
				return amount;
			}
			return Count(amount, multiplier);
		}

		internal static IEnumerable<CodeInstruction> RewriteRoll(IEnumerable<CodeInstruction> input)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>();
			int num = 0;
			foreach (CodeInstruction item in input)
			{
				list.Add(item);
				if (item.operand is MethodInfo methodInfo && !(methodInfo.DeclaringType != typeof(DropTable)) && !(methodInfo.Name != "GetDropList") && methodInfo.GetParameters().Length == 0)
				{
					list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					list.Add(new CodeInstruction(OpCodes.Call, (object)ScaleList));
					num++;
				}
			}
			if (num != 1)
			{
				throw new InvalidOperationException("Native mining/destruction drop roll changed; Black Marble Yield expected one roll, found " + num + ".");
			}
			return list;
		}

		private static bool Store(CodeInstruction code)
		{
			if (!(code.opcode == OpCodes.Stloc) && !(code.opcode == OpCodes.Stloc_S) && !(code.opcode == OpCodes.Stloc_0) && !(code.opcode == OpCodes.Stloc_1) && !(code.opcode == OpCodes.Stloc_2))
			{
				return code.opcode == OpCodes.Stloc_3;
			}
			return true;
		}

		private static CodeInstruction Load(CodeInstruction store)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			OpCode opcode = store.opcode;
			return new CodeInstruction((opcode == OpCodes.Stloc_0) ? OpCodes.Ldloc_0 : ((opcode == OpCodes.Stloc_1) ? OpCodes.Ldloc_1 : ((opcode == OpCodes.Stloc_2) ? OpCodes.Ldloc_2 : ((opcode == OpCodes.Stloc_3) ? OpCodes.Ldloc_3 : OpCodes.Ldloc))), store.operand);
		}

		internal static IEnumerable<CodeInstruction> RewritePiece(IEnumerable<CodeInstruction> input)
		{
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Expected O, but got Unknown
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Expected O, but got Unknown
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Expected O, but got Unknown
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(input);
			CodeInstruction val = null;
			CodeInstruction val2 = null;
			int num = -1;
			for (int i = 0; i + 1 < list.Count; i++)
			{
				if (list[i].operand is FieldInfo fieldInfo && fieldInfo.DeclaringType == typeof(Requirement) && fieldInfo.Name == "m_amount" && Store(list[i + 1]))
				{
					val = list[i + 1];
				}
				if (list[i].operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(ObjectDB) && methodInfo.Name == "GetItemPrefab" && Store(list[i + 1]))
				{
					val2 = list[i + 1];
				}
				if (num < 0 && list[i].operand is FieldInfo fieldInfo2 && fieldInfo2.DeclaringType == typeof(Piece) && fieldInfo2.Name == "m_destroyedLootPrefab")
				{
					num = i - 1;
				}
			}
			if (val == null || val2 == null || num < 0 || list[num].opcode != OpCodes.Ldarg_0)
			{
				throw new InvalidOperationException("Native Piece.DropResources amount/split pattern changed; Black Marble Yield cannot apply.");
			}
			List<CodeInstruction> list2 = new List<CodeInstruction>();
			for (int j = 0; j < list.Count; j++)
			{
				if (j == num)
				{
					list2.Add(new CodeInstruction(list[j]));
					list2.Add(Load(val2));
					list2.Add(Load(val));
					list2.Add(new CodeInstruction(OpCodes.Call, (object)ScaleAmount));
					list2.Add(new CodeInstruction(val.opcode, val.operand));
					list2.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				}
				else
				{
					list2.Add(list[j]);
				}
			}
			return list2;
		}
	}
	[HarmonyPatch(typeof(MineRock5), "DamageArea", new Type[]
	{
		typeof(int),
		typeof(HitData)
	})]
	internal static class MarbleSectionYield
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return MarbleYield.RewriteRoll(instructions);
		}
	}
	[HarmonyPatch(typeof(MineRock), "RPC_Hit")]
	internal static class MarbleLegacyYield
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return MarbleYield.RewriteRoll(instructions);
		}
	}
	[HarmonyPatch(typeof(DropOnDestroyed), "OnDestroyed")]
	internal static class MarbleDestroyedYield
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return MarbleYield.RewriteRoll(instructions);
		}
	}
	[HarmonyPatch(typeof(Piece), "DropResources")]
	internal static class MarbleStructureYield
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return MarbleYield.RewritePiece(instructions);
		}
	}
	internal static class MarbleDrops
	{
		internal sealed class Scope : IDisposable
		{
			internal readonly DropTable Original;

			private readonly MineRock5 sections;

			private readonly MineRock legacy;

			private readonly DropOnDestroyed whole;

			private bool ended;

			internal Scope(Component rock, DropTable original, DropTable table)
			{
				Original = original;
				sections = (MineRock5)(object)((rock is MineRock5) ? rock : null);
				legacy = (MineRock)(object)((rock is MineRock) ? rock : null);
				if (Object.op_Implicit((Object)(object)sections))
				{
					sections.m_dropItems = table;
					return;
				}
				if (Object.op_Implicit((Object)(object)legacy))
				{
					legacy.m_dropItems = table;
					return;
				}
				whole = rock.GetComponent<DropOnDestroyed>();
				whole.m_dropWhenDestroyed = table;
			}

			public void Dispose()
			{
				if (!ended)
				{
					ended = true;
					if (Object.op_Implicit((Object)(object)sections))
					{
						sections.m_dropItems = Original;
					}
					else if (Object.op_Implicit((Object)(object)legacy))
					{
						legacy.m_dropItems = Original;
					}
					else if (Object.op_Implicit((Object)(object)whole))
					{
						whole.m_dropWhenDestroyed = Original;
					}
				}
			}
		}

		private static ZNetScene cachedScene;

		private static DropTable replacement;

		private static bool missingLogged;

		private static readonly HashSet<long> notices = new HashSet<long>();

		internal unsafe static Scope Begin(Component rock, ZNetView view, DropTable table, Vector3? hitPosition = null)
		{
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Invalid comparison between Unknown and I4
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Invalid comparison between Unknown and I4
			LiveConfig instance = LiveConfig.Instance;
			if (instance == null || !instance.Ready || !instance.Current.Enabled || !instance.Current.MarbleSection || !instance.Current.MarbleDrops || !Object.op_Implicit((Object)(object)rock) || !Object.op_Implicit((Object)(object)view) || !view.IsValid() || !view.IsOwner() || table == null)
			{
				return null;
			}
			if (!(rock is MineRock5) && !(rock is MineRock) && !Object.op_Implicit((Object)(object)rock.GetComponent<DropOnDestroyed>()))
			{
				return null;
			}
			if (cachedScene != ZNetScene.instance)
			{
				Clear();
			}
			string name = ((Object)rock.gameObject).name;
			ZDO zDO = view.GetZDO();
			int num = ((zDO != null) ? zDO.GetPrefab() : 0);
			string text = DropPolicy.NameForHash(num);
			object obj;
			if (num == 0)
			{
				obj = null;
			}
			else
			{
				ZNetScene instance2 = ZNetScene.instance;
				obj = ((instance2 != null) ? instance2.GetPrefab(num) : null);
			}
			GameObject val = (GameObject)obj;
			if (text == null)
			{
				text = (Object.op_Implicit((Object)(object)val) ? DropPolicy.Normalize(((Object)val).name) : ((num == 0) ? DropPolicy.Normalize(name) : null));
			}
			bool flag = ((num != 0) ? (DropPolicy.NameForHash(num) != null) : DropPolicy.IsTarget(text));
			if (DropPolicy.Excluded(num) || (text != null && DropPolicy.Excluded(StringExtensionMethods.GetStableHashCode(text))) || Object.op_Implicit((Object)(object)rock.GetComponentInParent<Piece>()))
			{
				return null;
			}
			Vector3 val2 = (Vector3)(((??)hitPosition) ?? rock.transform.position);
			Heightmap val3 = Heightmap.FindHeightmap(val2);
			WorldGenerator instance3 = WorldGenerator.instance;
			Biome val4 = (Biome)((!Object.op_Implicit((Object)(object)val3)) ? ((instance3 != null) ? ((int)instance3.GetBiome(val2.x, val2.z, 0.02f, false)) : 0) : ((int)val3.GetBiome(val2, 0.02f, false)));
			if ((int)val4 != 512)
			{
				return null;
			}
			bool flag2 = false;
			if (!flag && Object.op_Implicit((Object)(object)val) && !Object.op_Implicit((Object)(object)val.GetComponent<Piece>()))
			{
				ZNetScene instance4 = ZNetScene.instance;
				object obj2 = ((instance4 != null) ? instance4.GetPrefab("Stone") : null);
				if (obj2 == null)
				{
					ObjectDB instance5 = ObjectDB.instance;
					obj2 = ((instance5 != null) ? instance5.GetItemPrefab("Stone") : null);
				}
				GameObject stone = (GameObject)obj2;
				MineRock5 component = val.GetComponent<MineRock5>();
				MineRock component2 = val.GetComponent<MineRock>();
				DropOnDestroyed component3 = val.GetComponent<DropOnDestroyed>();
				flag2 = (Object.op_Implicit((Object)(object)component) ? StoneOnly(component.m_dropItems, stone) : (Object.op_Implicit((Object)(object)component2) ? StoneOnly(component2.m_dropItems, stone) : (Object.op_Implicit((Object)(object)component3) && StoneOnly(component3.m_dropWhenDestroyed, stone))));
			}
			if (!flag && !flag2)
			{
				if ((rock is MineRock5 || rock is MineRock) && Notice(num, 1))
				{
					Plugin.MiningInfo("Black Marble: unrecognized mining prefab: hash=" + num + ", runtime=" + name + ", prefab=" + (text ?? "unresolved") + ". Native drops retained.");
				}
				return null;
			}
			DropTable val5 = Resolve();
			if (val5 == null)
			{
				return null;
			}
			if (Notice(num, 3))
			{
				Plugin.MiningInfo("Black Marble: replacement applied: prefab=" + (text ?? "stone rock") + ", hash=" + num + ", runtime=" + name + ", biome=" + ((object)(*(Biome*)(&val4))/*cast due to .constrained prefix*/).ToString() + ".");
			}
			return new Scope(rock, table, val5);
		}

		private static bool Notice(int hash, int kind)
		{
			long item = (long)(((ulong)(uint)hash << 32) | (uint)kind);
			if (notices.Count < 32)
			{
				return notices.Add(item);
			}
			return false;
		}

		private static bool Material(GameObject item, GameObject canonical, string name)
		{
			if (Object.op_Implicit((Object)(object)item))
			{
				if (!Object.op_Implicit((Object)(object)canonical) || item != canonical)
				{
					return DropPolicy.Normalize(((Object)item).name) == name;
				}
				return true;
			}
			return false;
		}

		internal static bool StoneOnly(DropTable table, GameObject stone = null)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (table?.m_drops == null || table.m_drops.Count == 0)
			{
				return false;
			}
			foreach (DropData drop in table.m_drops)
			{
				if (!Material(drop.m_item, stone, "Stone"))
				{
					return false;
				}
			}
			return true;
		}

		private static DropTable Resolve()
		{
			ZNetScene instance = ZNetScene.instance;
			if (cachedScene != instance)
			{
				cachedScene = instance;
				replacement = null;
				missingLogged = false;
			}
			if (replacement == null && Object.op_Implicit((Object)(object)instance))
			{
				object obj = instance.GetPrefab("BlackMarble");
				if (obj == null)
				{
					ObjectDB instance2 = ObjectDB.instance;
					obj = ((instance2 != null) ? instance2.GetItemPrefab("BlackMarble") : null);
				}
				GameObject val = (GameObject)obj;
				object obj2 = instance.GetPrefab("Stone");
				if (obj2 == null)
				{
					ObjectDB instance3 = ObjectDB.instance;
					obj2 = ((instance3 != null) ? instance3.GetItemPrefab("Stone") : null);
				}
				GameObject stonePrefab = (GameObject)obj2;
				object obj3 = instance.GetPrefab("Grausten");
				if (obj3 == null)
				{
					ObjectDB instance4 = ObjectDB.instance;
					obj3 = ((instance4 != null) ? instance4.GetItemPrefab("Grausten") : null);
				}
				GameObject graustenPrefab = (GameObject)obj3;
				string[] array = new string[3] { "cliff_ashlands2_frac", "rock4_ashlands_frac", "cliff_ashlands1_frac" };
				foreach (string name in array)
				{
					GameObject val2 = instance.GetPrefab(name) ?? instance.m_prefabs.Find((GameObject p) => Object.op_Implicit((Object)(object)p) && ((Object)p).name == name);
					MineRock5 val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<MineRock5>() : null);
					if (Object.op_Implicit((Object)(object)val3) && Object.op_Implicit((Object)(object)val))
					{
						replacement = CopyAshlandsTable(val3.m_dropItems, val, stonePrefab, graustenPrefab);
					}
					if (replacement != null)
					{
						Plugin.MiningInfo("Black Marble: native source=" + name + ", drops=" + replacement.m_dropMin + "-" + replacement.m_dropMax + ", chance=" + replacement.m_dropChance + "; Grausten replaced with BlackMarble, Stone preserved.");
						break;
					}
				}
			}
			if (replacement == null && !missingLogged)
			{
				Plugin.Warn("Could not resolve an Ashlands Grausten/Stone drop table and BlackMarble; native Mistlands drops are retained.");
				missingLogged = true;
			}
			return replacement;
		}

		internal static DropTable CopyAshlandsTable(DropTable source, GameObject marble, GameObject stonePrefab = null, GameObject graustenPrefab = null)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			if (source == null || source.m_drops == null || !Object.op_Implicit((Object)(object)marble))
			{
				return null;
			}
			bool flag = false;
			bool flag2 = false;
			foreach (DropData drop in source.m_drops)
			{
				if (!Object.op_Implicit((Object)(object)drop.m_item))
				{
					return null;
				}
				if (Material(drop.m_item, stonePrefab, "Stone"))
				{
					flag = true;
					continue;
				}
				if (Material(drop.m_item, graustenPrefab, "Grausten"))
				{
					flag2 = true;
					continue;
				}
				return null;
			}
			if (!flag || !flag2)
			{
				return null;
			}
			DropTable val = new DropTable
			{
				m_dropMin = source.m_dropMin,
				m_dropMax = source.m_dropMax,
				m_dropChance = source.m_dropChance,
				m_oneOfEach = source.m_oneOfEach,
				m_drops = new List<DropData>(source.m_drops.Count)
			};
			foreach (DropData drop2 in source.m_drops)
			{
				val.m_drops.Add(new DropData
				{
					m_item = (Material(drop2.m_item, graustenPrefab, "Grausten") ? marble : drop2.m_item),
					m_stackMin = drop2.m_stackMin,
					m_stackMax = drop2.m_stackMax,
					m_weight = drop2.m_weight,
					m_dontScale = drop2.m_dontScale
				});
			}
			return val;
		}

		internal static void Clear()
		{
			cachedScene = ZNetScene.instance;
			replacement = null;
			missingLogged = false;
			notices.Clear();
		}
	}
	[HarmonyPatch(typeof(MineRock5), "DamageArea", new Type[]
	{
		typeof(int),
		typeof(HitData)
	})]
	internal static class MinedSection
	{
		private static void Prefix(MineRock5 __instance, HitData __1, ZNetView ___m_nview, DropTable ___m_dropItems, out MarbleDrops.Scope __state)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			__state = MarbleDrops.Begin((Component)(object)__instance, ___m_nview, ___m_dropItems, __1?.m_point);
		}

		private static void Postfix(MarbleDrops.Scope __state)
		{
			__state?.Dispose();
		}

		private static Exception Finalizer(Exception __exception, MarbleDrops.Scope __state)
		{
			__state?.Dispose();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Destructible), "Destroy", new Type[] { typeof(HitData) })]
	internal static class WholeRock
	{
		private static void Prefix(Destructible __instance, ZNetView ___m_nview, GameObject ___m_spawnWhenDestroyed, out MarbleDrops.Scope __state)
		{
			DropOnDestroyed component = ((Component)__instance).GetComponent<DropOnDestroyed>();
			DropTable table = (Object.op_Implicit((Object)(object)component) ? component.m_dropWhenDestroyed : null);
			if (Object.op_Implicit((Object)(object)___m_spawnWhenDestroyed) && (Object.op_Implicit((Object)(object)___m_spawnWhenDestroyed.GetComponent<MineRock5>()) || Object.op_Implicit((Object)(object)___m_spawnWhenDestroyed.GetComponent<MineRock>())))
			{
				table = null;
			}
			__state = MarbleDrops.Begin((Component)(object)__instance, ___m_nview, table);
		}

		private static void Postfix(MarbleDrops.Scope __state)
		{
			__state?.Dispose();
		}

		private static Exception Finalizer(Exception __exception, MarbleDrops.Scope __state)
		{
			__state?.Dispose();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(MineRock), "RPC_Hit")]
	internal static class LegacyMinedSection
	{
		private static void Prefix(MineRock __instance, HitData __1, ZNetView ___m_nview, DropTable ___m_dropItems, out MarbleDrops.Scope __state)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			__state = MarbleDrops.Begin((Component)(object)__instance, ___m_nview, ___m_dropItems, __1?.m_point);
		}

		private static void Postfix(MarbleDrops.Scope __state)
		{
			__state?.Dispose();
		}

		private static Exception Finalizer(Exception __exception, MarbleDrops.Scope __state)
		{
			__state?.Dispose();
			return __exception;
		}
	}
	[BepInPlugin("dannanboi.mistlandscontrol", "MistlandsControl", "1.0.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class Connection
		{
			internal ZNetPeer Peer;

			internal string Version;

			internal float StartedAt;

			internal bool Rejected;

			internal float CloseAt;
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class NewConnection
		{
			[HarmonyPriority(800)]
			private static void Prefix(ZNet __instance, ZNetPeer peer)
			{
				Instance?.RegisterConnection(__instance, peer);
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static class PeerInfo
		{
			[HarmonyPriority(800)]
			private static bool Prefix(ZRpc rpc)
			{
				if (!((Object)(object)Instance == (Object)null))
				{
					return Instance.GatePeerInfo(rpc);
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
		private static class ShowError
		{
			private static void Postfix(FejdStartup __instance)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Invalid comparison between Unknown and I4
				if (connectionError != null && (int)ZNet.GetConnectionStatus() == 3)
				{
					object? value = ErrorText.GetValue(__instance);
					TMP_Text val = (TMP_Text)((value is TMP_Text) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						val.text = connectionError;
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static class NetworkAwake
		{
			private static void Postfix(ZNet __instance)
			{
				Instance?.BeginNetwork(__instance);
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private static class NetworkShutdown
		{
			private static void Postfix(ZNet __instance)
			{
				if ((Object)(object)Instance != (Object)null && Instance.network == __instance)
				{
					Instance.BeginNetwork(null);
				}
			}
		}

		private const string HelloRpc = "dannanboi.mistlandscontrol.Hello";

		private const string SettingsRpc = "dannanboi.mistlandscontrol.Settings";

		private const string RejectedRpc = "dannanboi.mistlandscontrol.Rejected";

		private const int Protocol = 6;

		private static string connectionError;

		private static readonly FieldInfo ConnectionStatus = AccessTools.Field(typeof(ZNet), "m_connectionStatus");

		private static readonly FieldInfo ErrorText = AccessTools.Field(typeof(FejdStartup), "m_connectionFailedError");

		private readonly Dictionary<ZRpc, Connection> connections = new Dictionary<ZRpc, Connection>();

		private readonly List<ZRpc> removedConnections = new List<ZRpc>();

		public const string Guid = "dannanboi.mistlandscontrol";

		public const string ModName = "MistlandsControl";

		public const string PluginVersion = "1.0.1";

		public const string ModVersion = "1.0.1";

		internal static Plugin Instance;

		private Harmony harmony;

		private SectionPatches sections;

		private LiveConfig settings;

		private ZNet network;

		private bool dirty;

		private bool wisplightDirty = true;

		private bool firstRefresh = true;

		private bool wasWisplightEnabled;

		private bool wasTowerEnabled;

		private string towerRecipeApplied;

		private WisplightItems wisplights;

		private WispTowerPieces towers;

		private float nextPublish;

		private float nextPeerCheck;

		private bool authority => !settings.Client;

		private bool settingsReady => settings.Ready;

		internal static bool VersionMatches(string received)
		{
			return string.Equals(received, "1.0.1", StringComparison.Ordinal);
		}

		private void RegisterConnection(ZNet net, ZNetPeer peer)
		{
			if ((Object)(object)net == (Object)null || peer == null || peer.m_rpc == null)
			{
				return;
			}
			BeginNetwork(net);
			if (!connections.ContainsKey(peer.m_rpc))
			{
				if (!net.IsServer() && peer.m_server)
				{
					connectionError = null;
					settings.WaitForServer();
				}
				Connection value = new Connection
				{
					Peer = peer,
					StartedAt = Time.realtimeSinceStartup
				};
				connections[peer.m_rpc] = value;
				peer.m_rpc.Register<string>("dannanboi.mistlandscontrol.Hello", (Action<ZRpc, string>)ReceiveHello);
				peer.m_rpc.Register<ZPackage>("dannanboi.mistlandscontrol.Settings", (Action<ZRpc, ZPackage>)ReceiveSettings);
				peer.m_rpc.Register<string>("dannanboi.mistlandscontrol.Rejected", (Action<ZRpc, string>)ReceiveRejection);
				peer.m_rpc.Invoke("dannanboi.mistlandscontrol.Hello", new object[1] { "1.0.1" });
			}
		}

		private bool IsServerConnection(ZRpc rpc)
		{
			if (Object.op_Implicit((Object)(object)network) && !network.IsServer() && connections.TryGetValue(rpc, out var value))
			{
				return value.Peer.m_server;
			}
			return false;
		}

		private void ReceiveHello(ZRpc rpc, string version)
		{
			if (!Object.op_Implicit((Object)(object)network) || !connections.TryGetValue(rpc, out var value) || value.Rejected || value.Version != null)
			{
				return;
			}
			value.Version = ((version != null && version.Length <= 32) ? version : "");
			if (!VersionMatches(value.Version))
			{
				if (network.IsServer())
				{
					Reject(rpc, value, Requirement(version));
				}
				else if (IsServerConnection(rpc))
				{
					FailClient("This server requires MistlandsControl " + SafeVersion(version) + ". You have 1.0.1. Install the matching version to join.");
				}
			}
			else if (authority)
			{
				SendSettings(rpc);
			}
		}

		private static string SafeVersion(string value)
		{
			if (string.IsNullOrEmpty(value) || value.Length > 32)
			{
				return "(unknown version)";
			}
			foreach (char c in value)
			{
				if (!char.IsLetterOrDigit(c) && c != '.' && c != '-' && c != '+' && c != '_')
				{
					return "(unknown version)";
				}
			}
			return value;
		}

		private static string Requirement(string found)
		{
			return "This server requires MistlandsControl 1.0.1. " + (string.IsNullOrEmpty(found) ? "The mod is missing. " : ("Your version is " + SafeVersion(found) + ". ")) + "Install the matching version to join.";
		}

		private void Reject(ZRpc rpc, Connection connection, string message)
		{
			if (!connection.Rejected)
			{
				connection.Rejected = true;
				connection.CloseAt = Time.realtimeSinceStartup + 2f;
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				bool flag = connection.Version != null;
				if (flag)
				{
					rpc.Invoke("dannanboi.mistlandscontrol.Rejected", new object[1] { message });
				}
				rpc.Invoke("Error", new object[1] { flag ? 3 : 5 });
			}
		}

		private void ReceiveRejection(ZRpc rpc, string message)
		{
			if (IsServerConnection(rpc) && !connections[rpc].Rejected)
			{
				if (string.IsNullOrEmpty(message) || message.Length > 512)
				{
					message = Requirement(null);
				}
				FailClient(message.Replace("<", "").Replace(">", ""));
			}
		}

		private void FailClient(string message)
		{
			connectionError = message;
			settings.WaitForServer();
			foreach (Connection value in connections.Values)
			{
				value.Rejected = true;
				value.CloseAt = Time.realtimeSinceStartup;
			}
			ConnectionStatus.SetValue(null, (object)(ConnectionStatus)3);
			((BaseUnityPlugin)this).Logger.LogError((object)message);
		}

		private bool GatePeerInfo(ZRpc rpc)
		{
			if (!connections.TryGetValue(rpc, out var value))
			{
				if (Object.op_Implicit((Object)(object)network) && network.IsServer())
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)(Requirement(null) + " Connection registration was not verified."));
					rpc.Invoke("Error", new object[1] { 5 });
				}
				else
				{
					FailClient("The connection could not verify MistlandsControl 1.0.1.");
				}
				return false;
			}
			if (value.Rejected)
			{
				return false;
			}
			if (VersionMatches(value.Version) && (network.IsServer() || settingsReady))
			{
				return true;
			}
			if (network.IsServer())
			{
				Reject(rpc, value, Requirement(value.Version));
			}
			else if (connectionError == null)
			{
				FailClient("The server must also run MistlandsControl 1.0.1 and send its settings before you can join.");
			}
			return false;
		}

		private void SendSettings(ZRpc rpc)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(6);
			Settings current = settings.Current;
			val.Write(current.Enabled);
			val.Write(current.MarbleDrops);
			val.Write(current.Farming);
			val.Write(current.Wisplight);
			val.Write(current.Upgrade2);
			val.Write(current.Upgrade3);
			val.Write(current.Title2);
			val.Write(current.Description2);
			val.Write(current.Title3);
			val.Write(current.Description3);
			val.Write(current.WispTower);
			val.Write(current.MarbleSection);
			val.Write(current.FarmingSection);
			val.Write(current.DemistingSection);
			val.Write(current.MarbleYield);
			val.Write(current.TowerRecipe);
			val.Write(current.TowerMultiplier);
			val.Write(current.LightIntensity);
			rpc.Invoke("dannanboi.mistlandscontrol.Settings", new object[1] { val });
		}

		private void BroadcastSettings()
		{
			if (!Object.op_Implicit((Object)(object)network) || !network.IsServer() || connections.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<ZRpc, Connection> connection in connections)
			{
				if (VersionMatches(connection.Value.Version) && !connection.Value.Rejected && connection.Key.IsConnected())
				{
					SendSettings(connection.Key);
				}
			}
		}

		private void ReceiveSettings(ZRpc rpc, ZPackage data)
		{
			if (!IsServerConnection(rpc))
			{
				return;
			}
			Connection connection = connections[rpc];
			if (!VersionMatches(connection.Version) || connection.Rejected)
			{
				return;
			}
			try
			{
				if (data == null || data.Size() < 15 || data.Size() > 32768 || data.ReadInt() != 6)
				{
					throw new FormatException("Invalid settings packet or protocol.");
				}
				Settings values = new Settings(data.ReadBool(), data.ReadBool(), data.ReadBool(), data.ReadBool(), data.ReadString(), data.ReadString(), data.ReadString(), data.ReadString(), data.ReadString(), data.ReadString(), data.ReadBool(), data.ReadBool(), data.ReadBool(), data.ReadBool(), data.ReadSingle(), data.ReadString(), data.ReadSingle(), data.ReadSingle());
				if (data.GetPos() != data.Size())
				{
					throw new FormatException("Unexpected data in settings packet.");
				}
				settings.Receive(values);
				LogSettings();
			}
			catch (Exception ex)
			{
				FailClient("MistlandsControl 1.0.1: " + ex.Message);
			}
		}

		private void PumpConnections(float now)
		{
			if (connections.Count == 0)
			{
				return;
			}
			removedConnections.Clear();
			bool flag = network.IsServer();
			foreach (KeyValuePair<ZRpc, Connection> connection in connections)
			{
				Connection value = connection.Value;
				if (!connection.Key.IsConnected())
				{
					removedConnections.Add(connection.Key);
					continue;
				}
				if (flag && !value.Rejected && value.Version == null && now - value.StartedAt > 30f)
				{
					Reject(connection.Key, value, Requirement(null));
				}
				else if (!flag && value.Peer.m_server && !settingsReady && connectionError == null && now - value.StartedAt > 30f)
				{
					FailClient("The server did not complete the MistlandsControl 1.0.1 handshake.");
				}
				if (value.Rejected && now >= value.CloseAt)
				{
					network.Disconnect(value.Peer);
					removedConnections.Add(connection.Key);
				}
			}
			foreach (ZRpc removedConnection in removedConnections)
			{
				connections.Remove(removedConnection);
			}
			removedConnections.Clear();
		}

		private static void ValidateApi()
		{
			WisplightItems.ValidateApi();
			WisplightEffects.ValidateApi();
			WispTowerPieces.ValidateApi();
			FarmingPaint.ValidateApi();
			if (ConnectionStatus == null || ConnectionStatus.FieldType != typeof(ConnectionStatus) || ErrorText == null || AccessTools.Field(typeof(MineRock5), "m_nview")?.FieldType != typeof(ZNetView) || AccessTools.Field(typeof(Destructible), "m_nview")?.FieldType != typeof(ZNetView) || AccessTools.Field(typeof(Destructible), "m_spawnWhenDestroyed")?.FieldType != typeof(GameObject) || AccessTools.Field(typeof(Player), "m_placementGhost")?.FieldType != typeof(GameObject))
			{
				throw new MissingMemberException("This Valheim build is incompatible with MistlandsControl.");
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(MineRock5), "DamageArea", new Type[2]
			{
				typeof(int),
				typeof(HitData)
			}, (Type[])null);
			if (methodInfo == null || methodInfo.ReturnType != typeof(bool) || AccessTools.Method(typeof(Destructible), "Destroy", new Type[1] { typeof(HitData) }, (Type[])null) == null || AccessTools.Method(typeof(Player), "UpdatePlacementGhost", new Type[1] { typeof(bool) }, (Type[])null) == null || AccessTools.Method(typeof(Heightmap), "GetVegetationMask", new Type[1] { typeof(Vector3) }, (Type[])null) == null || AccessTools.Method(typeof(ZNet), "OnNewConnection", new Type[1] { typeof(ZNetPeer) }, (Type[])null) == null || AccessTools.Method(typeof(ZNet), "RPC_PeerInfo", new Type[2]
			{
				typeof(ZRpc),
				typeof(ZPackage)
			}, (Type[])null) == null)
			{
				throw new MissingMethodException("This Valheim build is incompatible with MistlandsControl.");
			}
		}

		private void Awake()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			ValidateApi();
			Instance = this;
			settings = new LiveConfig(((BaseUnityPlugin)this).Config);
			settings.AuthorityChanged += Changed;
			settings.Refresh += QueueWisplight;
			wisplights = new WisplightItems(Paths.ConfigPath);
			towers = new WispTowerPieces();
			harmony = new Harmony("dannanboi.mistlandscontrol");
			sections = new SectionPatches(harmony);
			sections.Refresh();
			BeginNetwork(ZNet.instance);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"MistlandsControl 1.0.1 loaded.");
		}

		internal static void Warn(string message)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)message);
			}
		}

		internal static void MiningInfo(string message)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)message);
			}
		}

		private void Changed()
		{
			dirty = true;
		}

		internal void QueueWisplight()
		{
			wisplightDirty = true;
		}

		private void BeginNetwork(ZNet value)
		{
			if (network == value)
			{
				return;
			}
			foreach (ZRpc key in connections.Keys)
			{
				key.Unregister("dannanboi.mistlandscontrol.Hello");
				key.Unregister("dannanboi.mistlandscontrol.Settings");
				key.Unregister("dannanboi.mistlandscontrol.Rejected");
			}
			connections.Clear();
			network = value;
			if ((Object)(object)value != (Object)null)
			{
				connectionError = null;
			}
			MarbleDrops.Clear();
			FarmingRules.Clear();
			dirty = false;
			nextPublish = (nextPeerCheck = 0f);
			settings.SetClient(client: false);
			settings.SetClient((Object)(object)value != (Object)null && !value.IsServer());
		}

		private void Update()
		{
			BeginNetwork(ZNet.instance);
			settings.PollFile(Time.realtimeSinceStartup);
			if (WisplightEffects.Enabled && wisplights != null && wisplights.PollIcons(Time.realtimeSinceStartup))
			{
				wisplightDirty = true;
			}
			if (wisplightDirty)
			{
				wisplightDirty = false;
				sections?.Refresh();
				bool enabled = WisplightEffects.Enabled;
				bool enabled2 = WispTowerPieces.Enabled;
				if (firstRefresh || enabled || wasWisplightEnabled)
				{
					wisplights?.RegisterDB(ObjectDB.instance);
				}
				string towerRecipe = settings.Current.TowerRecipe;
				if (firstRefresh || enabled2 || wasTowerEnabled || towerRecipeApplied != towerRecipe)
				{
					towers?.Register(ZNetScene.instance, ObjectDB.instance, before: false);
					towers?.Refresh();
				}
				towerRecipeApplied = towerRecipe;
				wasWisplightEnabled = enabled;
				wasTowerEnabled = enabled2;
				firstRefresh = false;
				FarmingPaint.RefreshLoadedTerrain();
			}
			if (Object.op_Implicit((Object)(object)network))
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (dirty && authority && realtimeSinceStartup >= nextPublish)
				{
					dirty = false;
					nextPublish = realtimeSinceStartup + 0.25f;
					BroadcastSettings();
				}
				if (realtimeSinceStartup >= nextPeerCheck)
				{
					nextPeerCheck = realtimeSinceStartup + 1f;
					PumpConnections(realtimeSinceStartup);
				}
			}
		}

		private void LogSettings()
		{
			Settings current = settings.Current;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Enable Mod=" + current.Enabled + "; Black Marble Enable Section=" + current.MarbleSection + "; Mine from any stone=" + current.MarbleDrops + "; Black Marble Yield=" + current.MarbleYield + "; Cultivate Enable Section=" + current.FarmingSection + "; Allow cultivation everywhere=" + current.Farming + "; Wisplight Enable Section=" + current.Wisplight + "; De-Misting Items Enable Section=" + current.DemistingSection + "; Wisp Tower=" + current.WispTower + "; Demist Multiplier=" + current.TowerMultiplier + "; Light Intensity=" + current.LightIntensity + (authority ? " (local/server settings)" : " (server locked)")));
		}

		private void OnDestroy()
		{
			if (settings != null)
			{
				BeginNetwork(null);
				settings.AuthorityChanged -= Changed;
				settings.Refresh -= QueueWisplight;
				settings.Dispose();
			}
			wisplights?.Dispose();
			towers?.Dispose();
			sections?.Dispose();
			WisplightEffects.Restore();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	internal sealed class SectionPatches : IDisposable
	{
		private sealed class Group
		{
			private readonly Harmony harmony;

			private readonly Type[] types;

			private bool active;

			internal Group(string name, params Type[] patches)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Expected O, but got Unknown
				harmony = new Harmony("dannanboi.mistlandscontrol." + name);
				types = patches;
			}

			internal void Set(bool enabled)
			{
				if (active == enabled)
				{
					return;
				}
				if (enabled)
				{
					Type[] array = types;
					foreach (Type type in array)
					{
						harmony.CreateClassProcessor(type).Patch();
					}
				}
				else
				{
					harmony.UnpatchSelf();
				}
				active = enabled;
			}
		}

		private readonly Group mining = new Group("Mining", typeof(MinedSection), typeof(WholeRock), typeof(LegacyMinedSection));

		private readonly Group yield = new Group("Yield", typeof(MarbleSectionYield), typeof(MarbleLegacyYield), typeof(MarbleDestroyedYield), typeof(MarbleStructureYield));

		private readonly Group farming = new Group("Farming", typeof(CultivatePlacement), typeof(CultivableMistlandsGround), typeof(CompiledCultivationPaint), typeof(LegacyCultivationPaint), typeof(SavedCultivationPaint));

		private readonly Group wisplight = new Group("Wisplight", typeof(WisplightEffectUpdate));

		private bool wisplightActive;

		internal SectionPatches(Harmony core)
		{
			Type[] nestedTypes = typeof(Plugin).GetNestedTypes(BindingFlags.NonPublic);
			foreach (Type type in nestedTypes)
			{
				if (Attribute.IsDefined(type, typeof(HarmonyPatch)))
				{
					core.CreateClassProcessor(type).Patch();
				}
			}
			nestedTypes = new Type[9]
			{
				typeof(SaveLocalPreferences),
				typeof(ReloadLocalPreferences),
				typeof(WisplightDatabaseAwake),
				typeof(WisplightDatabaseCopy),
				typeof(WisplightSceneAwake),
				typeof(WisplightInventoryLoad),
				typeof(WisplightTranslation),
				typeof(WisplightDemisterAwake),
				typeof(WispTowerPlacement)
			};
			foreach (Type type2 in nestedTypes)
			{
				core.CreateClassProcessor(type2).Patch();
			}
		}

		internal void Refresh()
		{
			LiveConfig instance = LiveConfig.Instance;
			bool flag = instance != null && instance.Ready && instance.Current.Enabled;
			Settings settings = instance?.Current ?? Settings.Defaults;
			mining.Set(flag && settings.MarbleSection && settings.MarbleDrops);
			yield.Set(flag && settings.MarbleSection && settings.MarbleYield != 1f);
			farming.Set(flag && settings.FarmingSection && settings.Farming);
			wisplight.Set(flag && settings.Wisplight);
			if (!flag || !settings.FarmingSection || !settings.Farming)
			{
				FarmingRules.Clear();
			}
			bool flag2 = flag && settings.Wisplight;
			if (flag2 != wisplightActive)
			{
				wisplightActive = flag2;
				WisplightEffects.Refresh();
			}
		}

		public void Dispose()
		{
			mining.Set(enabled: false);
			yield.Set(enabled: false);
			farming.Set(enabled: false);
			wisplight.Set(enabled: false);
		}
	}
	internal sealed class Settings
	{
		internal readonly bool Enabled;

		internal readonly bool MarbleDrops;

		internal readonly bool Farming;

		internal readonly bool Wisplight;

		internal readonly bool WispTower;

		internal readonly bool MarbleSection;

		internal readonly bool FarmingSection;

		internal readonly bool DemistingSection;

		internal readonly float MarbleYield;

		internal readonly float TowerMultiplier;

		internal readonly float LightIntensity;

		internal readonly string TowerRecipe;

		internal readonly string Upgrade2;

		internal readonly string Upgrade3;

		internal readonly string Title2;

		internal readonly string Description2;

		internal readonly string Title3;

		internal readonly string Description3;

		internal const string DefaultTitle2 = "Energized Wisplight";

		internal const string DefaultTitle3 = "Ancient Wisplight";

		internal const string DefaultDescription2 = "An energized wisplight that pushes the mist back twice as far.";

		internal const string DefaultDescription3 = "An ancient wisplight that pushes the mist back three times as far.";

		internal static readonly Settings Defaults = new Settings(enabled: true, marbleDrops: true);

		internal Settings(bool enabled, bool marbleDrops, bool farming = true, bool wisplight = true, string upgrade2 = "Demister[1],Wisp[1],Silver[2]", string upgrade3 = "dbmc_wisp2[1],Wisp[2],Silver[4]", string title2 = "Energized Wisplight", string description2 = "An energized wisplight that pushes the mist back twice as far.", string title3 = "Ancient Wisplight", string description3 = "An ancient wisplight that pushes the mist back three times as far.", bool wispTower = true, bool marbleSection = true, bool farmingSection = true, bool demistingSection = true, float marbleYield = 1f, string towerRecipe = "Wisp[5],Eitr[10],BlackMarble[30]", float towerMultiplier = 5f, float lightIntensity = 1f)
		{
			UpgradeRecipe.Validate(upgrade2, enabled && wisplight);
			UpgradeRecipe.Validate(upgrade3, enabled && wisplight);
			ValidateText(title2, 128, title: true);
			ValidateText(title3, 128, title: true);
			ValidateText(description2, 2048, title: false);
			ValidateText(description3, 2048, title: false);
			if (float.IsNaN(marbleYield) || float.IsInfinity(marbleYield) || marbleYield < 1f || marbleYield > 10f)
			{
				throw new FormatException("Black Marble Yield must be between 1 and 10.");
			}
			if (float.IsNaN(towerMultiplier) || float.IsInfinity(towerMultiplier) || towerMultiplier < 1f || towerMultiplier > 10f)
			{
				throw new FormatException("Demist Multiplier must be between 1 and 10.");
			}
			UpgradeRecipe.Validate(towerRecipe, enabled && demistingSection && wispTower);
			if (float.IsNaN(lightIntensity) || float.IsInfinity(lightIntensity) || lightIntensity < 0f || lightIntensity > 3f)
			{
				throw new FormatException("Light Intensity must be between 0 and 3.");
			}
			Enabled = enabled;
			MarbleDrops = marbleDrops;
			Farming = farming;
			Wisplight = wisplight;
			WispTower = wispTower;
			MarbleSection = marbleSection;
			FarmingSection = farmingSection;
			DemistingSection = demistingSection;
			MarbleYield = marbleYield;
			TowerRecipe = towerRecipe;
			TowerMultiplier = towerMultiplier;
			LightIntensity = lightIntensity;
			Upgrade2 = upgrade2;
			Upgrade3 = upgrade3;
			Title2 = title2;
			Description2 = description2;
			Title3 = title3;
			Description3 = description3;
		}

		internal bool Same(Settings other)
		{
			if (other != null && Enabled == other.Enabled && MarbleDrops == other.MarbleDrops && Farming == other.Farming && Wisplight == other.Wisplight && WispTower == other.WispTower && Upgrade2 == other.Upgrade2 && Upgrade3 == other.Upgrade3 && Title2 == other.Title2 && Description2 == other.Description2 && Title3 == other.Title3 && Description3 == other.Description3 && MarbleSection == other.MarbleSection && FarmingSection == other.FarmingSection && DemistingSection == other.DemistingSection && MarbleYield == other.MarbleYield && TowerRecipe == other.TowerRecipe && TowerMultiplier == other.TowerMultiplier)
			{
				return LightIntensity == other.LightIntensity;
			}
			return false;
		}

		private static void ValidateText(string text, int max, bool title)
		{
			if (text == null || text.Length > max || (title && string.IsNullOrWhiteSpace(text)))
			{
				throw new FormatException("Wisplight titles must be nonempty and at most 128 characters; descriptions at most 2048 characters.");
			}
		}
	}
	internal static class DropPolicy
	{
		private static readonly string[] Targets = new string[10] { "cliff_mistlands1", "cliff_mistlands1_frac", "cliff_mistlands2", "cliff_mistlands2_frac", "rock_mistlands1", "rock_mistlands1_frac", "rock_mistlands2", "rock_mistlands2_frac", "rock1_mistlands", "rock1_mountain_frac" };

		private static readonly Dictionary<int, string> TargetHashes = TargetIds();

		private static readonly HashSet<int> ExcludedHashes = ExcludedIds();

		private static Dictionary<int, string> TargetIds()
		{
			Dictionary<int, string> dictionary = new Dictionary<int, string>();
			string[] targets = Targets;
			foreach (string text in targets)
			{
				dictionary.Add(StringExtensionMethods.GetStableHashCode(text), text);
			}
			return dictionary;
		}

		private static HashSet<int> ExcludedIds()
		{
			HashSet<int> hashSet = new HashSet<int>();
			string[] array = new string[14]
			{
				"cliff_mistlands1_creep", "cliff_mistlands1_creep_frac", "ancient_skull", "giant_skull_frac", "giant_ribs_frac", "giant_helmet1_destruction", "giant_helmet2_destruction", "MineRock_Copper", "MineRock_Iron", "MineRock_Obsidian",
				"MineRock_Tin", "rock4_copper_frac", "rock3_silver_frac", "silvervein_frac"
			};
			foreach (string text in array)
			{
				hashSet.Add(StringExtensionMethods.GetStableHashCode(text));
			}
			return hashSet;
		}

		internal static string NameForHash(int hash)
		{
			if (!TargetHashes.TryGetValue(hash, out var value))
			{
				return null;
			}
			return value;
		}

		internal static bool Excluded(int hash)
		{
			return ExcludedHashes.Contains(hash);
		}

		internal static string Normalize(string name)
		{
			while (name != null && name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				name = name.Substring(0, name.Length - 7);
			}
			return name;
		}

		internal static bool IsTarget(string name)
		{
			name = Normalize(name);
			string[] targets = Targets;
			for (int i = 0; i < targets.Length; i++)
			{
				if (targets[i] == name)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class UpgradeRecipe
	{
		internal struct Ingredient
		{
			internal readonly string Prefab;

			internal readonly int Amount;

			internal Ingredient(string prefab, int amount)
			{
				Prefab = prefab;
				Amount = amount;
			}
		}

		internal const string Level2Default = "Demister[1],Wisp[1],Silver[2]";

		internal const string Level3Default = "dbmc_wisp2[1],Wisp[2],Silver[4]";

		internal const int MaxLength = 2048;

		internal const int MaxIngredients = 32;

		private static readonly char[] InvalidNameCharacters = new char[3] { '\r', '\n', '=' };

		internal static void Validate(string text, bool enabled)
		{
			if (text == null || text.Length > 2048)
			{
				throw new FormatException("Custom recipe must be at most " + 2048 + " characters.");
			}
			if (enabled)
			{
				Parse(text);
			}
		}

		internal static Ingredient[] Parse(string text)
		{
			if (string.IsNullOrWhiteSpace(text) || text.Length > 2048)
			{
				throw new FormatException("Custom recipe requires Item1[amount],Item2[amount] entries.");
			}
			string[] array = text.Split(',');
			if (array.Length > 32)
			{
				throw new FormatException("Custom recipe supports at most " + 32 + " ingredients.");
			}
			Ingredient[] array2 = new Ingredient[array.Length];
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				int num = text2.IndexOf('[');
				if (num <= 0 || !text2.EndsWith("]", StringComparison.Ordinal) || text2.IndexOf('[', num + 1) >= 0 || text2.IndexOf(']') != text2.Length - 1)
				{
					throw new FormatException("Invalid custom recipe entry: " + text2 + ". Use Item[amount].");
				}
				string text3 = text2.Substring(0, num).Trim();
				if (text3.Length == 0 || text3.Length > 128 || text3.IndexOfAny(InvalidNameCharacters) >= 0 || !int.TryParse(text2.Substring(num + 1, text2.Length - num - 2).Trim(), NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 1)
				{
					throw new FormatException("Custom recipe amounts must be positive whole numbers; use exact item prefab names.");
				}
				if (!hashSet.Add(text3))
				{
					throw new FormatException("Duplicate custom recipe item: " + text3);
				}
				array2[i] = new Ingredient(text3, result);
			}
			return array2;
		}
	}
	internal static class WispTowerGeometry
	{
		internal const string ColumnName = "blackmarble_column_1";

		internal static void Build(GameObject tower, GameObject columnSource)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ae: Unknown result type (might be due to invalid IL or missing references)
			List<Transform> list = new List<Transform>();
			float bottom = float.PositiveInfinity;
			float top = float.NegativeInfinity;
			for (int i = 0; i < columnSource.transform.childCount; i++)
			{
				Transform child = columnSource.transform.GetChild(i);
				if (((Component)child).CompareTag("snappoint"))
				{
					list.Add(child);
					float num = child.localPosition.y * columnSource.transform.localScale.y;
					bottom = Mathf.Min(bottom, num);
					top = Mathf.Max(top, num);
				}
			}
			if ((top - bottom <= 0.01f || float.IsInfinity(bottom)) && !MeshExtent(columnSource, out bottom, out top))
			{
				throw new InvalidOperationException("Small Black Marble column geometry could not be measured.");
			}
			float num2 = top - bottom;
			if (num2 <= 0.01f || float.IsNaN(num2) || float.IsInfinity(num2))
			{
				throw new InvalidOperationException("Small Black Marble column height is invalid.");
			}
			List<Transform> list2 = new List<Transform>();
			for (int j = 0; j < tower.transform.childCount; j++)
			{
				list2.Add(tower.transform.GetChild(j));
			}
			GameObject val = new GameObject("DvergrMistTowerTop");
			val.transform.SetParent(tower.transform, false);
			foreach (Transform item in list2)
			{
				item.SetParent(val.transform, false);
			}
			Collider[] components = tower.GetComponents<Collider>();
			foreach (Collider val2 in components)
			{
				Component obj = val.AddComponent(((object)val2).GetType());
				string[] array = new string[12]
				{
					"sharedMesh", "convex", "center", "size", "radius", "height", "direction", "isTrigger", "sharedMaterial", "cookingOptions",
					"enabled", "contactOffset"
				};
				foreach (string name in array)
				{
					PropertyInfo property = ((object)val2).GetType().GetProperty(name);
					if (property != null && property.CanRead && property.CanWrite)
					{
						property.SetValue(obj, property.GetValue(val2, null), null);
					}
				}
				Object.DestroyImmediate((Object)(object)val2);
			}
			val.transform.localPosition = new Vector3(0f, num2 * 3f, 0f);
			LODGroup component = tower.GetComponent<LODGroup>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.localReferencePoint += val.transform.localPosition;
				component.RecalculateBounds();
			}
			List<Vector3> positions = new List<Vector3>();
			for (int m = 0; m < 3; m++)
			{
				GameObject val3 = Object.Instantiate<GameObject>(columnSource, tower.transform, false);
				((Object)val3).name = "BlackMarbleColumn" + (m + 1);
				val3.transform.localPosition = new Vector3(0f, (float)m * num2 - bottom, 0f);
				val3.transform.localRotation = Quaternion.identity;
				MonoBehaviour[] componentsInChildren = val3.GetComponentsInChildren<MonoBehaviour>(true);
				for (int k = 0; k < componentsInChildren.Length; k++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren[k]);
				}
				Rigidbody[] componentsInChildren2 = val3.GetComponentsInChildren<Rigidbody>(true);
				for (int k = 0; k < componentsInChildren2.Length; k++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren2[k]);
				}
				foreach (Transform item2 in list)
				{
					Vector3 position = tower.transform.InverseTransformPoint(val3.transform.TransformPoint(item2.localPosition));
					Snap(tower, positions, position);
				}
				val3.SetActive(true);
			}
			for (int n = 0; n <= 3; n++)
			{
				Snap(tower, positions, new Vector3(0f, (float)n * num2, 0f));
			}
			components = tower.GetComponentsInChildren<Collider>(true);
			for (int k = 0; k < components.Length; k++)
			{
				((Component)components[k]).gameObject.layer = tower.layer;
			}
		}

		private static void Snap(GameObject tower, List<Vector3> positions, Vector3 position)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			foreach (Vector3 position2 in positions)
			{
				Vector3 val = position2 - position;
				if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
				{
					return;
				}
			}
			positions.Add(position);
			GameObject val2 = new GameObject("snap_tower_" + positions.Count)
			{
				tag = "snappoint"
			};
			val2.transform.SetParent(tower.transform, false);
			val2.transform.localPosition = position;
		}

		private static bool MeshExtent(GameObject column, out float bottom, out float top)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			bottom = float.PositiveInfinity;
			top = float.NegativeInfinity;
			MeshFilter[] componentsInChildren = column.GetComponentsInChildren<MeshFilter>(true);
			Vector3 val2 = default(Vector3);
			foreach (MeshFilter val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val.sharedMesh))
				{
					Bounds bounds = val.sharedMesh.bounds;
					for (int j = 0; j < 8; j++)
					{
						((Vector3)(ref val2))..ctor(((j & 1) == 0) ? ((Bounds)(ref bounds)).min.x : ((Bounds)(ref bounds)).max.x, ((j & 2) == 0) ? ((Bounds)(ref bounds)).min.y : ((Bounds)(ref bounds)).max.y, ((j & 4) == 0) ? ((Bounds)(ref bounds)).min.z : ((Bounds)(ref bounds)).max.z);
						float num = column.transform.InverseTransformPoint(((Component)val).transform.TransformPoint(val2)).y * column.transform.localScale.y;
						bottom = Mathf.Min(bottom, num);
						top = Mathf.Max(top, num);
					}
				}
			}
			if (!float.IsInfinity(bottom))
			{
				return top > bottom;
			}
			return false;
		}
	}
	public sealed class WispTowerLighting : MonoBehaviour
	{
		private static readonly FieldInfo FlickerBase = AccessTools.Field(typeof(LightFlicker), "m_baseIntensity");

		public Light[] NativeLights;

		public float[] NativeIntensities;

		public LightFlicker[] Flickers;

		private WispTowerPieces registry;

		private bool started;

		internal void Prepare()
		{
			NativeLights = ((Component)this).gameObject.GetComponentsInChildren<Light>(true);
			NativeIntensities = new float[NativeLights.Length];
			Flickers = (LightFlicker[])(object)new LightFlicker[NativeLights.Length];
			for (int i = 0; i < NativeLights.Length; i++)
			{
				NativeIntensities[i] = NativeLights[i].intensity;
				Flickers[i] = ((Component)NativeLights[i]).GetComponent<LightFlicker>();
				if (Object.op_Implicit((Object)(object)Flickers[i]))
				{
					(((Component)NativeLights[i]).GetComponent<WispTowerLightStart>() ?? ((Component)NativeLights[i]).gameObject.AddComponent<WispTowerLightStart>()).Controller = this;
				}
			}
		}

		private void Awake()
		{
			registry = WispTowerPieces.Instance;
			registry?.Add(this);
			Apply();
		}

		private void Start()
		{
			started = true;
			Apply();
		}

		internal void Apply()
		{
			float multiplier = (WispTowerPieces.Enabled ? LiveConfig.Instance.Current.LightIntensity : 1f);
			Set(multiplier);
		}

		private void Set(float multiplier)
		{
			if (NativeLights == null || NativeIntensities == null || Flickers == null)
			{
				return;
			}
			for (int i = 0; i < NativeLights.Length; i++)
			{
				Light val = NativeLights[i];
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				float num = NativeIntensities[i] * multiplier;
				LightFlicker val2 = Flickers[i];
				if (Object.op_Implicit((Object)(object)val2))
				{
					if (started)
					{
						FlickerBase.SetValue(val2, num);
					}
					if (multiplier == 0f || !((Behaviour)val2).enabled)
					{
						val.intensity = num;
					}
				}
				else
				{
					val.intensity = num;
				}
			}
		}

		internal void Restore()
		{
			Set(1f);
		}

		private void OnDestroy()
		{
			Restore();
			registry?.Remove(this);
		}

		internal static void ValidateApi()
		{
			if (FlickerBase?.FieldType != typeof(float))
			{
				throw new MissingMemberException("Wisp Tower light flicker API is incompatible with this Valheim build.");
			}
		}
	}
	public sealed class WispTowerLightStart : MonoBehaviour
	{
		public WispTowerLighting Controller;

		private void Start()
		{
			Controller?.Apply();
		}
	}
	internal sealed class WispTowerPieces : IDisposable
	{
		internal const string PrefabName = "dbmc_wisp_tower";

		internal const string SourceName = "dverger_demister_large";

		internal const string TorchName = "piece_groundtorch_mist";

		internal const string DefaultRecipe = "Wisp[5],Eitr[10],BlackMarble[30]";

		internal const string StoneName = "stone_wall_1x1";

		internal static WispTowerPieces Instance;

		private static readonly FieldInfo[] PieceSettings = PrefabSettings();

		private static readonly FieldInfo SceneHashes = AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs");

		private static readonly MethodInfo RefreshPieces = AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", Type.EmptyTypes, (Type[])null);

		private readonly GameObject root;

		private readonly List<PieceTable> tables = new List<PieceTable>();

		private readonly HashSet<WispTowerRadius> radii = new HashSet<WispTowerRadius>();

		private readonly HashSet<WispTowerLighting> lights = new HashSet<WispTowerLighting>();

		private GameObject prefab;

		private Piece piece;

		private string lastError;

		private string materialsRecipe;

		private bool available;

		internal float TorchRadius { get; private set; }

		internal static bool Enabled
		{
			get
			{
				if (LiveConfig.Instance != null && LiveConfig.Instance.Ready && LiveConfig.Instance.Current.Enabled && LiveConfig.Instance.Current.DemistingSection)
				{
					return LiveConfig.Instance.Current.WispTower;
				}
				return false;
			}
		}

		internal static bool CanBuild
		{
			get
			{
				if (Enabled && Instance != null)
				{
					return Instance.available;
				}
				return false;
			}
		}

		private static FieldInfo[] PrefabSettings()
		{
			List<FieldInfo> list = new List<FieldInfo>();
			FieldInfo[] fields = typeof(Piece).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.Name != "m_nview" && fieldInfo.Name != "m_myListIndex" && !fieldInfo.Name.StartsWith("m_creator", StringComparison.Ordinal))
				{
					list.Add(fieldInfo);
				}
			}
			return list.ToArray();
		}

		internal WispTowerPieces()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			root = new GameObject("MistlandsControl_TowerPrefab");
			root.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)root);
			Instance = this;
		}

		internal static bool IsTower(string name)
		{
			if (name != null && name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				name = name.Substring(0, name.Length - 7);
			}
			return name == "dbmc_wisp_tower";
		}

		internal void Register(ZNetScene scene, ObjectDB db, bool before)
		{
			if (!Object.op_Implicit((Object)(object)scene) || !Object.op_Implicit((Object)(object)db))
			{
				return;
			}
			GameObject itemPrefab = db.GetItemPrefab("Hammer");
			ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			PieceTable val2 = (Object.op_Implicit((Object)(object)val) ? val.m_itemData.m_shared.m_buildPieces : null);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return;
			}
			try
			{
				GameObject val3 = val2.m_pieces.Find((GameObject p) => Object.op_Implicit((Object)(object)p) && ((Object)p).name == "piece_groundtorch_mist");
				Piece val4 = (Object.op_Implicit((Object)(object)val3) ? val3.GetComponent<Piece>() : null);
				if (!Object.op_Implicit((Object)(object)val4))
				{
					throw new InvalidOperationException("Native Wisp Torch build piece was not found.");
				}
				float num = NativeRadius(val3);
				if (num <= 0f || float.IsNaN(num) || float.IsInfinity(num))
				{
					throw new InvalidOperationException("Native Wisp Torch mist-clearing radius was not found.");
				}
				string text = LiveConfig.Instance?.Current.TowerRecipe ?? "Wisp[5],Eitr[10],BlackMarble[30]";
				Requirement[] array;
				if (!Enabled && Object.op_Implicit((Object)(object)piece) && materialsRecipe == text)
				{
					array = piece.m_resources;
				}
				else
				{
					try
					{
						array = Materials(db, text);
					}
					catch when (!Enabled)
					{
						array = (Object.op_Implicit((Object)(object)piece) ? piece.m_resources : Materials(db, "Wisp[5],Eitr[10],BlackMarble[30]"));
					}
				}
				if (!Object.op_Implicit((Object)(object)prefab))
				{
					GameObject val5 = scene.m_prefabs.Find((GameObject p) => Object.op_Implicit((Object)(object)p) && ((Object)p).name == "dverger_demister_large") ?? scene.GetPrefab("dverger_demister_large");
					GameObject val6 = val2.m_pieces.Find((GameObject p) => Object.op_Implicit((Object)(object)p) && ((Object)p).