Decompiled source of DestroyedMoons v1.0.8

BepInEx/plugins/DestroyedMoons/DestroyedMoons.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DestroyedMoons.Config;
using DestroyedMoons.Core;
using DestroyedMoons.Integrations;
using DestroyedMoons.Meltdown;
using DestroyedMoons.Networking;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DestroyedMoons")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.8.0")]
[assembly: AssemblyInformationalVersion("1.0.8")]
[assembly: AssemblyProduct("DestroyedMoons")]
[assembly: AssemblyTitle("DestroyedMoons")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.8.0")]
[module: UnverifiableCode]
[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 DestroyedMoons
{
	[BepInPlugin("soldierling.DestroyedMoons", "DestroyedMoons", "1.0.8")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Init(((BaseUnityPlugin)this).Config);
			if (!ModConfig.Enabled.Value)
			{
				Log.LogWarning((object)"DestroyedMoons is disabled in config. Skipping all patches.");
				return;
			}
			_harmony = new Harmony("soldierling.DestroyedMoons");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			SafeInit("MeltdownDetector", delegate
			{
				MeltdownDetector.Initialize(_harmony);
			});
			SafeInit("RandomMoonFX", delegate
			{
				RandomMoonFxIntegration.Initialize(_harmony);
			});
			SafeInit("MeltdownChance", delegate
			{
				MeltdownChanceIntegration.Initialize();
			});
			SafeInit("SaveDeletion", delegate
			{
				SaveDeletionPatch.Initialize(_harmony);
			});
			Log.LogInfo((object)("DestroyedMoons v1.0.8 loaded. " + $"FacilityMeltdown={SoftDeps.HasFacilityMeltdown}, " + $"RandomMoonFX={SoftDeps.HasRandomMoonFX}, " + $"MeltdownChance={SoftDeps.HasMeltdownChance}, " + $"LethalLevelLoader={SoftDeps.HasLethalLevelLoader}."));
		}

		private static void SafeInit(string label, Action action)
		{
			try
			{
				action();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"{label} init failed (mod continues): {arg}");
			}
		}
	}
	internal static class PluginInfo
	{
		public const string GUID = "soldierling.DestroyedMoons";

		public const string NAME = "DestroyedMoons";

		public const string VERSION = "1.0.8";

		public const string FACILITY_MELTDOWN_GUID = "me.loaforc.facilitymeltdown";

		public const string FACILITY_MELTDOWN_GUID_LEGACY = "TeamXiaolan.FacilityMeltdown";

		public const string RANDOM_MOON_FX_GUID = "zigzag.randommoonfx";

		public const string MELTDOWN_CHANCE_GUID = "den.meltdownchance";

		public const string LETHAL_LEVEL_LOADER_GUID = "imabatby.lethallevelloader";
	}
}
namespace DestroyedMoons.Routing
{
	[HarmonyPatch(typeof(StartOfRound), "ChangeLevelServerRpc")]
	internal static class ChangeLevelServerRpcPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(int levelID)
		{
			try
			{
				string text = MoonKey.FromSelectableLevel(MoonResolver.LevelById(levelID));
				if (DestroyedMoonsService.IsBlockedForRouting(text))
				{
					Plugin.Log.LogWarning((object)$"Cancelled level change to destroyed moon '{text}' (levelID {levelID}).");
					HUDNotify.Show(ModConfig.BlockedMessage.Value);
					return false;
				}
			}
			catch
			{
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")]
	internal static class ParsePlayerSentencePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Terminal __instance, ref TerminalNode __result)
		{
			try
			{
				Handle(__instance, ref __result);
			}
			catch
			{
			}
		}

