Decompiled source of Epic Loot No Fog Destinations v0.3.0

plugins/EpicLootNoFogDestinations/Valheim.EpicLootNoFogDestinations.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using EpicLoot.Adventure;
using EpicLoot.Adventure.Feature;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ValheimEpicLootNoFogDestinations
{
	internal static class ApiContract
	{
		internal static readonly string[] Entries = new string[24]
		{
			"E|EpicLoot.Adventure.BiomeIndexState|Ready|2", "E|EpicLoot.Adventure.BountyState|Available|0", "F|EpicLoot.Adventure.BountyInfo|Biome|False|True|Heightmap/Biome", "F|EpicLoot.Adventure.BountyInfo|State|False|True|EpicLoot.Adventure.BountyState", "F|EpicLoot.Adventure.BountyListElement|BountyInfo|False|True|EpicLoot.Adventure.BountyInfo", "F|EpicLoot.Adventure.Feature.MerchantListPanel`1|MainButton|False|True|UnityEngine.UI.Button", "F|EpicLoot.Adventure.TreasureMapListElement|AlreadyPurchased|False|True|System.Boolean", "M|EpicLoot.Adventure.Feature.AvailableBountiesListPanel|OnMainButtonClicked|False|False|System.Void|", "M|EpicLoot.Adventure.Feature.AvailableBountiesListPanel|RefreshButton|False|True|System.Void|EpicLoot.Adventure.Currencies", "M|EpicLoot.Adventure.Feature.BountyLocationEarlyCache|GetRadiusBand|True|False|System.Void|Heightmap/Biome,System.Single&,System.Single&",
			"M|EpicLoot.Adventure.Feature.BountyLocationEarlyCache|TryGetBiomePoint|True|True|System.Collections.IEnumerator|Heightmap/Biome,EpicLoot.Adventure.AdventureSaveData,System.Action`2<System.Boolean,UnityEngine.Vector3>", "M|EpicLoot.Adventure.Feature.MerchantListPanel`1|GetSelectedItem|False|False|!0|", "M|EpicLoot.Adventure.Feature.TreasureMapListPanel|OnMainButtonClicked|False|False|System.Void|", "M|EpicLoot.Adventure.Feature.TreasureMapListPanel|RefreshButton|False|True|System.Void|EpicLoot.Adventure.Currencies", "M|EpicLoot.Adventure.TreasureMapListElement|get_Biome|False|True|Heightmap/Biome|", "M|EpicLoot.Adventure.WorldBiomeIndex|EnsureBuilt|True|False|System.Void|", "M|EpicLoot.Adventure.WorldBiomeIndex|get_State|True|False|EpicLoot.Adventure.BiomeIndexState|", "M|EpicLoot.Adventure.WorldBiomeIndex|TryCandidate|True|False|System.Boolean|Heightmap/Biome,UnityEngine.Vector2,System.Single,System.Single,System.Boolean,System.Boolean,System.Int32&,UnityEngine.Vector3&", "P|EpicLoot.Adventure.Feature.BountyLocationEarlyCache|TryGetBiomePoint|biome,saveData,onComplete", "T|EpicLoot.Adventure.BountyInfo",
			"T|EpicLoot.Adventure.BountyListElement", "T|EpicLoot.Adventure.BountyState", "T|EpicLoot.Adventure.Feature.MerchantListPanel`1", "T|EpicLoot.Adventure.TreasureMapListElement"
		};
	}
	internal static class CentreRules
	{
		internal static bool Visible(BitArray own, BitArray shared, int index)
		{
			if (index >= 0 && index < own.Length && index < shared.Length)
			{
				if (!own[index])
				{
					return shared[index];
				}
				return true;
			}
			return false;
		}

		internal static void PixelCentre(int index, int size, float pixelSize, out float x, out float z)
		{
			if (size <= 0 || index < 0 || index >= (long)size * (long)size || pixelSize <= 0f || float.IsNaN(pixelSize) || float.IsInfinity(pixelSize))
			{
				throw new ArgumentOutOfRangeException("index");
			}
			x = (float)(index % size - size / 2) * pixelSize;
			z = (float)(index / size - size / 2) * pixelSize;
		}

		internal static double Radius(float x, float z)
		{
			return Math.Sqrt((double)x * (double)x + (double)z * (double)z);
		}

		internal static bool InBand(double radius, float min, float max)
		{
			if (radius >= (double)min)
			{
				return radius <= (double)max;
			}
			return false;
		}

		internal static double BandDistance(double radius, float min, float max)
		{
			if (!(radius < (double)min))
			{
				if (!(radius > (double)max))
				{
					return 0.0;
				}
				return radius - (double)max;
			}
			return (double)min - radius;
		}
	}
	internal static class Compatibility
	{
		private const BindingFlags Flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		internal static string TypeName(Type t)
		{
			if (t.IsGenericParameter)
			{
				return "!" + t.GenericParameterPosition;
			}
			if (t.IsByRef)
			{
				return TypeName(t.GetElementType()) + "&";
			}
			if (t.IsArray)
			{
				return TypeName(t.GetElementType()) + "[]";
			}
			if (t.IsGenericType)
			{
				return t.GetGenericTypeDefinition().FullName.Replace('+', '/') + "<" + string.Join(",", t.GetGenericArguments().Select(TypeName).ToArray()) + ">";
			}
			return t.FullName.Replace('+', '/');
		}

		internal static string MethodKey(MethodBase m)
		{
			return "M|" + m.DeclaringType.FullName.Replace('+', '/') + "|" + m.Name + "|" + m.IsStatic + "|" + m.IsPublic + "|" + ((m is MethodInfo) ? TypeName(((MethodInfo)m).ReturnType) : "System.Void") + "|" + string.Join(",", (from p in m.GetParameters()
				select TypeName(p.ParameterType)).ToArray());
		}

		internal static string FieldKey(FieldInfo f)
		{
			return "F|" + f.DeclaringType.FullName.Replace('+', '/') + "|" + f.Name + "|" + f.IsStatic + "|" + f.IsPublic + "|" + TypeName(f.FieldType);
		}

		internal static List<string> Check(Assembly assembly, IEnumerable<string> contract)
		{
			List<string> list = new List<string>();
			foreach (string entry in contract)
			{
				try
				{
					string[] parts = entry.Split('|');
					Type type = assembly.GetType(parts[1].Replace('/', '+'), throwOnError: false);
					if (type == null)
					{
						list.Add("Missing type: " + parts[1]);
						continue;
					}
					bool flag = false;
					if (parts[0] == "T")
					{
						flag = true;
					}
					else if (parts[0] == "F")
					{
						flag = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Any((FieldInfo f) => FieldKey(f) == entry);
					}
					else if (parts[0] == "M")
					{
						flag = (from m in type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast<MethodBase>().Concat(type.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
							where m.Name == parts[2]
							select m).Any((MethodBase m) => MethodKey(m) == entry);
					}
					else if (parts[0] == "E")
					{
						FieldInfo field = type.GetField(parts[2], BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						flag = field != null && Convert.ToInt64(field.GetRawConstantValue()).ToString(CultureInfo.InvariantCulture) == parts[3];
					}
					else if (parts[0] == "P")
					{
						flag = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Any((MethodInfo m) => m.Name == parts[2] && string.Join(",", (from p in m.GetParameters()
							select p.Name).ToArray()) == parts[3]);
					}
					if (!flag)
					{
						list.Add("API changed: " + entry);
					}
				}
				catch (Exception ex)
				{
					list.Add("Cannot inspect " + entry + ": " + ex.GetType().Name + ": " + ex.Message);
				}
			}
			return list;
		}
	}
	internal sealed class CompatibilityWarning
	{
		internal const string MinimumVersion = "0.14.4";

		internal const string SupportedVersion = "0.15.4";

		internal const string SupportedVersions = "0.14.4–0.14.5, 0.14.7–0.14.13 and 0.15.1–0.15.4 (API checks)";

		internal const string Header = "EpicLoot: No Fog Destinations — INACTIVE";

		internal const string HookFailureReason = "The required EpicLoot hooks could not be installed. Check the BepInEx log and update/reinstall this add-on.";

		internal string? Reason;

		private WeakReference? warnedWorld;

		private long warnedPlayer;

		internal static string? VersionReason(Version? installed)
		{
			if (!(installed == null))
			{
				return null;
			}
			return "EpicLoot not detected. Install EpicLoot and restart the game.";
		}

		internal static string Message(string reason)
		{
			return "The explored-destination restriction is NOT active.\n\nNEW bounties and treasures may be placed in unexplored map areas. This add-on does not block EpicLoot from running.\n\nPreviously checked EpicLoot versions: 0.14.4–0.14.5, 0.14.7–0.14.13 and 0.15.1–0.15.4 (API checks).\n" + reason;
		}

		internal static string PreviewMessage()
		{
			return "PREVIEW ONLY: EpicLoot, settings and the actual restriction are not changed.\n\n" + Message("Example: a required EpicLoot placement API changed. Check the BepInEx log and update this add-on.");
		}

		internal static bool PreviewReady(int requestedFrame, int currentFrame, bool consoleVisible, bool popupAvailable, bool popupVisible)
		{
			if (currentFrame > requestedFrame && !consoleVisible && popupAvailable)
			{
				return !popupVisible;
			}
			return false;
		}

		internal bool TryShow(bool enabled, object? world, long playerId, bool uiReady, Action<string> show)
		{
			if (!enabled || Reason == null || world == null || playerId == 0L || !uiReady)
			{
				return false;
			}
			if (warnedWorld?.Target == world && warnedPlayer == playerId)
			{
				return false;
			}
			show(Message(Reason));
			warnedWorld = new WeakReference(world);
			warnedPlayer = playerId;
			return true;
		}
	}
	[BepInPlugin("kenjorissen.mods.epiclootnofogdestinations", "EpicLoot: No Fog Destinations", "0.3.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class EpicLootNoFogDestinationsPlugin : BaseUnityPlugin
	{
		[HarmonyPatch]
		private static class OfferButtonPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return PanelMethods("RefreshButton");
			}

			private static void Postfix(object __instance)
			{
				Instance?.RefreshButton(__instance);
			}
		}

		[HarmonyPatch]
		private static class OfferClickPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				return PanelMethods("OnMainButtonClicked");
			}

			private static bool Prefix(object __instance)
			{
				return Instance?.CanClick(__instance) ?? true;
			}
		}

		[HarmonyPatch]
		private static class PlacementPatch
		{
			private static MethodBase TargetMethod()
			{
				return NativeBridge.LocateMethod;
			}

			private static bool Prefix(Biome biome, Action<bool, Vector3> onComplete, ref IEnumerator __result)
			{
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				EpicLootNoFogDestinationsPlugin instance = Instance;
				if ((Object)(object)instance == (Object)null || !instance.Active)
				{
					return true;
				}
				__result = instance.CreatePlacementRoutine(biome, onComplete);
				return false;
			}
		}

		[HarmonyPatch]
		private static class RevealPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Minimap), "Explore", new Type[2]
				{
					typeof(int),
					typeof(int)
				}, (Type[])null);
				yield return AccessTools.Method(typeof(Minimap), "ExploreOthers", new Type[2]
				{
					typeof(int),
					typeof(int)
				}, (Type[])null);
			}

			private static void Postfix(bool __result)
			{
				if (__result)
				{
					Instance?.index?.Revealed();
				}
			}
		}

		[HarmonyPatch]
		private static class ResetMapPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Minimap), "Reset", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Minimap), "ResetSharedMapData", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Minimap), "SetMapData", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Minimap), "ResetAndExplore", new Type[2]
				{
					typeof(BitArray),
					typeof(BitArray)
				}, (Type[])null);
			}

			private static void Postfix()
			{
				Instance?.index?.Reset();
			}
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Action<string> <>9__18_0;

			public static PopupButtonCallback <>9__19_0;

			public static ConsoleEvent <>9__20_0;

			internal void <Update>b__18_0(string message)
			{
				ShowWarningPopup("EpicLoot: No Fog Destinations — INACTIVE", message);
			}

			internal void <ShowWarningPopup>b__19_0()
			{
				UnifiedPopup.Pop();
				EpicLootNoFogDestinationsPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogInfo((object)"Compatibility warning popup acknowledged.");
				}
			}

			internal void <RegisterWarningPreview>b__20_0(ConsoleEventArgs args)
			{
				EpicLootNoFogDestinationsPlugin instance = Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.PreviewWarning(args.Context);
				}
				else
				{
					args.Context.AddString("EpicLoot: No Fog Destinations is not loaded.");
				}
			}
		}

		public const string PluginGuid = "kenjorissen.mods.epiclootnofogdestinations";

		public const string PluginName = "EpicLoot: No Fog Destinations";

		public const string PluginVersion = "0.3.0";

		public const string EpicGuid = "randyknapp.mods.epicloot";

		private static EpicLootNoFogDestinationsPlugin? Instance;

		private ConfigEntry<bool> enabledSetting;

		private Harmony? harmony;

		private ExploredCentreIndex? index;

		private bool reportedError;

		private bool reportedWarningError;

		private bool previewQueued;

		private readonly CompatibilityWarning compatibilityWarning = new CompatibilityWarning();

		private const string MissingCentre = "No suitable explored centre found. Explore more of this biome.";

		private const string Scanning = "Checking explored locations...";

		private bool Active
		{
			get
			{
				if (enabledSetting.Value)
				{
					return index != null;
				}
				return false;
			}
		}

		private string UnavailableReason
		{
			get
			{
				ExploredCentreIndex? exploredCentreIndex = index;
				if (exploredCentreIndex == null || !exploredCentreIndex.Building)
				{
					return "No suitable explored centre found. Explore more of this biome.";
				}
				return "Checking explored locations...";
			}
		}

		private void Awake()
		{
			Instance = this;
			enabledSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Restrict NEW EpicLoot bounties and treasures to explored centres. Disabling restores EpicLoot's original placement.");
			try
			{
				RegisterWarningPreview();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Compatibility warning preview command unavailable: " + ex));
			}
			PluginInfo value;
			Version version = (Chainloader.PluginInfos.TryGetValue("randyknapp.mods.epicloot", out value) ? value.Metadata.Version : null);
			compatibilityWarning.Reason = CompatibilityWarning.VersionReason(version);
			if (compatibilityWarning.Reason == null)
			{
				try
				{
					List<string> list = Compatibility.Check(((object)value.Instance).GetType().Assembly, ApiContract.Entries);
					if (list.Count > 0)
					{
						compatibilityWarning.Reason = "EpicLoot " + version?.ToString() + ": " + string.Join("; ", list.ToArray());
					}
					else
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("EpicLoot " + version?.ToString() + " passed API checks; installing explored-destination hooks."));
					}
				}
				catch (Exception ex2)
				{
					compatibilityWarning.Reason = "Could not inspect EpicLoot APIs: " + ex2.Message;
				}
			}
			if (compatibilityWarning.Reason != null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("No exploration patches installed. " + compatibilityWarning.Reason));
				return;
			}
			try
			{
				InstallSupportedPatches();
			}
			catch (Exception ex3)
			{
				compatibilityWarning.Reason = "The required EpicLoot hooks could not be installed. Check the BepInEx log and update/reinstall this add-on.";
				index?.Reset();
				index = null;
				try
				{
					Harmony? obj = harmony;
					if (obj != null)
					{
						obj.UnpatchSelf();
					}
				}
				catch (Exception ex4)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Could not clean up exploration patches: " + ex4));
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not install exploration patches. Exploration restriction is inactive: " + ex3));
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void InstallSupportedPatches()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			_ = NativeBridge.LocateMethod;
			index = new ExploredCentreIndex((MonoBehaviour)(object)this, ((BaseUnityPlugin)this).Logger);
			harmony = new Harmony("kenjorissen.mods.epiclootnofogdestinations");
			harmony.PatchAll(typeof(EpicLootNoFogDestinationsPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"EpicLoot: No Fog Destinations v0.3.0 loaded: new offers/placements use explored centres; existing quests are unchanged.");
		}

		private void Update()
		{
			index?.CheckContext();
			if (compatibilityWarning.Reason == null || !enabledSetting.Value || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return;
			}
			try
			{
				compatibilityWarning.TryShow(enabledSetting.Value, WorldGenerator.instance, Player.m_localPlayer.GetPlayerID(), UnifiedPopup.IsAvailable() && !UnifiedPopup.IsVisible() && !Console.IsVisible(), delegate(string message)
				{
					ShowWarningPopup("EpicLoot: No Fog Destinations — INACTIVE", message);
				});
			}
			catch (Exception ex)
			{
				if (!reportedWarningError)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Could not display compatibility warning: " + ex));
					reportedWarningError = true;
				}
			}
		}

		private static void ShowWarningPopup(string header, string message)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			object obj = <>c.<>9__19_0;
			if (obj == null)
			{
				PopupButtonCallback val = delegate
				{
					UnifiedPopup.Pop();
					EpicLootNoFogDestinationsPlugin? instance = Instance;
					if (instance != null)
					{
						((BaseUnityPlugin)instance).Logger.LogInfo((object)"Compatibility warning popup acknowledged.");
					}
				};
				<>c.<>9__19_0 = val;
				obj = (object)val;
			}
			UnifiedPopup.Push((PopupBase)new WarningPopup(header, message, (PopupButtonCallback)obj, false));
			UnifiedPopup.SetFocus();
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void RegisterWarningPreview()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			object obj = <>c.<>9__20_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					EpicLootNoFogDestinationsPlugin instance = Instance;
					if ((Object)(object)instance != (Object)null)
					{
						instance.PreviewWarning(args.Context);
					}
					else
					{
						args.Context.AddString("EpicLoot: No Fog Destinations is not loaded.");
					}
				};
				<>c.<>9__20_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("epiclootnofog_warningpreview", "Preview the compatibility warning; does not change EpicLoot, settings or restrictions.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private void PreviewWarning(Terminal terminal)
		{
			if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !UnifiedPopup.IsAvailable())
			{
				terminal.AddString("Enter a world and wait for the UI before previewing the warning.");
			}
			else if (UnifiedPopup.IsVisible())
			{
				terminal.AddString("Close the existing popup before previewing the warning.");
			}
			else if (!previewQueued)
			{
				RequestContext<Player, WorldGenerator, Minimap> context = new RequestContext<Player, WorldGenerator, Minimap>(Player.m_localPlayer, WorldGenerator.instance, Minimap.instance);
				int frameCount = Time.frameCount;
				float deadline = Time.realtimeSinceStartup + 10f;
				terminal.AddString("Queuing warning preview; the console will close automatically.");
				if (Object.op_Implicit((Object)(object)Console.instance))
				{
					((Component)((Terminal)Console.instance).m_chatWindow).gameObject.SetActive(false);
				}
				previewQueued = true;
				try
				{
					((MonoBehaviour)this).StartCoroutine(ShowPreviewLater(context, frameCount, deadline));
				}
				catch (Exception ex)
				{
					previewQueued = false;
					((BaseUnityPlugin)this).Logger.LogError((object)("Could not queue compatibility warning preview: " + ex));
				}
			}
		}

		private IEnumerator ShowPreviewLater(RequestContext<Player, WorldGenerator, Minimap> context, int frame, float deadline)
		{
			try
			{
				while (Object.op_Implicit((Object)(object)context.Player) && context.Matches(Player.m_localPlayer, WorldGenerator.instance, Minimap.instance) && Time.realtimeSinceStartup < deadline)
				{
					if (CompatibilityWarning.PreviewReady(frame, Time.frameCount, Console.IsVisible(), UnifiedPopup.IsAvailable(), UnifiedPopup.IsVisible()))
					{
						try
						{
							ShowWarningPopup("PREVIEW — EpicLoot: No Fog Destinations — INACTIVE", CompatibilityWarning.PreviewMessage());
							yield break;
						}
						catch (Exception ex)
						{
							((BaseUnityPlugin)this).Logger.LogError((object)("Could not preview compatibility warning: " + ex));
							yield break;
						}
					}
					yield return null;
				}
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Compatibility warning preview cancelled: session changed or UI stayed unavailable.");
			}
			finally
			{
				previewQueued = false;
			}
		}

		private void OnDestroy()
		{
			index?.Reset();
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Instance = null;
		}

		private bool TryPoint(Biome biome, out Vector3 point, bool recheck = false)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			point = Vector3.zero;
			try
			{
				return index != null && index.TryGet(biome, out point, recheck);
			}
			catch (Exception ex)
			{
				if (!reportedError)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Explored placement unavailable; refusing new purchases/acceptance: " + ex));
					reportedError = true;
				}
				return false;
			}
		}

		private static void Tell(string message)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		private void RefreshButton(object panel)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: 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)
			if (!Active)
			{
				return;
			}
			Button val = null;
			Biome biome = (Biome)0;
			if (panel is MerchantListPanel<TreasureMapListElement> val2)
			{
				TreasureMapListElement val3 = NativeBridge.SelectedTreasure(val2);
				if (!Object.op_Implicit((Object)(object)val3) || val3.AlreadyPurchased)
				{
					return;
				}
				val = val2.MainButton;
				biome = val3.Biome;
			}
			else if (panel is MerchantListPanel<BountyListElement> val4)
			{
				BountyListElement val5 = NativeBridge.SelectedBounty(val4);
				if (!Object.op_Implicit((Object)(object)val5) || (int)val5.BountyInfo.State != 0)
				{
					return;
				}
				val = val4.MainButton;
				biome = val5.BountyInfo.Biome;
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			if (!TryPoint(biome, out var _))
			{
				((Selectable)val).interactable = false;
				(((Component)val).GetComponent<UITooltip>() ?? ((Component)val).gameObject.AddComponent<UITooltip>()).m_text = UnavailableReason;
				return;
			}
			UITooltip component = ((Component)val).GetComponent<UITooltip>();
			if (Object.op_Implicit((Object)(object)component) && (component.m_text == "No suitable explored centre found. Explore more of this biome." || component.m_text == "Checking explored locations..."))
			{
				component.m_text = "";
			}
		}

		private bool CanClick(object panel)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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)
			if (!Active)
			{
				return true;
			}
			Biome biome;
			if (panel is MerchantListPanel<TreasureMapListElement> arg)
			{
				TreasureMapListElement val = NativeBridge.SelectedTreasure(arg);
				if (!Object.op_Implicit((Object)(object)val) || val.AlreadyPurchased)
				{
					return true;
				}
				biome = val.Biome;
			}
			else
			{
				if (!(panel is MerchantListPanel<BountyListElement> arg2))
				{
					return true;
				}
				BountyListElement val2 = NativeBridge.SelectedBounty(arg2);
				if (!Object.op_Implicit((Object)(object)val2) || (int)val2.BountyInfo.State != 0)
				{
					return true;
				}
				biome = val2.BountyInfo.Biome;
			}
			if (TryPoint(biome, out var _, recheck: true))
			{
				return true;
			}
			Tell(UnavailableReason);
			return false;
		}

		private IEnumerator CreatePlacementRoutine(Biome biome, Action<bool, Vector3> callback)
		{
			//IL_0022: 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)
			RequestContext<Player, WorldGenerator, Minimap> context = new RequestContext<Player, WorldGenerator, Minimap>(Player.m_localPlayer, WorldGenerator.instance, Minimap.instance);
			float deadline = Time.realtimeSinceStartup + 30f;
			PlacementRequest<Vector3> placementRequest = new PlacementRequest<Vector3>(callback, Vector3.zero, delegate(Exception error)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Placement request error; failed validation completes as failure, and upstream callbacks are never retried: " + error));
			});
			return placementRequest.Guard(Locate(biome, context, placementRequest, deadline));
		}

		private IEnumerator Locate(Biome biome, RequestContext<Player, WorldGenerator, Minimap> context, PlacementRequest<Vector3> request, float deadline)
		{
			//IL_000e: 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)
			bool success = false;
			Vector3 point = Vector3.zero;
			while (Object.op_Implicit((Object)(object)context.Player) && context.Matches(Player.m_localPlayer, WorldGenerator.instance, Minimap.instance) && Time.realtimeSinceStartup < deadline)
			{
				if (TryPoint(biome, out point, recheck: true))
				{
					success = true;
					break;
				}
				ExploredCentreIndex? exploredCentreIndex = index;
				if (exploredCentreIndex == null || !exploredCentreIndex.Building || Time.realtimeSinceStartup >= deadline)
				{
					break;
				}
				yield return null;
			}
			if (success)
			{
				success = Object.op_Implicit((Object)(object)context.Player) && Object.op_Implicit((Object)(object)context.Map) && Time.realtimeSinceStartup < deadline && context.Matches(Player.m_localPlayer, WorldGenerator.instance, Minimap.instance) && NativeBridge.IsExplored(context.Map, point);
			}
			if (success)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"New {biome} adventure centre: ({point.x:0.0}, {point.z:0.0}); explored=True.");
				index?.ForgetOffer(biome);
			}
			else
			{
				Tell("No suitable explored centre found. Explore more of this biome.");
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"No explored {biome} centre: placement refused without purchase/acceptance.");
			}
			request.Complete(success, point);
		}

		private static IEnumerable<MethodBase> PanelMethods(string name)
		{
			string[] array = new string[2] { "TreasureMapListPanel", "AvailableBountiesListPanel" };
			foreach (string text in array)
			{
				Type type = AccessTools.TypeByName("EpicLoot.Adventure.Feature." + text) ?? throw new MissingMemberException(text);
				yield return AccessTools.DeclaredMethod(type, name, (Type[])null, (Type[])null) ?? throw new MissingMethodException(text, name);
			}
		}
	}
	internal sealed class ExploredCentreIndex
	{
		private sealed class Offer
		{
			internal Vector3 Point;

			internal float Min;

			internal float Max;

			internal float Expires;

			internal bool Found;
		}

		private readonly MonoBehaviour host;

		private readonly ManualLogSource log;

		private readonly Random random = new Random();

		private Dictionary<Biome, List<Vector3>> points = new Dictionary<Biome, List<Vector3>>();

		private Dictionary<Biome, List<Vector3>>? buildingPoints;

		private readonly Dictionary<Biome, Offer> offers = new Dictionary<Biome, Offer>();

		private Coroutine? running;

		private Minimap? map;

		private Player? player;

		private WorldGenerator? world;

		private long revision;

		private long scannedRevision = -1L;

		private float nextScan;

		private bool complete;

		internal bool Building => running != null;

		internal ExploredCentreIndex(MonoBehaviour host, ManualLogSource log)
		{
			this.host = host;
			this.log = log;
		}

		internal void Revealed()
		{
			revision++;
		}

		internal void ForgetOffer(Biome biome)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			offers.Remove(biome);
		}

		internal void Reset()
		{
			if (running != null)
			{
				host.StopCoroutine(running);
			}
			running = null;
			buildingPoints = null;
			points.Clear();
			offers.Clear();
			map = null;
			player = null;
			world = null;
			complete = false;
			revision++;
			scannedRevision = -1L;
			nextScan = 0f;
		}

		internal void CheckContext()
		{
			if ((Object)(object)map != (Object)(object)Minimap.instance || (Object)(object)player != (Object)(object)Player.m_localPlayer || world != WorldGenerator.instance)
			{
				Reset();
			}
		}

		private bool Ensure()
		{
			CheckContext();
			if (!Object.op_Implicit((Object)(object)Minimap.instance) || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || WorldGenerator.instance == null || !Object.op_Implicit((Object)(object)ZoneSystem.instance))
			{
				return false;
			}
			map = Minimap.instance;
			player = Player.m_localPlayer;
			world = WorldGenerator.instance;
			if (running == null && scannedRevision != revision && Time.realtimeSinceStartup >= nextScan)
			{
				running = host.StartCoroutine(Scan(revision));
			}
			return true;
		}

		private IEnumerator Scan(long startedRevision)
		{
			yield return null;
			Dictionary<Biome, List<Vector3>> next = (buildingPoints = new Dictionary<Biome, List<Vector3>>());
			try
			{
				NativeBridge.EnsureNativeIndex();
				float deadline = Time.realtimeSinceStartup + 30f;
				while (!NativeBridge.IndexReady && Time.realtimeSinceStartup < deadline)
				{
					yield return null;
				}
				if (!NativeBridge.IndexReady || !Object.op_Implicit((Object)(object)map))
				{
					yield break;
				}
				BitArray own = NativeBridge.Explored.Invoke(map);
				BitArray shared = NativeBridge.Shared.Invoke(map);
				int size = map.m_textureSize;
				if ((long)size * (long)size != own.Length || own.Length != shared.Length)
				{
					yield break;
				}
				Stopwatch budget = Stopwatch.StartNew();
				int found = 0;
				for (int i = 0; i < own.Length; i++)
				{
					if (CentreRules.Visible(own, shared, i))
					{
						CentreRules.PixelCentre(i, size, map.m_pixelSize, out var x, out var z);
						Biome biome = world.GetBiome(x, z, 0.02f, false);
						if ((int)biome != 0 && (int)biome != 895 && NativeBridge.Validate(biome, new Vector3(x, 0f, z), 0f, float.MaxValue, requireBand: false, out var point))
						{
							if (!next.TryGetValue(biome, out List<Vector3> value))
							{
								value = (next[biome] = new List<Vector3>());
							}
							value.Add(point);
							found++;
						}
					}
					if ((i & 0x3F) == 0 && budget.Elapsed.TotalMilliseconds >= 2.0)
					{
						yield return null;
						budget.Restart();
					}
				}
				points = next;
				complete = true;
				scannedRevision = startedRevision;
				offers.Clear();
				log.LogInfo((object)$"Explored-centre scan ready: {found} eligible centres in {points.Count} biomes.");
			}
			finally
			{
				ExploredCentreIndex exploredCentreIndex = this;
				exploredCentreIndex.buildingPoints = null;
				exploredCentreIndex.running = null;
				exploredCentreIndex.nextScan = Time.realtimeSinceStartup + 5f;
			}
		}

		internal bool TryGet(Biome biome, out Vector3 point, bool recheck = false)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: 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_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			point = Vector3.zero;
			if (!Ensure() || !NativeBridge.IndexReady)
			{
				return false;
			}
			offers.TryGetValue(biome, out Offer value);
			if (!recheck && value != null && value.Expires > Time.realtimeSinceStartup)
			{
				if (!value.Found)
				{
					return false;
				}
				if (NativeBridge.IsExplored(map, value.Point))
				{
					point = value.Point;
					return true;
				}
			}
			NativeBridge.GetRadiusBand(biome, out var min, out var max);
			if (value != null && value.Min == min && value.Max == max && recheck)
			{
				if (value.Found && NativeBridge.IsExplored(map, value.Point) && (!recheck || NativeBridge.Validate(biome, value.Point, min, max, CentreRules.InBand(CentreRules.Radius(value.Point.x, value.Point.z), min, max), out var _)))
				{
					point = value.Point;
					return true;
				}
				if (!value.Found && !recheck)
				{
					return false;
				}
			}
			Vector3 selected = Vector3.zero;
			int seenInBand = 0;
			int ties = 0;
			double closest = double.PositiveInfinity;
			if (points.TryGetValue(biome, out List<Vector3> value2))
			{
				Consider(value2);
			}
			if (buildingPoints != null && buildingPoints.TryGetValue(biome, out List<Vector3> value3))
			{
				Consider(value3);
			}
			bool flag = (seenInBand > 0 || !double.IsPositiveInfinity(closest)) && NativeBridge.Validate(biome, selected, min, max, seenInBand > 0, out point) && NativeBridge.IsExplored(map, point);
			offers[biome] = new Offer
			{
				Point = point,
				Min = min,
				Max = max,
				Found = flag,
				Expires = Time.realtimeSinceStartup + 0.5f
			};
			return flag;
			void Consider(List<Vector3> list)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: 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_002a: 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_006f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
				foreach (Vector3 item in list)
				{
					if (NativeBridge.IsExplored(map, item))
					{
						double radius = CentreRules.Radius(item.x, item.z);
						if (CentreRules.InBand(radius, min, max))
						{
							if (random.Next(++seenInBand) == 0)
							{
								selected = item;
							}
						}
						else if (seenInBand == 0 && complete)
						{
							double num = CentreRules.BandDistance(radius, min, max);
							if (num < closest)
							{
								closest = num;
								selected = item;
								ties = 1;
							}
							else if (num == closest && random.Next(++ties) == 0)
							{
								selected = item;
							}
						}
					}
				}
			}
		}
	}
	internal static class NativeBridge
	{
		internal delegate void RadiusBand(Biome biome, out float min, out float max);

		private delegate bool CandidateCheck(Biome biome, Vector2 candidate, float min, float max, bool requireBand, bool requireZoneAgreement, ref int tried, out Vector3 point);

		internal static readonly Type LocationType = RequiredType("EpicLoot.Adventure.Feature.BountyLocationEarlyCache");

		private static readonly Type IndexType = RequiredType("EpicLoot.Adventure.WorldBiomeIndex");

		internal static readonly MethodInfo LocateMethod = AccessTools.Method(LocationType, "TryGetBiomePoint", (Type[])null, (Type[])null) ?? throw new MissingMethodException("TryGetBiomePoint");

		internal static readonly RadiusBand GetRadiusBand = AccessTools.MethodDelegate<RadiusBand>(AccessTools.Method(LocationType, "GetRadiusBand", (Type[])null, (Type[])null), (object)null, true);

		internal static readonly Action EnsureNativeIndex = AccessTools.MethodDelegate<Action>(AccessTools.Method(IndexType, "EnsureBuilt", (Type[])null, (Type[])null), (object)null, true);

		private static readonly MethodInfo IndexState = AccessTools.PropertyGetter(IndexType, "State") ?? throw new MissingMethodException("WorldBiomeIndex.State");

		private static readonly CandidateCheck CheckCandidate = AccessTools.MethodDelegate<CandidateCheck>(AccessTools.Method(IndexType, "TryCandidate", (Type[])null, (Type[])null), (object)null, true);

		internal static readonly Func<Minimap, Vector3, bool> IsExplored = AccessTools.MethodDelegate<Func<Minimap, Vector3, bool>>(AccessTools.Method(typeof(Minimap), "IsExplored", (Type[])null, (Type[])null), (object)null, true);

		internal static readonly FieldRef<Minimap, BitArray> Explored = AccessTools.FieldRefAccess<Minimap, BitArray>("m_explored");

		internal static readonly FieldRef<Minimap, BitArray> Shared = AccessTools.FieldRefAccess<Minimap, BitArray>("m_exploredOthers");

		internal static readonly Func<MerchantListPanel<TreasureMapListElement>, TreasureMapListElement> SelectedTreasure = AccessTools.MethodDelegate<Func<MerchantListPanel<TreasureMapListElement>, TreasureMapListElement>>(AccessTools.Method(typeof(MerchantListPanel<TreasureMapListElement>), "GetSelectedItem", (Type[])null, (Type[])null), (object)null, true);

		internal static readonly Func<MerchantListPanel<BountyListElement>, BountyListElement> SelectedBounty = AccessTools.MethodDelegate<Func<MerchantListPanel<BountyListElement>, BountyListElement>>(AccessTools.Method(typeof(MerchantListPanel<BountyListElement>), "GetSelectedItem", (Type[])null, (Type[])null), (object)null, true);

		internal static bool IndexReady => IndexState.Invoke(null, null)?.ToString() == "Ready";

		private static Type RequiredType(string name)
		{
			return AccessTools.TypeByName(name) ?? throw new MissingMemberException(name);
		}

		internal static bool Validate(Biome biome, Vector3 candidate, float min, float max, bool requireBand, out Vector3 point)
		{
			//IL_0004: 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)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			int tried = 0;
			Vector2 candidate2 = default(Vector2);
			((Vector2)(ref candidate2))..ctor(candidate.x, candidate.z);
			if (!CheckCandidate(biome, candidate2, min, max, requireBand, requireZoneAgreement: true, ref tried, out point))
			{
				return CheckCandidate(biome, candidate2, min, max, requireBand, requireZoneAgreement: false, ref tried, out point);
			}
			return true;
		}
	}
	internal sealed class RequestContext<TPlayer, TWorld, TMap> where TPlayer : class where TWorld : class where TMap : class
	{
		internal readonly TPlayer? Player;

		internal readonly TWorld? World;

		internal readonly TMap? Map;

		internal RequestContext(TPlayer? player, TWorld? world, TMap? map)
		{
			Player = player;
			World = world;
			Map = map;
		}

		internal bool Matches(TPlayer? player, TWorld? world, TMap? map)
		{
			if (Player != null && World != null && Map != null && Player == player && World == world)
			{
				return Map == map;
			}
			return false;
		}
	}
	internal sealed class PlacementRequest<TPoint>
	{
		private readonly Action<bool, TPoint>? callback;

		private readonly TPoint failurePoint;

		private readonly Action<Exception> report;

		private bool completed;

		internal PlacementRequest(Action<bool, TPoint>? callback, TPoint failurePoint, Action<Exception> report)
		{
			this.callback = callback;
			this.failurePoint = failurePoint;
			this.report = report;
		}

		internal void Complete(bool success, TPoint point)
		{
			if (completed)
			{
				return;
			}
			completed = true;
			try
			{
				callback?.Invoke(success, (TPoint)(success ? ((object)point) : ((object)failurePoint)));
			}
			catch (Exception error)
			{
				Report(error);
			}
		}

		private void Report(Exception error)
		{
			try
			{
				report(error);
			}
			catch (Exception)
			{
			}
		}

		private bool Step(IEnumerator routine, out object? current)
		{
			current = null;
			try
			{
				if (!routine.MoveNext())
				{
					return false;
				}
				current = routine.Current;
				return true;
			}
			catch (Exception error)
			{
				Report(error);
				return false;
			}
		}

		internal IEnumerator Guard(IEnumerator routine)
		{
			try
			{
				object current;
				while (!completed && Step(routine, out current))
				{
					yield return current;
				}
			}
			finally
			{
				PlacementRequest<TPoint> placementRequest = this;
				try
				{
					(routine as IDisposable)?.Dispose();
				}
				catch (Exception error)
				{
					placementRequest.Report(error);
				}
				if (!placementRequest.completed)
				{
					placementRequest.Complete(success: false, placementRequest.failurePoint);
				}
			}
		}
	}
}