Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Epic Loot No Fog Destinations v0.3.0
plugins/EpicLootNoFogDestinations/Valheim.EpicLootNoFogDestinations.dll
Decompiled a week agousing 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); } } } } }