		private static void Handle(Terminal __instance, ref TerminalNode __result)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.screenText == (Object)null)
			{
				return;
			}
			string text = __instance.screenText.text;
			int textAdded = __instance.textAdded;
			if (textAdded <= 0 || textAdded > text.Length)
			{
				return;
			}
			string text2 = text.Substring(text.Length - textAdded).Trim().ToLowerInvariant();
			if (text2.Length == 0)
			{
				return;
			}
			if (ModConfig.EnableTerminalCommands.Value && text2 == "moonstatus")
			{
				List<string> list = DestroyedMoonsService.DescribeBlocked();
				string text3 = ((list.Count == 0) ? "No moons are currently destroyed." : ("Destroyed / blocked moons:\n - " + string.Join("\n - ", list)));
				__result = TerminalNodeFactory.Message(text3);
			}
			else if (ModConfig.EnableMoonBuyback.Value)
			{
				if (text2 == "buyback")
				{
					__result = TerminalNodeFactory.Message(BuybackBody("MOON BUYBACK\n\n"));
				}
				else if (text2.StartsWith("buyback "))
				{
					__result = TerminalNodeFactory.Message(DoBuyback(__instance, text2.Substring(8).Trim()));
				}
				else if ((text2 == "help" || text2 == "other") && DestroyedMoonsService.DescribeAutoBlocked().Count > 0)
				{
					__result = AppendBuybackSection(__result);
				}
			}
		}

		private static TerminalNode AppendBuybackSection(TerminalNode original)
		{
			string text = "\n\n>MOON BUYBACK\n____________________________\n\n" + BuybackBody(string.Empty);
			if ((Object)(object)original == (Object)null)
			{
				return TerminalNodeFactory.Message(text);
			}
			TerminalNode obj = Object.Instantiate<TerminalNode>(original);
			obj.displayText = (original.displayText ?? string.Empty) + text;
			return obj;
		}

		private static string BuybackBody(string header)
		{
			int value = ModConfig.MoonBuybackPrice.Value;
			StringBuilder stringBuilder = new StringBuilder();
			if (!string.IsNullOrEmpty(header))
			{
				stringBuilder.Append(header);
			}
			List<string> list = DestroyedMoonsService.DescribeAutoBlocked();
			if (list.Count == 0)
			{
				stringBuilder.Append("No moons are currently destroyed.\n");
				return stringBuilder.ToString();
			}
			stringBuilder.Append("These moons were lost to nuclear meltdowns.\n");
			stringBuilder.Append("Restore one for ").Append(value).Append(" credits with:\n");
			stringBuilder.Append("  >BUYBACK [moon name]\n\n");
			foreach (string item in list)
			{
				stringBuilder.Append(" * ").Append(item).Append("   (")
					.Append(value)
					.Append("cr)\n");
			}
			return stringBuilder.ToString();
		}

		private static string DoBuyback(Terminal terminal, string arg)
		{
			if (string.IsNullOrEmpty(arg))
			{
				return "Usage: BUYBACK [moon name]";
			}
			string text = DestroyedMoonsService.ResolveAutoBlockedKey(arg);
			if (text == null)
			{
				return "'" + arg + "' is not a destroyed moon. Type BUYBACK to see the list.";
			}
			int value = ModConfig.MoonBuybackPrice.Value;
			if (terminal.groupCredits < value)
			{
				return $"Not enough credits. Restoring a moon costs {value}; you have {terminal.groupCredits}.";
			}
			string arg2 = text;
			foreach (SelectableLevel item in MoonResolver.AllLevels())
			{
				if (MoonKey.FromSelectableLevel(item) == text)
				{
					arg2 = item.PlanetName;
					break;
				}
			}
			terminal.groupCredits -= value;
			try
			{
				terminal.SyncGroupCreditsServerRpc(terminal.groupCredits, terminal.numberOfItemsInDropship);
			}
			catch
			{
			}
			BlockedMoonsNetwork.RequestUnblock(text);
			return $"'{arg2}' has been restored for {value} credits.\nRemaining balance: {terminal.groupCredits}.";
		}
	}
	internal static class TerminalNodeFactory
	{
		public static TerminalNode Message(string text)
		{
			TerminalNode obj = ScriptableObject.CreateInstance<TerminalNode>();
			((Object)obj).name = "DestroyedMoonsMessage";
			obj.displayText = "\n" + text + "\n\n";
			obj.clearPreviousText = true;
			obj.maxCharactersToType = 999;
			obj.buyRerouteToMoon = -1;
			obj.buyItemIndex = -1;
			obj.itemCost = 0;
			obj.displayPlanetInfo = -1;
			obj.shipUnlockableID = -1;
			obj.playSyncedClip = -1;
			obj.acceptAnything = false;
			obj.overrideOptions = false;
			return obj;
		}
	}
	[HarmonyPatch(typeof(Terminal), "LoadNewNode")]
	internal static class LoadNewNodePatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref TerminalNode node)
		{
			try
			{
				if ((Object)(object)node == (Object)null || node.buyRerouteToMoon < 0)
				{
					return;
				}
				string text = MoonKey.FromSelectableLevel(MoonResolver.LevelById(node.buyRerouteToMoon));
				if (DestroyedMoonsService.IsBlockedForRouting(text))
				{
					if (ModConfig.Verbose)
					{
						Plugin.Log.LogInfo((object)$"Blocked terminal reroute to '{text}' (levelID {node.buyRerouteToMoon}).");
					}
					node = TerminalNodeFactory.Message(ModConfig.BlockedMessage.Value);
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "TextPostProcess")]
	[HarmonyAfter(new string[] { "imabatby.lethallevelloader" })]
	internal static class TextPostProcessStrikethroughPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix(ref string __result)
		{
			if (!ModConfig.StrikethroughBlockedMoons.Value || string.IsNullOrEmpty(__result) || __result.IndexOf("<s>", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return;
			}
			try
			{
				foreach (SelectableLevel item in MoonResolver.AllLevels())
				{
					if (!DestroyedMoonsService.IsBlocked(MoonKey.FromSelectableLevel(item)))
					{
						continue;
					}
					string planetName = item.PlanetName;
					if (!string.IsNullOrEmpty(planetName) && __result.IndexOf(planetName, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						__result = StrikeAll(__result, planetName);
						continue;
					}
					string text = MoonKey.NumberlessDisplay(planetName);
					if (!string.IsNullOrEmpty(text))
					{
						__result = StrikeAll(__result, text);
					}
				}
			}
			catch
			{
			}
		}

		private static string StrikeAll(string text, string name)
		{
			int startIndex = 0;
			while (true)
			{
				int num = text.IndexOf(name, startIndex, StringComparison.OrdinalIgnoreCase);
				if (num < 0)
				{
					break;
				}
				if (num >= 3 && text.Substring(num - 3, 3).Equals("<s>", StringComparison.OrdinalIgnoreCase))
				{
					startIndex = num + name.Length;
					continue;
				}
				string text2 = text.Substring(num, name.Length);
				string text3 = "<s>" + text2 + "</s>";
				text = text.Substring(0, num) + text3 + text.Substring(num + name.Length);
				startIndex = num + text3.Length;
			}
			return text;
		}
	}
}
namespace DestroyedMoons.Networking
{
	internal static class BlockedMoonsNetwork
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnSyncReceived;

			public static HandleNamedMessageDelegate <1>__OnRequestReceived;

			public static HandleNamedMessageDelegate <2>__OnUnblockReceived;
		}

		private const string MSG_SYNC = "DestroyedMoons_Sync";

		private const string MSG_REQUEST = "DestroyedMoons_Req";

		private const string MSG_UNBLOCK = "DestroyedMoons_Unblock";

		private static bool _registered;

		public static void Register()
		{
			//IL_003c: 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_0047: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null || _registered)
			{
				return;
			}
			try
			{
				CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
				object obj = <>O.<0>__OnSyncReceived;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = OnSyncReceived;
					<>O.<0>__OnSyncReceived = val;
					obj = (object)val;
				}
				customMessagingManager.RegisterNamedMessageHandler("DestroyedMoons_Sync", (HandleNamedMessageDelegate)obj);
				CustomMessagingManager customMessagingManager2 = singleton.CustomMessagingManager;
				object obj2 = <>O.<1>__OnRequestReceived;
				if (obj2 == null)
				{
					HandleNamedMessageDelegate val2 = OnRequestReceived;
					<>O.<1>__OnRequestReceived = val2;
					obj2 = (object)val2;
				}
				customMessagingManager2.RegisterNamedMessageHandler("DestroyedMoons_Req", (HandleNamedMessageDelegate)obj2);
				CustomMessagingManager customMessagingManager3 = singleton.CustomMessagingManager;
				object obj3 = <>O.<2>__OnUnblockReceived;
				if (obj3 == null)
				{
					HandleNamedMessageDelegate val3 = OnUnblockReceived;
					<>O.<2>__OnUnblockReceived = val3;
					obj3 = (object)val3;
				}
				customMessagingManager3.RegisterNamedMessageHandler("DestroyedMoons_Unblock", (HandleNamedMessageDelegate)obj3);
				_registered = true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to register net messages: " + ex.Message));
			}
		}

		public unsafe static void BroadcastFullListToAll()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkUtils.IsHost)
			{
				return;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null)
			{
				return;
			}
			string text = BlockedMoonsStore.SerializeForSync();
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(text.Length * 2 + 256, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe(text, false);
				try
				{
					singleton.CustomMessagingManager.SendNamedMessageToAll("DestroyedMoons_Sync", val, (NetworkDelivery)4);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Broadcast failed: " + ex.Message));
				}
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private unsafe static void SendListToClient(ulong clientId)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null)
			{
				return;
			}
			string text = BlockedMoonsStore.SerializeForSync();
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(text.Length * 2 + 256, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe(text, false);
				try
				{
					singleton.CustomMessagingManager.SendNamedMessage("DestroyedMoons_Sync", clientId, val, (NetworkDelivery)4);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Targeted sync failed: " + ex.Message));
				}
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public unsafe static void RequestFullListFromHost()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (NetworkUtils.IsHost)
			{
				return;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(8, (Allocator)2, -1);
			try
			{
				int num = 0;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
				try
				{
					singleton.CustomMessagingManager.SendNamedMessage("DestroyedMoons_Req", 0uL, val, (NetworkDelivery)2);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("List request failed: " + ex.Message));
				}
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public unsafe static void RequestUnblock(string moonKey)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (NetworkUtils.IsHost)
			{
				DestroyedMoonsService.UnblockMoon(moonKey);
				return;
			}
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.CustomMessagingManager == null || string.IsNullOrEmpty(moonKey))
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(moonKey.Length * 2 + 64, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe(moonKey, false);
				try
				{
					singleton.CustomMessagingManager.SendNamedMessage("DestroyedMoons_Unblock", 0uL, val, (NetworkDelivery)2);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Unblock request failed: " + ex.Message));
				}
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void OnSyncReceived(ulong senderClientId, FastBufferReader reader)
		{
			if (NetworkUtils.IsHost)
			{
				return;
			}
			try
			{
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				string[] array = (string.IsNullOrEmpty(text) ? Array.Empty<string>() : text.Split('\n'));
				BlockedMoonsStore.ReplaceFromSync(array);
				Plugin.Log.LogInfo((object)$"Synced {array.Length} destroyed moon(s) from host.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to read sync: " + ex.Message));
			}
		}

		private static void OnRequestReceived(ulong senderClientId, FastBufferReader reader)
		{
			if (NetworkUtils.IsHost)
			{
				SendListToClient(senderClientId);
			}
		}

		private static void OnUnblockReceived(ulong senderClientId, FastBufferReader reader)
		{
			if (!NetworkUtils.IsHost)
			{
				return;
			}
			try
			{
				string key = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref key, false);
				DestroyedMoonsService.UnblockMoon(key);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to read unblock request: " + ex.Message));
			}
		}
	}
}
namespace DestroyedMoons.Meltdown
{
	internal static class MeltdownDetector
	{
		private const string HANDLER_TYPE = "FacilityMeltdown.MeltdownSequence.Behaviours.MeltdownHandler";

