Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MistlandsControl v1.0.1
BepInEx/plugins/dannanboi_MistlandsControl.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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).