		private const string START_METHOD = "StartMeltdownClientRpc";

		private const string EXPLOSION_TYPE = "FacilityMeltdown.MeltdownSequence.Behaviours.FacilityExplosionHandler";

		private const string EXPLOSION_METHOD = "Awake";

		private const string API_TYPE = "FacilityMeltdown.API.MeltdownAPI";

		private static Harmony _harmony;

		private static bool _bound;

		private static string _lastKey;

		private static DateTime _lastTime;

		private static readonly string[] MethodNameCandidates = new string[12]
		{
			"Detonate", "Explode", "Explosion", "SpawnExplosion", "MeltdownActually", "MeltdownEnd", "OnMeltdownFinished", "FinishMeltdown", "CauseMeltdown", "TriggerMeltdown",
			"StartMeltdown", "Meltdown"
		};

		public static bool IsBound => _bound;

		public static void Initialize(Harmony harmony)
		{
			_harmony = harmony;
			if (!SoftDeps.HasFacilityMeltdown)
			{
				Plugin.Log.LogWarning((object)"FacilityMeltdown not detected. Auto-block on meltdown is disabled. Manual/terminal blocking and routing patches still work.");
				return;
			}
			string value = ModConfig.MeltdownPatchTarget.Value;
			if (string.IsNullOrWhiteSpace(value) || !TryPatchByTarget(value))
			{
				int num = 0;
				if (ModConfig.BlockOnMeltdownStart.Value && TryPatchMethod("FacilityMeltdown.MeltdownSequence.Behaviours.MeltdownHandler", "StartMeltdownClientRpc", "meltdown start"))
				{
					num++;
				}
				if (TryPatchMethod("FacilityMeltdown.MeltdownSequence.Behaviours.FacilityExplosionHandler", "Awake", "real detonation"))
				{
					num++;
				}
				if (num == 0 && TrySubscribeApi())
				{
					num++;
				}
				if (num == 0 && TryPatchByScan())
				{
					num++;
				}
				if (num == 0)
				{
					Plugin.Log.LogError((object)"Could not locate any FacilityMeltdown detonation hook. Set 'Advanced/MeltdownPatchTarget' in the config to 'Namespace.Type:Method'. Routing block, persistence and the 'destroymoon' command still work.");
				}
			}
		}

		private static bool TryPatchMethod(string typeName, string methodName, string label)
		{
			try
			{
				Type type = AccessTools.TypeByName(typeName);
				if (type == null)
				{
					return false;
				}
				MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					return false;
				}
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, PostfixMethod(), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_bound = true;
				Plugin.Log.LogInfo((object)("Meltdown hook bound to " + typeName + ":" + methodName + " (" + label + ")."));
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Hook " + typeName + ":" + methodName + " failed: " + ex.Message));
				return false;
			}
		}

		private static bool TryPatchByTarget(string target)
		{
			try
			{
				string[] array = target.Split(':');
				if (array.Length != 2)
				{
					Plugin.Log.LogError((object)("Invalid MeltdownPatchTarget '" + target + "'."));
					return false;
				}
				Type type = AccessTools.TypeByName(array[0]);
				MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, array[1], (Type[])null, (Type[])null) : null);
				if (methodInfo == null)
				{
					Plugin.Log.LogError((object)("MeltdownPatchTarget '" + target + "' not found."));
					return false;
				}
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, PostfixMethod(), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_bound = true;
				Plugin.Log.LogInfo((object)("Meltdown hook bound to override target " + type.FullName + ":" + methodInfo.Name + "."));
				return true;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to patch override '{target}': {arg}");
				return false;
			}
		}

		private static bool TrySubscribeApi()
		{
			try
			{
				Type type = AccessTools.TypeByName("FacilityMeltdown.API.MeltdownAPI");
				if (type == null)
				{
					return false;
				}
				Action action = OnDetonationEvent;
				MethodInfo methodInfo = AccessTools.Method(type, "RegisterMeltdownListener", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(typeof(Action)))
					{
						methodInfo.Invoke(null, new object[1] { action });
						_bound = true;
						Plugin.Log.LogInfo((object)"Meltdown hook subscribed via FacilityMeltdown.API.MeltdownAPI.RegisterMeltdownListener.");
						return true;
					}
				}
				FieldInfo field = type.GetField("OnMeltdownStart", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && typeof(Delegate).IsAssignableFrom(field.FieldType) && field.IsStatic)
				{
					Delegate a = field.GetValue(null) as Delegate;
					field.SetValue(null, Delegate.Combine(a, action));
					_bound = true;
					Plugin.Log.LogInfo((object)"Meltdown hook attached to FacilityMeltdown.API.MeltdownAPI.OnMeltdownStart field.");
					return true;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("MeltdownAPI hook failed: " + ex.Message));
			}
			return false;
		}

		private static bool TryPatchByScan()
		{
			List<(int, MethodInfo)> list = new List<(int, MethodInfo)>();
			foreach (Type item in MeltdownTypes())
			{
				MethodInfo[] methods;
				try
				{
					methods = item.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				catch
				{
					continue;
				}
				MethodInfo[] array = methods;
				foreach (MethodInfo m in array)
				{
					int num = Array.FindIndex(MethodNameCandidates, (string n) => string.Equals(n, m.Name, StringComparison.OrdinalIgnoreCase));
					if (num < 0)
					{
						continue;
					}
					try
					{
						if (m.IsAbstract || m.IsGenericMethodDefinition || m.GetParameters().Length != 0)
						{
							continue;
						}
						goto IL_009d;
					}
					catch
					{
					}
					continue;
					IL_009d:
					list.Add((MethodNameCandidates.Length - num, m));
				}
			}
			foreach (var item2 in list.OrderByDescending<(int, MethodInfo), int>(((int score, MethodInfo m) s) => s.score))
			{
				try
				{
					_harmony.Patch((MethodBase)item2.Item2, (HarmonyMethod)null, PostfixMethod(), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					_bound = true;
					Plugin.Log.LogInfo((object)("Meltdown hook bound to method " + item2.Item2.DeclaringType?.FullName + ":" + item2.Item2.Name + " (auto-scan)."));
					return true;
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Failed to patch " + item2.Item2.Name + ": " + ex.Message));
				}
			}
			return false;
		}

		private static HarmonyMethod PostfixMethod()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			return new HarmonyMethod(typeof(MeltdownDetector).GetMethod("DetonationPostfix", BindingFlags.Static | BindingFlags.NonPublic));
		}

		private static IEnumerable<Type> MeltdownTypes()
		{
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name.IndexOf("FacilityMeltdown", StringComparison.OrdinalIgnoreCase) >= 0);
			if (assembly == null)
			{
				yield break;
			}
			Type[] array;
			try
			{
				array = assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				array = ex.Types.Where((Type t) => t != null).ToArray();
			}
			catch
			{
				yield break;
			}
			Type[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				yield return array2[num];
			}
		}

		private static void DetonationPostfix()
		{
			HandleDetonation();
		}

		private static void OnDetonationEvent()
		{
			HandleDetonation();
		}

		private static void HandleDetonation()
		{
			if (!ModConfig.Enabled.Value)
			{
				return;
			}
			string currentMoonKey = MoonResolver.GetCurrentMoonKey();
			bool isHost = NetworkUtils.IsHost;
			Plugin.Log.LogInfo((object)string.Format("Meltdown signal received. moon='{0}', IsHost={1}.", currentMoonKey ?? "?", isHost));
			if (!isHost)
			{
				return;
			}
			if (string.IsNullOrEmpty(currentMoonKey))
			{
				Plugin.Log.LogWarning((object)"Meltdown signal but current moon unresolved.");
			}
			else if (!(currentMoonKey == _lastKey) || !((DateTime.UtcNow - _lastTime).TotalSeconds < 10.0))
			{
				_lastKey = currentMoonKey;
				_lastTime = DateTime.UtcNow;
				if (DestroyedMoonsService.Whitelist().Contains(currentMoonKey))
				{
					Plugin.Log.LogInfo((object)("Moon '" + currentMoonKey + "' had a meltdown but is whitelisted — not blocking."));
					return;
				}
				BlockedMoonsStore.EnsureLoaded();
				Plugin.Log.LogInfo((object)("FacilityMeltdown on '" + currentMoonKey + "'. Blocking this moon permanently."));
				DestroyedMoonsService.BlockMoon(currentMoonKey);
			}
		}
	}
}
namespace DestroyedMoons.Integrations
{
	internal static class MeltdownChanceIntegration
	{
		public static bool Present { get; private set; }

		public static void Initialize()
		{
			Present = SoftDeps.HasMeltdownChance;
			if (Present)
			{
				Plugin.Log.LogInfo((object)"Meltdown_Chance detected. Blocking will only occur on real detonations.");
			}
			else if (ModConfig.Verbose)
			{
				Plugin.Log.LogInfo((object)"Meltdown_Chance not present.");
			}
		}
	}
	internal static class RandomMoonFxIntegration
	{
		private const string UTILS_TYPE = "RandomMoonFX.Utils";

		private const string BLACKLIST_METHOD = "IsMoonBlacklisted";

		private const string VALID_METHOD = "IsMoonValid";

		private static bool _initialized;

		private static readonly Stopwatch _callClock = Stopwatch.StartNew();

		private static long _lastCallMs = -1000L;

		private static int _burst;

		private const int BurstLimit = 2000;

		public static void Initialize(Harmony harmony)
		{
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Expected O, but got Unknown
			if (_initialized)
			{
				return;
			}
			_initialized = true;
			if (!SoftDeps.HasRandomMoonFX)
			{
				if (ModConfig.Verbose)
				{
					Plugin.Log.LogInfo((object)"RandomMoonFX not present — integration skipped.");
				}
				return;
			}
			if (!ModConfig.FilterRandomMoonFXPool.Value)
			{
				Plugin.Log.LogInfo((object)"RandomMoonFX pool filtering disabled by config. Destroyed moons are still blocked by the terminal/level-change guard.");
				return;
			}
			Type type = AccessTools.TypeByName("RandomMoonFX.Utils");
			if (type == null)
			{
				Plugin.Log.LogWarning((object)"RandomMoonFX present but 'RandomMoonFX.Utils' not found. Destroyed moons still blocked by routing patches, but not filtered from the random pool.");
				return;
			}
			bool flag = false;
			MethodInfo methodInfo = AccessTools.Method(type, "IsMoonBlacklisted", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				try
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(RandomMoonFxIntegration).GetMethod("IsMoonBlacklistedPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					flag = true;
					Plugin.Log.LogInfo((object)"RandomMoonFX filtered via postfix on RandomMoonFX.Utils.IsMoonBlacklisted.");
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Failed to patch IsMoonBlacklisted: " + ex.Message));
				}
			}
			MethodInfo methodInfo2 = AccessTools.Method(type, "IsMoonValid", (Type[])null, (Type[])null);
			if (methodInfo2 != null)
			{
				try
				{
					harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(RandomMoonFxIntegration).GetMethod("IsMoonValidPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					flag = true;
					if (ModConfig.Verbose)
					{
						Plugin.Log.LogInfo((object)"RandomMoonFX also filtered via RandomMoonFX.Utils.IsMoonValid.");
					}
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogWarning((object)("Failed to patch IsMoonValid: " + ex2.Message));
				}
			}
			if (!flag)
			{
				Plugin.Log.LogWarning((object)"RandomMoonFX present but no filter method could be patched. Routing patches remain the backstop.");
			}
			Type type2 = AccessTools.TypeByName("RandomMoonFX.Plugin");
			if (!(type2 != null))
			{
				return;
			}
			string[] array = new string[2] { "RouteRandomPlanet", "StartRandomPlanet" };
			foreach (string text in array)
			{
				MethodInfo methodInfo3 = AccessTools.Method(type2, text, (Type[])null, (Type[])null);
				if (!(methodInfo3 == null))
				{
					try
					{
						harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(RandomMoonFxIntegration).GetMethod("RoutingFinalizer", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null);
					}
					catch (Exception ex3)
					{
						Plugin.Log.LogWarning((object)("Failed to add finalizer to " + text + ": " + ex3.Message));
					}
				}
			}
		}

		private static Exception RoutingFinalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Plugin.Log.LogWarning((object)("Suppressed a RandomMoonFX routing exception to avoid a crash: " + __exception.Message));
				DestroyedMoonsService.BeginRoutingBypass(3000);
			}
			return null;
		}

		public static void SyncBlacklist(IEnumerable<string> blockedKeys)
		{
		}

		private static void IsMoonBlacklistedPostfix(object[] __args, ref bool __result)
		{
			try
			{
				if (!RerollStormGuard() && !__result && ArgIsBlockedLevel(__args))
				{
					__result = true;
				}
			}
			catch
			{
			}
		}

		private static void IsMoonValidPostfix(object[] __args, ref bool __result)
		{
			try
			{
				if (!RerollStormGuard() && __result && ArgIsBlockedLevel(__args))
				{
					__result = false;
				}
			}
			catch
			{
			}
		}

		private static bool RerollStormGuard()
		{
			long elapsedMilliseconds = _callClock.ElapsedMilliseconds;
			if (elapsedMilliseconds - _lastCallMs <= 3)
			{
				_burst++;
			}
			else
			{
				_burst = 0;
			}
			_lastCallMs = elapsedMilliseconds;
			if (_burst > 2000)
			{
				_burst = 0;
				DestroyedMoonsService.BeginRoutingBypass(3000);
				Plugin.Log.LogWarning((object)"RandomMoonFX reroll-storm detected — temporarily allowing routing to prevent a hang.");
				return true;
			}
			return false;
		}

		private static bool ArgIsBlockedLevel(object[] args)
		{
			if (args == null)
			{
				return false;
			}
			foreach (object obj in args)
			{
				SelectableLevel val = (SelectableLevel)((obj is SelectableLevel) ? obj : null);
				if (val != null)
				{
					return DestroyedMoonsService.IsBlockedForRouting(MoonKey.FromSelectableLevel(val));
				}
				if (obj is string raw)
				{
					return DestroyedMoonsService.IsBlockedForRouting(MoonKey.Normalize(raw));
				}
			}
			return false;
		}
	}
}
namespace DestroyedMoons.Core
{
	internal static class BlockedMoonsStore
	{
		private const string ES3_KEY = "DestroyedMoons_BlockedList";

		private static readonly HashSet<string> _blocked = new HashSet<string>();

		private static string _loadedSaveFile;

		public static int Generation { get; private set; }

		private static string CurrentSaveFile
		{
			get
			{
				GameNetworkManager instance = GameNetworkManager.Instance;
				if (!((Object)(object)instance != (Object)null) || string.IsNullOrEmpty(instance.currentSaveFileName))
				{
					return "LCSaveFile1";
				}
				return instance.currentSaveFileName;
			}
		}

		private static string JsonMirrorPath => Path.Combine(Paths.ConfigPath, "DestroyedMoons", "blocked_" + CurrentSaveFile + ".json");

		public static IReadOnlyCollection<string> AutoBlocked => _blocked;

		public static void EnsureLoaded()
		{
			if (_loadedSaveFile == null || !string.Equals(_loadedSaveFile, CurrentSaveFile, StringComparison.Ordinal))
			{
				Load();
			}
		}

		public static void Load()
		{
			_blocked.Clear();
			_loadedSaveFile = CurrentSaveFile;
			if (ModConfig.ResetOnNextLoad.Value)
			{
				Plugin.Log.LogWarning((object)("ResetOnNextLoad was set — clearing destroyed-moon list for '" + _loadedSaveFile + "'."));
				DeleteJsonMirror();
				Save();
				ModConfig.ResetOnNextLoad.Value = false;
				Generation++;
				return;
			}
			try
			{
				string text = null;
				if (Es3Interop.Available)
				{
					if (Es3Interop.KeyExists("DestroyedMoons_BlockedList", _loadedSaveFile))
					{
						text = Es3Interop.LoadString("DestroyedMoons_BlockedList", _loadedSaveFile, null);
					}
					else
					{
						DeleteJsonMirror();
					}
				}
				else
				{
					text = ReadJsonMirror();
				}
				if (!string.IsNullOrEmpty(text))
				{
					string[] array = text.Split(new char[2] { '\n', ';' }, StringSplitOptions.RemoveEmptyEntries);
					for (int i = 0; i < array.Length; i++)
					{
						string text2 = MoonKey.Normalize(array[i]);
						if (!string.IsNullOrEmpty(text2))
						{
							_blocked.Add(text2);
						}
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to load destroyed-moon list: {arg}");
			}
			Generation++;
			Plugin.Log.LogInfo((object)($"Loaded {_blocked.Count} destroyed moon(s) for save '{_loadedSaveFile}': " + "[" + string.Join(", ", _blocked) + "]"));
		}

		public static void Save()
		{
			string text = string.Join("\n", _blocked);
			try
			{
				Es3Interop.SaveString("DestroyedMoons_BlockedList", text, CurrentSaveFile);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ES3 save failed: " + ex.Message));
			}
			WriteJsonMirror(text);
		}

		public static bool Add(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return false;
			}
			if (_blocked.Add(key))
			{
				Generation++;
				Save();
				return true;
			}
			return false;
		}

		public static bool Remove(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return false;
			}
			if (_blocked.Remove(key))
			{
				Generation++;
				Save();
				return true;
			}
			return false;
		}

		public static void Clear()
		{
			_blocked.Clear();
			Generation++;
			Save();
		}

		public static void ReplaceFromSync(IEnumerable<string> keys)
		{
			_blocked.Clear();
			foreach (string key in keys)
			{
				string text = MoonKey.Normalize(key);
				if (!string.IsNullOrEmpty(text))
				{
					_blocked.Add(text);
				}
			}
			Generation++;
		}

		public static string SerializeForSync()
		{
			return string.Join("\n", _blocked);
		}

		public static void OnSaveDeleted(string saveFilePath)
		{
			if (!string.IsNullOrEmpty(saveFilePath))
			{
				string fileName = Path.GetFileName(saveFilePath);
				DeleteJsonMirrorFor(fileName);
				if (string.Equals(fileName, _loadedSaveFile, StringComparison.Ordinal))
				{
					_blocked.Clear();
					_loadedSaveFile = null;
					Generation++;
				}
				Plugin.Log.LogInfo((object)("Save '" + fileName + "' deleted — cleared DestroyedMoons data for that slot."));
			}
		}

		private static void DeleteJsonMirror()
		{
			DeleteJsonMirrorFor(CurrentSaveFile);
		}

		private static void DeleteJsonMirrorFor(string saveFileName)
		{
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "DestroyedMoons", "blocked_" + saveFileName + ".json");
				if (File.Exists(path))
				{
					File.Delete(path);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("JSON mirror delete failed: " + ex.Message));
			}
		}

		private static void WriteJsonMirror(string joined)
		{
			try
			{
				string directoryName = Path.GetDirectoryName(JsonMirrorPath);
				if (!Directory.Exists(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				IEnumerable<string> values = from s in joined.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
					select "\"" + s.Replace("\"", "\\\"") + "\"";
				File.WriteAllText(JsonMirrorPath, "{\n  \"saveFile\": \"" + CurrentSaveFile + "\",\n  \"blockedMoons\": [" + string.Join(", ", values) + "]\n}\n");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("JSON mirror write failed: " + ex.Message));
			}
		}

		private static string ReadJsonMirror()
		{
			try
			{
				if (!File.Exists(JsonMirrorPath))
				{
					return null;
				}
				string text = File.ReadAllText(JsonMirrorPath);
				int num = text.IndexOf('[');
				int num2 = text.IndexOf(']');
				if (num < 0 || num2 < 0 || num2 <= num)
				{
					return null;
				}
				string text2 = text.Substring(num + 1, num2 - num - 1);
				List<string> list = new List<string>();
				string[] array = text2.Split(',');
				for (int i = 0; i < array.Length; i++)
				{
					string text3 = array[i].Trim().Trim('"', ' ');
					if (!string.IsNullOrEmpty(text3))
					{
						list.Add(text3);
					}
				}
				return string.Join("\n", list);
			}
			catch
			{
				return null;
			}
		}
	}
	internal static class DestroyedMoonsService
	{
		private static HashSet<string> _wlCache;

		private static string _wlRaw;

		private static HashSet<string> _blCache;

		private static string _blRaw;

		private static bool _arbCache;

		private static int _arbGen = -1;

		private static string _arbWl;

		private static string _arbBl;

		private static int _arbLevelCount = -1;

		private static readonly Stopwatch _clock = Stopwatch.StartNew();

		private static long _bypassUntilMs = -1L;

		private static bool RoutingBypassActive => _clock.ElapsedMilliseconds < _bypassUntilMs;

		private static HashSet<string> ParseList(string csv)
		{
			HashSet<string> hashSet = new HashSet<string>();
			if (string.IsNullOrWhiteSpace(csv))
			{
				return hashSet;
			}
			string[] array = csv.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = MoonKey.Normalize(array[i]);
				if (!string.IsNullOrEmpty(text))
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}

		public static HashSet<string> Whitelist()
		{
			string text = ModConfig.Whitelist.Value ?? string.Empty;
			if (_wlCache == null || !string.Equals(text, _wlRaw, StringComparison.Ordinal))
			{
				_wlRaw = text;
				_wlCache = ParseList(text);
			}
			return _wlCache;
		}

		public static HashSet<string> ManualBlacklist()
		{
			string text = ModConfig.ManualBlacklist.Value ?? string.Empty;
			if (_blCache == null || !string.Equals(text, _blRaw, StringComparison.Ordinal))
			{
				_blRaw = text;
				_blCache = ParseList(text);
			}
			return _blCache;
		}

		public static HashSet<string> EffectiveBlocked()
		{
			HashSet<string> hashSet = new HashSet<string>(BlockedMoonsStore.AutoBlocked);
			hashSet.UnionWith(ManualBlacklist());
			hashSet.ExceptWith(Whitelist());
			return hashSet;
		}

		public static bool IsBlocked(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return false;
			}
			if (Whitelist().Contains(key))
			{
				return false;
			}
			if (!BlockedMoonsStore.AutoBlocked.Contains(key))
			{
				return ManualBlacklist().Contains(key);
			}
			return true;
		}

		public static bool IsBlocked(SelectableLevel level)
		{
			return IsBlocked(MoonKey.FromSelectableLevel(level));
		}

		public static bool AllRoutableBlocked()
		{
			StartOfRound instance = StartOfRound.Instance;
			int num = (((Object)(object)instance != (Object)null && instance.levels != null) ? instance.levels.Length : 0);
			string text = ModConfig.Whitelist.Value ?? string.Empty;
			string text2 = ModConfig.ManualBlacklist.Value ?? string.Empty;
			if (_arbGen == BlockedMoonsStore.Generation && _arbLevelCount == num && string.Equals(text, _arbWl, StringComparison.Ordinal) && string.Equals(text2, _arbBl, StringComparison.Ordinal))
			{
				return _arbCache;
			}
			int num2 = 0;
			int num3 = 0;
			foreach (SelectableLevel item in MoonResolver.AllLevels())
			{
				if (!MoonResolver.IsCompanyMoon(item))
				{
					num2++;
					if (!IsBlocked(MoonKey.FromSelectableLevel(item)))
					{
						num3++;
					}
				}
			}
			_arbCache = num2 > 0 && num3 == 0;
			_arbGen = BlockedMoonsStore.Generation;
			_arbLevelCount = num;
			_arbWl = text;
			_arbBl = text2;
			return _arbCache;
		}

		public static void BeginRoutingBypass(int milliseconds)
		{
			_bypassUntilMs = _clock.ElapsedMilliseconds + milliseconds;
		}

		public static bool IsBlockedForRouting(string key)
		{
			if (RoutingBypassActive)
			{
				return false;
			}
			if (IsBlocked(key))
			{
				return !AllRoutableBlocked();
			}
			return false;
		}

		public static bool IsBlockedForRouting(SelectableLevel level)
		{
			return IsBlockedForRouting(MoonKey.FromSelectableLevel(level));
		}

		public static void BlockMoon(string key)
		{
			if (string.IsNullOrEmpty(key))
			{
				return;
			}
			if (!NetworkUtils.IsHost)
			{
				if (ModConfig.Verbose)
				{
					Plugin.Log.LogInfo((object)("Ignoring BlockMoon('" + key + "') — not host."));
				}
				return;
			}
			BlockedMoonsStore.EnsureLoaded();
			bool num = BlockedMoonsStore.Add(key);
			if (num)
			{
				Plugin.Log.LogInfo((object)("Moon '" + key + "' permanently blocked for this save (nuclear meltdown)."));
			}
			RandomMoonFxIntegration.SyncBlacklist(EffectiveBlocked());
			BlockedMoonsNetwork.BroadcastFullListToAll();
			if (num)
			{
				MaybeResetIfExhausted();
			}
		}

		public static void UnblockMoon(string key)
		{
			if (!string.IsNullOrEmpty(key) && NetworkUtils.IsHost)
			{
				BlockedMoonsStore.EnsureLoaded();
				if (BlockedMoonsStore.Remove(key))
				{
					Plugin.Log.LogInfo((object)("Moon '" + key + "' restored (buyback)."));
				}
				RandomMoonFxIntegration.SyncBlacklist(EffectiveBlocked());
				BlockedMoonsNetwork.BroadcastFullListToAll();
			}
		}

		private static void MaybeResetIfExhausted()
		{
			if (ModConfig.AutoResetWhenAllBlocked.Value && AllRoutableBlocked())
			{
				Plugin.Log.LogInfo((object)"All routable moon(s) destroyed — auto-resetting the destroyed-moon list.");
				HUDNotify.Show("Every moon has been destroyed. The list resets — moons are available again.");
				ResetAll();
			}
		}

		public static void ResetAll()
		{
			BlockedMoonsStore.Clear();
			RandomMoonFxIntegration.SyncBlacklist(EffectiveBlocked());
			BlockedMoonsNetwork.BroadcastFullListToAll();
			Plugin.Log.LogInfo((object)"Destroyed-moon list cleared.");
		}

		private static Dictionary<string, string> NiceNameMap()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (SelectableLevel item in MoonResolver.AllLevels())
			{
				string text = MoonKey.FromSelectableLevel(item);
				if (!string.IsNullOrEmpty(text) && !dictionary.ContainsKey(text))
				{
					dictionary[text] = item.PlanetName;
				}
			}
			return dictionary;
		}

		public static List<string> DescribeBlocked()
		{
			Dictionary<string, string> map = NiceNameMap();
			string value;
			return (from k in EffectiveBlocked()
				select (!map.TryGetValue(k, out value)) ? k : value into x
				orderby x
				select x).ToList();
		}

		public static List<string> DescribeAutoBlocked()
		{
			Dictionary<string, string> map = NiceNameMap();
			HashSet<string> wl = Whitelist();
			string value;
			return (from k in BlockedMoonsStore.AutoBlocked
				where !wl.Contains(k)
				select (!map.TryGetValue(k, out value)) ? k : value into x
				orderby x
				select x).ToList();
		}

		public static string ResolveAutoBlockedKey(string typedName)
		{
			string text = MoonKey.Normalize(typedName);
			if (string.IsNullOrEmpty(text))
			{
				return null;
			}
			foreach (string item in BlockedMoonsStore.AutoBlocked)
			{
				if (item == text)
				{
					return item;
				}
			}
			foreach (string item2 in BlockedMoonsStore.AutoBlocked)
			{
				if (item2.Contains(text) || text.Contains(item2))
				{
					return item2;
				}
			}
			return null;
		}
	}
	internal static class Es3Interop
	{
		private static Type _es3;

		private static MethodInfo _keyExists;

		private static MethodInfo _saveObj;

		private static MethodInfo _loadDefault;

		private static bool _resolved;

		public static bool Available => Resolve();

		private static bool Resolve()
		{
			if (_resolved)
			{
				return _es3 != null;
			}
			_resolved = true;
			try
			{
				_es3 = AccessTypeByName("ES3");
				if (_es3 == null)
				{
					return false;
				}
				_keyExists = _es3.GetMethod("KeyExists", new Type[2]
				{
					typeof(string),
					typeof(string)
				});
				_saveObj = _es3.GetMethod("Save", new Type[3]
				{
					typeof(string),
					typeof(object),
					typeof(string)
				});
				_loadDefault = _es3.GetMethod("Load", new Type[3]
				{
					typeof(string),
					typeof(string),
					typeof(object)
				});
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ES3 reflection failed: " + ex.Message));
			}
			if (_es3 != null && _keyExists != null && _saveObj != null)
			{
				return _loadDefault != null;
			}
			return false;
		}

		private static Type AccessTypeByName(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				Type type = assemblies[i].GetType(name, throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			return null;
		}

		public static bool KeyExists(string key, string saveFile)
		{
			if (!Resolve())
			{
				return false;
			}
			try
			{
				return (bool)_keyExists.Invoke(null, new object[2] { key, saveFile });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ES3.KeyExists failed: " + ex.Message));
				return false;
			}
		}

		public static void SaveString(string key, string value, string saveFile)
		{
			if (!Resolve())
			{
				return;
			}
			try
			{
				_saveObj.Invoke(null, new object[3] { key, value, saveFile });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ES3.Save failed: " + ex.Message));
			}
		}

		public static string LoadString(string key, string saveFile, string fallback)
		{
			if (!Resolve())
			{
				return fallback;
			}
			try
			{
				return (string)_loadDefault.Invoke(null, new object[3] { key, saveFile, fallback });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("ES3.Load failed: " + ex.Message));
				return fallback;
			}
		}
	}
	internal static class HUDNotify
	{
		public static void Show(string body, string header = "Meltdown Aftermath")
		{
			HUDManager instance = HUDManager.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				try
				{
					instance.DisplayTip(header, body, true, false, "LC_Tip1");
				}
				catch
				{
				}
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "Start")]
	internal static class StartOfRoundStartPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			bool isHost = NetworkUtils.IsHost;
			Plugin.Log.LogInfo((object)$"StartOfRound.Start — IsHost={isHost}. Wiring DestroyedMoons.");
			BlockedMoonsNetwork.Register();
			if (isHost)
			{
				BlockedMoonsStore.Load();
				RandomMoonFxIntegration.SyncBlacklist(DestroyedMoonsService.EffectiveBlocked());
				BlockedMoonsNetwork.BroadcastFullListToAll();
			}
			else
			{
				BlockedMoonsNetwork.RequestFullListFromHost();
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound), "SetTimeAndPlanetToSavedSettings")]
	internal static class SetTimeAndPlanetToSavedSettingsPatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			if (NetworkUtils.IsHost)
			{
				BlockedMoonsStore.Load();
				RandomMoonFxIntegration.SyncBlacklist(DestroyedMoonsService.EffectiveBlocked());
				BlockedMoonsNetwork.BroadcastFullListToAll();
			}
		}
	}
	internal static class MoonKey
	{
		public static string FromSelectableLevel(SelectableLevel level)
		{
			if ((Object)(object)level == (Object)null)
			{
				return null;
			}
			return Normalize(level.PlanetName);
		}

		public static string NumberlessDisplay(string raw)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return raw;
			}
			raw = raw.Trim();
			int i;
			for (i = 0; i < raw.Length && char.IsDigit(raw[i]); i++)
			{
			}
			if (i > 0 && i < raw.Length && (raw[i] == ' ' || raw[i] == '-' || raw[i] == '.'))
			{
				return raw.Substring(i + 1).Trim();
			}
			return raw;
		}

		public static string Normalize(string raw)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return null;
			}
			raw = raw.Trim();
			int i;
			for (i = 0; i < raw.Length && char.IsDigit(raw[i]); i++)
			{
			}
			if (i > 0 && i < raw.Length && (raw[i] == ' ' || raw[i] == '-' || raw[i] == '.'))
			{
				raw = raw.Substring(i + 1).Trim();
			}
			StringBuilder stringBuilder = new StringBuilder(raw.Length);
			string text = raw;
			foreach (char c in text)
			{
				if (!char.IsWhiteSpace(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			if (stringBuilder.Length != 0)
			{
				return stringBuilder.ToString();
			}
			return null;
		}
	}
	internal static class MoonResolver
	{
		public static string GetCurrentMoonKey()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null)
			{
				return null;
			}
			return MoonKey.FromSelectableLevel(instance.currentLevel);
		}

		public static SelectableLevel CurrentLevel()
		{
			if (!((Object)(object)StartOfRound.Instance != (Object)null))
			{
				return null;
			}
			return StartOfRound.Instance.currentLevel;
		}

		public static SelectableLevel LevelById(int levelID)
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || instance.levels == null)
			{
				return null;
			}
			SelectableLevel[] levels = instance.levels;
			foreach (SelectableLevel val in levels)
			{
				if ((Object)(object)val != (Object)null && val.levelID == levelID)
				{
					return val;
				}
			}
			return null;
		}

		public static IEnumerable<SelectableLevel> AllLevels()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || instance.levels == null)
			{
				yield break;
			}
			SelectableLevel[] levels = instance.levels;
			foreach (SelectableLevel val in levels)
			{
				if ((Object)(object)val != (Object)null)
				{
					yield return val;
				}
			}
		}

		public static string DisplayName(SelectableLevel level)
		{
			if (!((Object)(object)level != (Object)null) || string.IsNullOrEmpty(level.PlanetName))
			{
				return "Unknown";
			}
			return level.PlanetName;
		}

		public static bool IsCompanyMoon(SelectableLevel level)
		{
			if ((Object)(object)level == (Object)null)
			{
				return true;
			}
			string planetName = level.PlanetName;
			if (string.IsNullOrEmpty(planetName))
			{
				return false;
			}
			planetName = planetName.ToLowerInvariant();
			if (!planetName.Contains("gordion"))
			{
				return planetName.Contains("company");
			}
			return true;
		}
	}
	internal static class NetworkUtils
	{
		public static bool IsHost
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton != (Object)null)
				{
					if (!singleton.IsHost)
					{
						return singleton.IsServer;
					}
					return true;
				}
				return false;
			}
		}

		public static bool IsClientConnected
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton != (Object)null)
				{
					return singleton.IsListening;
				}
				return false;
			}
		}
	}
	internal static class SaveDeletionPatch
	{
		public static void Initialize(Harmony harmony)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("ES3");
				if (type == null)
				{
					Plugin.Log.LogWarning((object)"ES3 type not found — save-deletion cleanup disabled.");
					return;
				}
				MethodInfo methodInfo = AccessTools.Method(type, "DeleteFile", new Type[1] { typeof(string) }, (Type[])null);
				if (methodInfo == null)
				{
					Plugin.Log.LogWarning((object)"ES3.DeleteFile(string) not found — save-deletion cleanup disabled.");
					return;
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SaveDeletionPatch).GetMethod("OnDeleteFile", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Plugin.Log.LogInfo((object)"Save-deletion cleanup hooked (ES3.DeleteFile).");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to hook save deletion: " + ex.Message));
			}
		}

		private static void OnDeleteFile(object[] __args)
		{
			try
			{
				if (__args != null && __args.Length != 0 && __args[0] is string saveFilePath)
				{
					BlockedMoonsStore.OnSaveDeleted(saveFilePath);
				}
			}
			catch
			{
			}
		}
	}
	internal static class SoftDeps
	{
		public static bool HasFacilityMeltdown => Has("me.loaforc.facilitymeltdown", "TeamXiaolan.FacilityMeltdown");

		public static bool HasRandomMoonFX => Has("zigzag.randommoonfx");

		public static bool HasMeltdownChance => Has("den.meltdownchance");

		public static bool HasLethalLevelLoader => Has("imabatby.lethallevelloader");

		private static bool Has(params string[] guids)
		{
			foreach (string key in Chainloader.PluginInfos.Keys)
			{
				foreach (string b in guids)
				{
					if (string.Equals(key, b, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
				}
			}
			return false;
		}
	}
}
namespace DestroyedMoons.Config
{
	internal static class ModConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<string> ManualBlacklist;

		public static ConfigEntry<string> Whitelist;

		public static ConfigEntry<bool> ResetOnNextLoad;

		public static ConfigEntry<string> BlockedMessage;

		public static ConfigEntry<bool> EnableTerminalCommands;

		public static ConfigEntry<bool> VerboseLogging;

		public static ConfigEntry<bool> StrikethroughBlockedMoons;

		public static ConfigEntry<bool> BlockOnMeltdownStart;

		public static ConfigEntry<bool> AutoResetWhenAllBlocked;

		public static ConfigEntry<bool> EnableMoonBuyback;

		public static ConfigEntry<int> MoonBuybackPrice;

		public static ConfigEntry<bool> FilterRandomMoonFXPool;

		public static ConfigEntry<string> MeltdownPatchTarget;

		public static bool Verbose
		{
			get
			{
				if (VerboseLogging != null)
				{
					return VerboseLogging.Value;
				}
				return false;
			}
		}

		public static void Init(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("General", "Enabled", true, "Master switch. If false the mod applies no patches at all.");
			BlockedMessage = cfg.Bind<string>("General", "BlockedMessage", "This moon was destroyed in a nuclear meltdown. Routing is impossible.", "Message shown when a player tries to travel to a destroyed moon.");
			ManualBlacklist = cfg.Bind<string>("Lists", "ManualBlacklist", "", "Moons that are ALWAYS blocked (comma-separated planet names, e.g. \"Titan, March\"). Numbers and case are ignored, so \"8 Titan\" and \"titan\" both match Titan.");
			Whitelist = cfg.Bind<string>("Lists", "Whitelist", "", "Moons that are NEVER blocked, even after a meltdown (comma-separated). Overrides everything else.");
			ResetOnNextLoad = cfg.Bind<bool>("Maintenance", "ResetBlockedListOnNextLoad", false, "If true, the stored destroyed-moon list for the loaded save is cleared on next load. This flag auto-resets itself back to false afterwards.");
			EnableTerminalCommands = cfg.Bind<bool>("Maintenance", "EnableTerminalCommands", true, "Adds terminal commands: 'moonstatus' (list blocked moons), 'destroymoon' (host: block current moon), 'restoremoons' (host: clear the list). Useful for testing the pipeline.");
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Extra logging for diagnosing detection and routing.");
			StrikethroughBlockedMoons = cfg.Bind<bool>("Visuals", "StrikethroughBlockedMoons", true, "Draw a line through destroyed moon names in the terminal (e.g. the >MOONS list) for clarity.");
			EnableMoonBuyback = cfg.Bind<bool>("Buyback", "EnableMoonBuyback", true, "Allow players to pay to restore a destroyed moon from the terminal. A 'MOON BUYBACK' section appears on the terminal HELP page once moons have been destroyed. Use the command: BUYBACK [moon name].");
			MoonBuybackPrice = cfg.Bind<int>("Buyback", "MoonBuybackPrice", 500, "Credit cost to buy back (restore) a single destroyed moon.");
			FilterRandomMoonFXPool = cfg.Bind<bool>("Compatibility", "FilterRandomMoonFXPool", true, "Remove destroyed moons from RandomMoonFX's random lever pool. Has a built-in anti-hang guard, but if the lever ever freezes the game you can set this to false — routing to destroyed moons is still blocked by the terminal/level-change guard.");
			AutoResetWhenAllBlocked = cfg.Bind<bool>("General", "AutoResetWhenAllBlocked", true, "When every routable (non-Company) moon has been destroyed, automatically clear the destroyed-moon list so the moons become available again — prevents a dead-end where nowhere is reachable.");
			BlockOnMeltdownStart = cfg.Bind<bool>("General", "BlockOnMeltdownStart", true, "Block the moon the moment a meltdown STARTS (reliable, catches meltdowns even if you leave before the blast). A started FacilityMeltdown always ends in an explosion, so the moon is doomed either way. If false, only the final detonation triggers the block.");
			MeltdownPatchTarget = cfg.Bind<string>("Advanced", "MeltdownPatchTarget", "", "Optional override for the FacilityMeltdown explosion hook. Leave blank for auto-detection. Format 'Namespace.Type:Method'. Check the BepInEx log to see what auto-detection found.");
		}
	}
}