Decompiled source of VegvisirCompass v1.7.1

VegvisirCompass.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MushroomMods;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("dHobbs17")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Loot a limited-use compass from a Vegvisir that points you at its boss. Built for no-map playthroughs.")]
[assembly: AssemblyFileVersion("1.7.1.0")]
[assembly: AssemblyInformationalVersion("1.7.1+7757ccc54aeca6be4b442e05fcdfb2495dea3f50")]
[assembly: AssemblyProduct("Vegvisir Compass")]
[assembly: AssemblyTitle("VegvisirCompass")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.7.1.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 MushroomMods
{
	internal static class PatchIsolation
	{
		internal static int PatchAllIsolated(Harmony harmony, Assembly assembly, ManualLogSource log)
		{
			int num = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(assembly);
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception ex)
				{
					num++;
					log.LogError((object)("Harmony patch class " + type.FullName + " was skipped and the rest of " + harmony.Id + " is still active. " + ex));
				}
			}
			return num;
		}
	}
}
namespace VegvisirCompass
{
	internal static class CompassItem
	{
		internal const string PrefabName = "VegvisirCompass";

		internal const string DisplayName = "Vegvisir Compass";

		internal const string Description = "A shard of rune-carved stone, warm to the touch. Read it and the runes will turn you toward what you seek, then burn away. Only has power close to the vegvisir that yielded it.";

		private const string CloneSourceItem = "SurtlingCore";

		private const string TargetKey = "vc_target";

		private const string BossKey = "vc_boss";

		private const string LocationKey = "vc_loc";

		private const string OriginKey = "vc_origin";

		private const string RangeKey = "vc_range";

		private const string IconResourceName = "VegvisirCompass.compass_icon.png";

		private static GameObject _prefab;

		private static GameObject _prefabContainer;

		private static Sprite _icon;

		private static bool _variantsInstalled;

		internal static bool IsCompass(ItemData item)
		{
			if (item?.m_shared != null)
			{
				return item.m_shared.m_name == "Vegvisir Compass";
			}
			return false;
		}

		internal static ItemData FindCompassForAny(Inventory inventory, List<string> locationNames)
		{
			if (inventory == null || locationNames == null || locationNames.Count == 0)
			{
				return null;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!IsCompass(allItem))
				{
					continue;
				}
				string locationName = GetLocationName(allItem);
				if (string.IsNullOrEmpty(locationName))
				{
					continue;
				}
				foreach (string locationName2 in locationNames)
				{
					if (!string.IsNullOrEmpty(locationName2) && locationName == locationName2)
					{
						return allItem;
					}
				}
			}
			return null;
		}

		internal static void SetTarget(ItemData item, Vector3 target, string bossName, string locationName)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			item.m_customData["vc_target"] = string.Format(CultureInfo.InvariantCulture, "{0};{1};{2}", target.x, target.y, target.z);
			item.m_customData["vc_boss"] = bossName ?? "";
			item.m_customData["vc_loc"] = locationName ?? "";
		}

		internal static string GetLocationName(ItemData item)
		{
			if (item?.m_customData != null && item.m_customData.TryGetValue("vc_loc", out var value))
			{
				return value;
			}
			return "";
		}

		internal static bool TryGetTarget(ItemData item, out Vector3 target)
		{
			//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_0090: 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)
			target = Vector3.zero;
			if (item?.m_customData == null)
			{
				return false;
			}
			if (!item.m_customData.TryGetValue("vc_target", out var value))
			{
				return false;
			}
			string[] array = value.Split(';');
			if (array.Length != 3)
			{
				return false;
			}
			if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return false;
			}
			target = new Vector3(result, result2, result3);
			return true;
		}

		internal static string GetBossName(ItemData item)
		{
			if (item?.m_customData != null && item.m_customData.TryGetValue("vc_boss", out var value))
			{
				return value;
			}
			return "";
		}

		internal static string GetLocalizedBossName(ItemData item)
		{
			string bossName = GetBossName(item);
			if (string.IsNullOrEmpty(bossName))
			{
				return "";
			}
			if (Localization.instance == null)
			{
				return bossName;
			}
			return Localization.instance.Localize(bossName);
		}

		internal static string GetDisplayName(ItemData item)
		{
			string text = item?.m_shared?.m_name ?? "";
			if (Localization.instance != null)
			{
				text = Localization.instance.Localize(text);
			}
			string localizedBossName = GetLocalizedBossName(item);
			if (!string.IsNullOrEmpty(localizedBossName))
			{
				return text + " - " + localizedBossName;
			}
			return text;
		}

		internal static void SetOrigin(ItemData item, Vector3 origin, float range)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			item.m_customData["vc_origin"] = string.Format(CultureInfo.InvariantCulture, "{0};{1};{2}", origin.x, origin.y, origin.z);
			item.m_customData["vc_range"] = range.ToString(CultureInfo.InvariantCulture);
		}

		internal static bool TryGetOrigin(ItemData item, out Vector3 origin)
		{
			//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_0090: 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)
			origin = Vector3.zero;
			if (item?.m_customData == null)
			{
				return false;
			}
			if (!item.m_customData.TryGetValue("vc_origin", out var value))
			{
				return false;
			}
			string[] array = value.Split(';');
			if (array.Length != 3)
			{
				return false;
			}
			if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (!float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return false;
			}
			if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
			{
				return false;
			}
			origin = new Vector3(result, result2, result3);
			return true;
		}

		internal static float GetRange(ItemData item)
		{
			if (item?.m_customData != null && item.m_customData.TryGetValue("vc_range", out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			return 0f;
		}

		internal static float HorizontalDistance(Vector3 a, Vector3 b)
		{
			//IL_0000: 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_000d: 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)
			float num = b.x - a.x;
			float num2 = b.z - a.z;
			return Mathf.Sqrt(num * num + num2 * num2);
		}

		internal static void EnsureRegistered(ObjectDB odb)
		{
			if ((Object)(object)odb == (Object)null || odb.m_items == null || (Object)(object)odb.GetItemPrefab("Wood") == (Object)null)
			{
				return;
			}
			if ((Object)(object)_prefab == (Object)null)
			{
				_prefab = BuildPrefab(odb);
				if ((Object)(object)_prefab == (Object)null)
				{
					return;
				}
			}
			if (!odb.m_items.Contains(_prefab))
			{
				odb.m_items.Add(_prefab);
				odb.UpdateRegisters();
				Plugin.Log.LogInfo((object)"Registered VegvisirCompass with ObjectDB.");
			}
		}

		internal static void EnsureNetworkRegistered(ZNetScene scene)
		{
			if (!((Object)(object)scene == (Object)null) && !((Object)(object)_prefab == (Object)null))
			{
				if (!scene.m_prefabs.Contains(_prefab))
				{
					scene.m_prefabs.Add(_prefab);
				}
				int stableHashCode = StringExtensionMethods.GetStableHashCode("VegvisirCompass");
				if (!scene.m_namedPrefabs.ContainsKey(stableHashCode))
				{
					scene.m_namedPrefabs[stableHashCode] = _prefab;
					Plugin.Log.LogInfo((object)"Registered VegvisirCompass with ZNetScene.");
				}
			}
		}

		private static Sprite LoadEmbeddedIcon()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_icon != (Object)null)
			{
				return _icon;
			}
			try
			{
				using Stream stream = typeof(CompassItem).Assembly.GetManifestResourceStream("VegvisirCompass.compass_icon.png");
				if (stream == null)
				{
					Plugin.Log.LogWarning((object)"Embedded icon VegvisirCompass.compass_icon.png not found.");
					return null;
				}
				byte[] array = new byte[stream.Length];
				int num;
				for (int i = 0; i < array.Length; i += num)
				{
					num = stream.Read(array, i, array.Length - i);
					if (num <= 0)
					{
						break;
					}
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!ImageConversion.LoadImage(val, array))
				{
					Plugin.Log.LogWarning((object)"Embedded icon could not be decoded.");
					Object.Destroy((Object)(object)val);
					return null;
				}
				((Object)val).name = "VegvisirCompassIcon";
				((Object)val).hideFlags = (HideFlags)61;
				_icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
				((Object)_icon).name = "VegvisirCompassIcon";
				((Object)_icon).hideFlags = (HideFlags)61;
				Plugin.Log.LogInfo((object)$"Loaded the embedded icon ({((Texture)val).width}x{((Texture)val).height}).");
				return _icon;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Failed to load the embedded icon: " + ex.Message));
				return null;
			}
		}

		private static Sprite[] BuildIconSet()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			Sprite val = LoadEmbeddedIcon();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Sprite[] array = (Sprite[])(object)new Sprite[5]
			{
				val,
				TintedSprite(val, CompassVariant.MerchantTint),
				TintedSprite(val, CompassVariant.MysteryTint),
				TintedSprite(val, CompassVariant.HildirQuestTint),
				TintedSprite(val, CompassVariant.ForgeTint)
			};
			for (int i = 0; i < array.Length; i++)
			{
				if ((Object)(object)array[i] == (Object)null)
				{
					array[i] = val;
				}
			}
			Plugin.Log.LogInfo((object)$"Built {array.Length} compass icon variants.");
			return array;
		}

		private static Sprite TintedSprite(Sprite source, Color tint)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Texture2D val = CompassVariant.Tint(source.texture, tint);
				Sprite obj = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
				((Object)obj).name = "VegvisirCompassIcon_" + ColorUtility.ToHtmlStringRGB(tint);
				((Object)obj).hideFlags = (HideFlags)61;
				return obj;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not tint the compass icon: " + ex.Message));
				return null;
			}
		}

		private static GameObject BuildPrefab(ObjectDB odb)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			string text = "SurtlingCore";
			GameObject itemPrefab = odb.GetItemPrefab(text);
			if ((Object)(object)itemPrefab == (Object)null)
			{
				Plugin.Log.LogError((object)("Cannot build the compass: no item prefab named " + text + " exists. Set Item/CloneSourceItem to a valid vanilla item."));
				return null;
			}
			if ((Object)(object)_prefabContainer == (Object)null)
			{
				_prefabContainer = new GameObject("VegvisirCompassPrefabs");
				_prefabContainer.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)_prefabContainer);
			}
			GameObject val = Object.Instantiate<GameObject>(itemPrefab, _prefabContainer.transform);
			((Object)val).name = "VegvisirCompass";
			ItemDrop component = val.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.Log.LogError((object)("Item prefab " + text + " has no ItemDrop component."));
				Object.Destroy((Object)(object)val);
				return null;
			}
			SharedData shared = component.m_itemData.m_shared;
			shared.m_name = "Vegvisir Compass";
			shared.m_description = "A shard of rune-carved stone, warm to the touch. Read it and the runes will turn you toward what you seek, then burn away. Only has power close to the vegvisir that yielded it.";
			shared.m_itemType = (ItemType)2;
			shared.m_maxStackSize = 1;
			Sprite[] array = BuildIconSet();
			if (array != null)
			{
				shared.m_icons = array;
				shared.m_variants = array.Length;
				_variantsInstalled = true;
			}
			shared.m_weight = 0.5f;
			shared.m_value = 0;
			shared.m_teleportable = true;
			shared.m_questItem = false;
			shared.m_centerCamera = false;
			shared.m_useDurability = true;
			shared.m_maxDurability = Plugin.UsesPerCompass.Value;
			shared.m_durabilityPerLevel = 0f;
			shared.m_useDurabilityDrain = 0f;
			shared.m_durabilityDrain = 0f;
			shared.m_canBeReparied = false;
			shared.m_food = 0f;
			shared.m_foodStamina = 0f;
			shared.m_foodEitr = 0f;
			shared.m_foodBurnTime = 0f;
			shared.m_foodRegen = 0f;
			shared.m_consumeStatusEffect = null;
			shared.m_equipStatusEffect = null;
			component.m_autoPickup = false;
			component.m_itemData.m_stack = 1;
			component.m_itemData.m_durability = Plugin.UsesPerCompass.Value;
			Plugin.Log.LogInfo((object)("Built VegvisirCompass from " + text + "."));
			return val;
		}

		internal static bool Grant(Player player, Vector3 target, string bossName, int uses, Vector3 origin, float range, string locationName)
		{
			//IL_0052: 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)
			Inventory val = ((player != null) ? ((Humanoid)player).GetInventory() : null);
			if (val == null)
			{
				return false;
			}
			ItemData val2 = val.AddItem("VegvisirCompass", 1, 1, 0, player.GetPlayerID(), player.GetPlayerName(), true, false);
			if (val2 == null)
			{
				Plugin.Log.LogWarning((object)"Could not add the compass - the inventory is probably full.");
				return false;
			}
			val2.m_durability = Mathf.Max(1, uses);
			SetTarget(val2, target, bossName, locationName);
			SetOrigin(val2, origin, range);
			if (_variantsInstalled)
			{
				val2.m_variant = CompassVariant.ForLocation(locationName);
				Plugin.Debug($"Compass for {locationName} uses icon variant {val2.m_variant}.");
			}
			return true;
		}
	}
	internal static class CompassRpc
	{
		private const string RequestName = "VC_RequestCompass";

		private const string ResponseName = "VC_GrantCompass";

		private const string LockInName = "VC_LockMerchant";

		private const int StatusGranted = 0;

		private const int StatusCooldown = 1;

		private const int StatusNotFound = 2;

		private static bool _registered;

		internal static void Register()
		{
			if (!_registered && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register<ZPackage>("VC_RequestCompass", (Action<long, ZPackage>)OnServerRequest);
				ZRoutedRpc.instance.Register<ZPackage>("VC_GrantCompass", (Action<long, ZPackage>)OnClientResponse);
				ZRoutedRpc.instance.Register<ZPackage>("VC_LockMerchant", (Action<long, ZPackage>)OnServerLockIn);
				_registered = true;
				Plugin.Debug("Registered compass RPCs.");
			}
		}

		internal static void Reset()
		{
			_registered = false;
		}

		internal static void RequestCompass(Vegvisir stone, Player player)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			foreach (VegvisrLocation location in stone.m_locations)
			{
				if (!string.IsNullOrEmpty(location.m_locationName))
				{
					list.Add(Encode(location.m_locationName, location.m_pinName));
				}
			}
			if (list.Count == 0)
			{
				Plugin.Log.LogWarning((object)"Vegvisir has no usable locations; ignoring.");
			}
			else
			{
				RequestCompass(((Component)stone).transform.position, list, player);
			}
		}

		internal static string Encode(string locationName, string label)
		{
			return locationName + "|" + (label ?? locationName);
		}

		internal static void RequestCompass(Vector3 originPosition, List<string> encodedTargets, Player player)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (ZRoutedRpc.instance != null && encodedTargets != null && encodedTargets.Count != 0)
			{
				ZPackage val = new ZPackage();
				val.Write(originPosition);
				val.Write(((Component)player).transform.position);
				val.Write(string.Join(",", encodedTargets.ToArray()));
				ZRoutedRpc.instance.InvokeRoutedRPC("VC_RequestCompass", new object[1] { val });
				Plugin.Debug($"Requested compass for {encodedTargets.Count} location(s).");
			}
		}

		internal static void RequestLockIn(MerchantDef def, Vector3 traderPosition)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (ZRoutedRpc.instance != null && def != null)
			{
				ZPackage val = new ZPackage();
				val.Write(def.LocationName);
				val.Write(traderPosition);
				ZRoutedRpc.instance.InvokeRoutedRPC("VC_LockMerchant", new object[1] { val });
				Plugin.Debug($"Asked the server to settle {def.DisplayName} at {traderPosition}.");
			}
		}

		private static void OnServerLockIn(long sender, ZPackage pkg)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				string text = pkg.ReadString();
				Vector3 traderPosition = pkg.ReadVector3();
				MerchantDef merchantDef = MerchantLocator.ForLocation(text);
				if (merchantDef == null)
				{
					Plugin.Log.LogWarning((object)("Lock-in requested for an unknown merchant location '" + text + "'."));
				}
				else
				{
					MerchantPlacement.LockIn(merchantDef, traderPosition);
				}
			}
		}

		private static void OnServerRequest(long sender, ZPackage pkg)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			Vector3 val = pkg.ReadVector3();
			pkg.ReadVector3();
			string text = pkg.ReadString();
			float value = Plugin.LootCooldownSeconds.Value;
			if (!LootCooldownRegistry.TryClaim(val, value, out var remainingSeconds))
			{
				Plugin.Debug($"Vegvisir on cooldown for {remainingSeconds:0}s; refusing peer {sender}.");
				Respond(sender, 1, Vector3.zero, "", remainingSeconds, 0, val, 0f, "");
				return;
			}
			int value2 = Plugin.UsesPerCompass.Value;
			float value3 = Plugin.RangeMeters.Value;
			int num = 0;
			string[] array = text.Split(',');
			LocationInstance val3 = default(LocationInstance);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split('|');
				string text2 = array2[0];
				string text3 = ((array2.Length > 1) ? array2[1] : text2);
				MerchantDef merchantDef = MerchantLocator.ForLocation(text2);
				Vector3 val2;
				if (merchantDef != null && MerchantLocator.TryGetSettledPosition(merchantDef, val, out var position))
				{
					val2 = position;
				}
				else
				{
					if (!ZoneSystem.instance.FindClosestLocation(text2, val, ref val3))
					{
						Plugin.Debug("No location instance found for " + text2 + ".");
						continue;
					}
					val2 = val3.m_position;
				}
				string text4 = HildirQuestCatalog.LabelFor(text2);
				if (string.IsNullOrEmpty(text4))
				{
					text4 = MysteryLocationCatalog.LabelFor(text2);
				}
				if (!string.IsNullOrEmpty(text4))
				{
					text3 = text4;
				}
				Plugin.Debug($"Granting compass to peer {sender}, target {text3} ({text2}) " + $"at {val2}, {value2} use(s), range {value3:0}m.");
				Respond(sender, 0, val2, text3, 0f, value2, val, value3, text2);
				num++;
			}
			if (num == 0)
			{
				Plugin.Log.LogWarning((object)"Could not resolve any location for this stone.");
				Respond(sender, 2, Vector3.zero, "", 0f, 0, val, 0f, "");
			}
		}

		private static void Respond(long peer, int status, Vector3 target, string bossName, float retryAfter, int uses, Vector3 origin, float range, string locationName)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write(status);
			val.Write(target);
			val.Write(bossName ?? "");
			val.Write(retryAfter);
			val.Write(uses);
			val.Write(origin);
			val.Write(range);
			val.Write(locationName ?? "");
			ZRoutedRpc.instance.InvokeRoutedRPC(peer, "VC_GrantCompass", new object[1] { val });
		}

		private static void OnClientResponse(long sender, ZPackage pkg)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			int num = pkg.ReadInt();
			Vector3 target = pkg.ReadVector3();
			string text = pkg.ReadString();
			float seconds = pkg.ReadSingle();
			int uses = pkg.ReadInt();
			Vector3 origin = pkg.ReadVector3();
			float range = pkg.ReadSingle();
			string locationName = pkg.ReadString();
			switch (num)
			{
			case 0:
				if (CompassItem.Grant(localPlayer, target, text, uses, origin, range, locationName))
				{
					string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text);
					((Character)localPlayer).Message((MessageType)2, string.IsNullOrEmpty(text2) ? "Vegvisir Compass acquired" : ("Vegvisir Compass - " + text2 + " acquired"), 0, (Sprite)null, false);
				}
				else
				{
					((Character)localPlayer).Message((MessageType)2, "$inventory_full", 0, (Sprite)null, false);
				}
				break;
			case 1:
				((Character)localPlayer).Message((MessageType)2, "The runes are dormant (" + FormatDuration(seconds) + ")", 0, (Sprite)null, false);
				break;
			default:
				((Character)localPlayer).Message((MessageType)2, "The runes reveal nothing", 0, (Sprite)null, false);
				break;
			}
		}

		private static string FormatDuration(float seconds)
		{
			int num = Mathf.Max(0, Mathf.CeilToInt(seconds));
			int num2 = num / 60;
			int num3 = num % 60;
			if (num2 <= 0)
			{
				return $"{num3}s";
			}
			return $"{num2}m {num3}s";
		}
	}
	internal static class CompassVariant
	{
		internal const int Boss = 0;

		internal const int Merchant = 1;

		internal const int MysteriousLocation = 2;

		internal const int HildirQuest = 3;

		internal const int ForgeOfPotential = 4;

		internal const int Count = 5;

		internal static readonly Color MerchantTint = new Color(0.8f, 0.84f, 0.9f);

		internal static readonly Color MysteryTint = new Color(0.88f, 0.27f, 0.2f);

		internal static readonly Color HildirQuestTint = new Color(0.64f, 0.38f, 0.92f);

		internal static readonly Color ForgeTint = new Color(0.55f, 0.78f, 0.92f);

		private const string MysteriousLocationPrefix = "PlaceofMystery";

		internal static int ForLocation(string locationName)
		{
			if (string.IsNullOrEmpty(locationName))
			{
				return 0;
			}
			if (locationName.StartsWith("PlaceofMystery", StringComparison.OrdinalIgnoreCase))
			{
				return 2;
			}
			MerchantDef[] all = MerchantCatalog.All;
			foreach (MerchantDef merchantDef in all)
			{
				if (string.Equals(locationName, merchantDef.LocationName, StringComparison.OrdinalIgnoreCase))
				{
					if (!merchantDef.IsUpgrader)
					{
						return 1;
					}
					return 4;
				}
			}
			HildirQuestDef[] all2 = HildirQuestCatalog.All;
			foreach (HildirQuestDef hildirQuestDef in all2)
			{
				if (string.Equals(locationName, hildirQuestDef.LocationName, StringComparison.OrdinalIgnoreCase))
				{
					return 3;
				}
			}
			return 0;
		}

		internal static Texture2D Tint(Texture2D source, Color tint, float brightness = 1.15f)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(((Texture)source).width, ((Texture)source).height, (TextureFormat)4, false);
			Color[] pixels = source.GetPixels();
			for (int i = 0; i < pixels.Length; i++)
			{
				Color val2 = pixels[i];
				float num = (val2.r * 0.299f + val2.g * 0.587f + val2.b * 0.114f) * brightness;
				pixels[i] = new Color(Mathf.Clamp01(num * tint.r), Mathf.Clamp01(num * tint.g), Mathf.Clamp01(num * tint.b), val2.a);
			}
			val.SetPixels(pixels);
			val.Apply(false);
			((Object)val).name = "VegvisirCompassIcon_Tinted";
			((Object)val).hideFlags = (HideFlags)61;
			return val;
		}
	}
	internal sealed class HildirQuestDef
	{
		internal string LocationName;

		internal string DisplayName;
	}
	internal static class HildirQuestCatalog
	{
		internal static readonly HildirQuestDef[] All = new HildirQuestDef[3]
		{
			new HildirQuestDef
			{
				LocationName = "Hildir_crypt",
				DisplayName = "Brass"
			},
			new HildirQuestDef
			{
				LocationName = "Hildir_cave",
				DisplayName = "Silver"
			},
			new HildirQuestDef
			{
				LocationName = "Hildir_plainsfortress",
				DisplayName = "Bronze"
			}
		};

		internal static string LabelFor(string locationName)
		{
			if (string.IsNullOrEmpty(locationName))
			{
				return "";
			}
			HildirQuestDef[] all = All;
			foreach (HildirQuestDef hildirQuestDef in all)
			{
				if (string.Equals(locationName, hildirQuestDef.LocationName, StringComparison.OrdinalIgnoreCase))
				{
					return hildirQuestDef.DisplayName;
				}
			}
			return "";
		}
	}
	internal static class LootCooldownRegistry
	{
		private const int PruneThreshold = 256;

		private static readonly Dictionary<long, DateTime> LastLooted = new Dictionary<long, DateTime>();

		private static readonly List<long> StaleKeys = new List<long>();

		internal static int TrackedCount => LastLooted.Count;

		internal static long KeyFor(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			long num = Mathf.RoundToInt(position.x * 2f);
			long num2 = Mathf.RoundToInt(position.z * 2f);
			return (num << 32) ^ (num2 & 0xFFFFFFFFu);
		}

		internal static bool TryClaim(Vector3 stonePosition, float cooldownSeconds, out float remainingSeconds)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			remainingSeconds = 0f;
			if (cooldownSeconds <= 0f)
			{
				return true;
			}
			long key = KeyFor(stonePosition);
			DateTime utcNow = DateTime.UtcNow;
			if (LastLooted.TryGetValue(key, out var value))
			{
				double totalSeconds = (utcNow - value).TotalSeconds;
				if (totalSeconds < (double)cooldownSeconds)
				{
					remainingSeconds = (float)((double)cooldownSeconds - totalSeconds);
					return false;
				}
			}
			LastLooted[key] = utcNow;
			PruneIfNeeded(cooldownSeconds, utcNow);
			return true;
		}

		private static void PruneIfNeeded(float cooldownSeconds, DateTime now)
		{
			if (LastLooted.Count < 256)
			{
				return;
			}
			StaleKeys.Clear();
			foreach (KeyValuePair<long, DateTime> item in LastLooted)
			{
				if ((now - item.Value).TotalSeconds >= (double)cooldownSeconds)
				{
					StaleKeys.Add(item.Key);
				}
			}
			foreach (long staleKey in StaleKeys)
			{
				LastLooted.Remove(staleKey);
			}
			Plugin.Debug($"Pruned {StaleKeys.Count} expired cooldown entries, {LastLooted.Count} remain.");
			StaleKeys.Clear();
		}

		internal static void Reset()
		{
			LastLooted.Clear();
			StaleKeys.Clear();
		}
	}
	internal sealed class MerchantDef
	{
		internal string LocationName;

		internal string DisplayName;

		internal string[] RuneStoneLocationNames;

		internal string TraderPrefabName;

		internal float GuidanceMinDistanceFromCentre;

		internal bool IsUpgrader;
	}
	internal static class MerchantCatalog
	{
		internal static readonly MerchantDef[] All = new MerchantDef[4]
		{
			new MerchantDef
			{
				LocationName = "Vendor_BlackForest",
				DisplayName = "Haldor",
				RuneStoneLocationNames = new string[1] { "Runestone_BlackForest" },
				TraderPrefabName = "Haldor",
				GuidanceMinDistanceFromCentre = 750f
			},
			new MerchantDef
			{
				LocationName = "Hildir_camp",
				DisplayName = "Hildir",
				RuneStoneLocationNames = new string[2] { "Runestone_Meadows", "Runestone_Plains" },
				TraderPrefabName = "Hildir",
				GuidanceMinDistanceFromCentre = 1500f
			},
			new MerchantDef
			{
				LocationName = "BogWitch_Camp",
				DisplayName = "Bog Witch",
				RuneStoneLocationNames = new string[1] { "Runestone_Swamps" },
				TraderPrefabName = "BogWitch",
				GuidanceMinDistanceFromCentre = 1500f
			},
			new MerchantDef
			{
				LocationName = "AncientUpgradeStation",
				DisplayName = "Forge of Potential",
				RuneStoneLocationNames = new string[1] { "Runestone_Mountains" },
				TraderPrefabName = "UpgradeStation",
				GuidanceMinDistanceFromCentre = 1500f,
				IsUpgrader = true
			}
		};

		internal static MerchantDef ResolveForStone(Vector3 stonePosition)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			Location location = Location.GetLocation(stonePosition, true);
			if ((Object)(object)location == (Object)null)
			{
				Plugin.Debug("Lorestone: no Location found around the stone.");
				return null;
			}
			string text = ((Object)((Component)location).gameObject).name.Replace("(Clone)", "").Trim();
			MerchantDef[] all = All;
			foreach (MerchantDef merchantDef in all)
			{
				string[] runeStoneLocationNames = merchantDef.RuneStoneLocationNames;
				foreach (string b in runeStoneLocationNames)
				{
					if (string.Equals(text, b, StringComparison.OrdinalIgnoreCase))
					{
						return merchantDef;
					}
				}
			}
			Plugin.Debug("Lorestone: location '" + text + "' is not a guided stone.");
			return null;
		}

		internal static MerchantDef ResolveForTrader(Trader trader)
		{
			if ((Object)(object)trader == (Object)null)
			{
				return null;
			}
			MerchantDef[] all = All;
			foreach (MerchantDef merchantDef in all)
			{
				if (!merchantDef.IsUpgrader && (NameMatches(trader.m_name, merchantDef) || NameMatches(((Object)((Component)trader).gameObject).name, merchantDef)))
				{
					return merchantDef;
				}
			}
			return null;
		}

		internal static MerchantDef ResolveForUpgrader(CraftingStation station)
		{
			if ((Object)(object)station == (Object)null || !station.m_upgrader)
			{
				return null;
			}
			MerchantDef[] all = All;
			foreach (MerchantDef merchantDef in all)
			{
				if (merchantDef.IsUpgrader && (NameMatches(((Object)((Component)station).gameObject).name, merchantDef) || NameMatches(station.m_name, merchantDef)))
				{
					return merchantDef;
				}
			}
			return null;
		}

		private static bool NameMatches(string name, MerchantDef def)
		{
			if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(def.TraderPrefabName))
			{
				return false;
			}
			if (name.IndexOf(def.TraderPrefabName, StringComparison.OrdinalIgnoreCase) < 0)
			{
				return name.IndexOf(def.DisplayName, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		internal static bool IsPastGuidanceGate(MerchantDef def, Vector3 stonePosition)
		{
			//IL_0000: 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_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = new Vector2(stonePosition.x, stonePosition.z);
			return ((Vector2)(ref val)).magnitude >= def.GuidanceMinDistanceFromCentre;
		}
	}
	internal static class MerchantLocator
	{
		internal static bool TryGetSettledPosition(MerchantDef merchant, Vector3 near, out Vector3 position)
		{
			//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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			position = Vector3.zero;
			if (merchant == null)
			{
				return false;
			}
			List<ZDO> list = MerchantPlacement.CollectTraders(merchant);
			if (list.Count == 0)
			{
				Plugin.Debug(merchant.DisplayName + ": none standing anywhere; falling back to the nearest candidate site.");
				return false;
			}
			float num = float.MaxValue;
			foreach (ZDO item in list)
			{
				Vector3 position2 = item.GetPosition();
				float num2 = CompassItem.HorizontalDistance(near, position2);
				if (num2 < num)
				{
					num = num2;
					position = position2;
				}
			}
			bool flag = MerchantPlacement.IsLocked(merchant);
			Plugin.Debug($"{merchant.DisplayName}: {list.Count} standing, nearest at {position} " + string.Format("({0:0}m away, {1}).", num, flag ? "settled" : "provisional"));
			return true;
		}

		internal static MerchantDef ForLocation(string locationName)
		{
			if (string.IsNullOrEmpty(locationName))
			{
				return null;
			}
			MerchantDef[] all = MerchantCatalog.All;
			foreach (MerchantDef merchantDef in all)
			{
				if (string.Equals(locationName, merchantDef.LocationName, StringComparison.OrdinalIgnoreCase))
				{
					return merchantDef;
				}
			}
			return null;
		}
	}
	internal static class MerchantPlacement
	{
		internal enum SupportState
		{
			Unknown,
			Active,
			DisabledUnsupported
		}

		private const string ActiveWorldKey = "VC_MerchantPlacement";

		private const float PresenceInterval = 2f;

		private const float SameTraderRadius = 12f;

		private static bool _warnedUnsupported;

		private static float _nextPresenceCheck;

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

		private static readonly Dictionary<string, Dictionary<Vector2s, Pose>> _lastSeen = new Dictionary<string, Dictionary<Vector2s, Pose>>();

		internal static SupportState State { get; private set; } = SupportState.Unknown;

		internal static bool IsActive => State == SupportState.Active;

		private static string LockKey(MerchantDef def)
		{
			return "VC_MerchantLocked_" + def.LocationName;
		}

		internal static bool IsLocked(MerchantDef def)
		{
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				return ZoneSystem.instance.GetGlobalKey(LockKey(def));
			}
			return false;
		}

		internal static void Reset()
		{
			State = SupportState.Unknown;
			_warnedUnsupported = false;
			_nextPresenceCheck = 0f;
			_lastSeen.Clear();
			_warnedMissingPrefab.Clear();
		}

		internal static void EvaluateWorldSupport(ZoneSystem zones)
		{
			if (State != SupportState.Unknown || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !zones.LocationsGenerated)
			{
				return;
			}
			if (zones.GetGlobalKey("VC_MerchantPlacement"))
			{
				State = SupportState.Active;
				Plugin.Log.LogInfo((object)"Merchant placement active on this world.");
				AutoLockPreExistingUpgraders(zones);
			}
			else if (AnyTraderAlreadyPlaced(zones))
			{
				State = SupportState.DisabledUnsupported;
				if (!_warnedUnsupported)
				{
					_warnedUnsupported = true;
					Plugin.Log.LogWarning((object)"Merchant placement disabled: a camp was already placed under vanilla rules on this world, so its other candidates are gone. Compasses still work; merchants simply stay wherever vanilla put them.");
				}
			}
			else
			{
				zones.SetGlobalKey("VC_MerchantPlacement");
				State = SupportState.Active;
				Plugin.Log.LogInfo((object)"Merchant placement enabled on this world.");
			}
		}

		internal static void SyncClientState(ZoneSystem zones)
		{
			if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer())
			{
				if (zones.GetGlobalKey("VC_MerchantPlacement"))
				{
					State = SupportState.Active;
				}
				else if (State == SupportState.Unknown && zones.LocationsGenerated)
				{
					State = SupportState.DisabledUnsupported;
				}
			}
		}

		private static bool AnyTraderAlreadyPlaced(ZoneSystem zones)
		{
			MerchantDef[] all = MerchantCatalog.All;
			foreach (MerchantDef merchantDef in all)
			{
				if (!merchantDef.IsUpgrader && AnyPlaced(zones, merchantDef))
				{
					return true;
				}
			}
			return false;
		}

		private static bool AnyPlaced(ZoneSystem zones, MerchantDef def)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			List<LocationInstance> list = new List<LocationInstance>();
			if (!zones.FindLocations(def.LocationName, ref list) || list == null)
			{
				return false;
			}
			foreach (LocationInstance item in list)
			{
				if (item.m_placed)
				{
					return true;
				}
			}
			return false;
		}

		private static void AutoLockPreExistingUpgraders(ZoneSystem zones)
		{
			MerchantDef[] all = MerchantCatalog.All;
			foreach (MerchantDef merchantDef in all)
			{
				if (merchantDef.IsUpgrader && !IsLocked(merchantDef) && AnyPlaced(zones, merchantDef))
				{
					zones.SetGlobalKey(LockKey(merchantDef));
					ZoneLocation location = zones.GetLocation(merchantDef.LocationName);
					if (location != null)
					{
						zones.RemoveUnplacedLocations(location);
					}
					Plugin.Log.LogInfo((object)(merchantDef.DisplayName + " was already placed on this world; locking it in place rather than managing provisional stations."));
				}
			}
		}

		internal static void LockIn(MerchantDef def, Vector3 traderPosition)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !IsActive || (Object)(object)ZoneSystem.instance == (Object)null || IsLocked(def))
			{
				return;
			}
			ZoneSystem.instance.SetGlobalKey(LockKey(def));
			int num = 0;
			foreach (ZDO item in CollectTraders(def))
			{
				if (!(CompassItem.HorizontalDistance(item.GetPosition(), traderPosition) <= 12f))
				{
					DestroyTrader(item);
					num++;
				}
			}
			ZoneLocation location = ZoneSystem.instance.GetLocation(def.LocationName);
			if (location != null)
			{
				ZoneSystem.instance.RemoveUnplacedLocations(location);
			}
			_lastSeen.Remove(def.LocationName);
			Plugin.Log.LogInfo((object)($"{def.DisplayName} settled at {traderPosition}" + ((num > 0) ? $"; removed {num} provisional site(s) elsewhere." : ".")));
		}

		internal static void ServerTick(ZoneSystem zones)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			EvaluateWorldSupport(zones);
			if (!IsActive || Time.time < _nextPresenceCheck)
			{
				return;
			}
			_nextPresenceCheck = Time.time + 2f;
			MerchantDef[] all = MerchantCatalog.All;
			foreach (MerchantDef merchantDef in all)
			{
				if (!IsLocked(merchantDef))
				{
					try
					{
						UpdateProvisionalTraders(zones, merchantDef);
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("Presence sweep for " + merchantDef.DisplayName + " failed: " + ex.Message));
					}
				}
			}
		}

		private static void UpdateProvisionalTraders(ZoneSystem zones, MerchantDef def)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: 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)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			foreach (ZDO item in CollectTraders(def))
			{
				Vector3 position = item.GetPosition();
				if (IsWithinVanillaRange(position))
				{
					Remember(def, ZoneSystem.GetZone(position), position, item.GetRotation());
					list.Add(position);
				}
				else
				{
					DestroyTrader(item);
					Plugin.Debug($"Despawned the provisional {def.DisplayName} at {position}; nobody is near.");
				}
			}
			foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance in zones.m_locationInstances)
			{
				LocationInstance value = locationInstance.Value;
				if (value.m_placed && !(PrefabName(value) != def.LocationName) && IsWithinVanillaRange(value.m_position) && !AlreadyStanding(list, value.m_position) && SpawnTrader(def, locationInstance.Key, value.m_position))
				{
					list.Add(value.m_position);
					Plugin.Debug($"Spawned a provisional {def.DisplayName} at the camp in zone {locationInstance.Key}.");
				}
			}
		}

		private static bool AlreadyStanding(List<Vector3> standing, Vector3 campPosition)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			foreach (Vector3 item in standing)
			{
				if (CompassItem.HorizontalDistance(item, campPosition) <= 12f)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsWithinVanillaRange(Vector3 position)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				return false;
			}
			if (ZNetScene.InActiveArea(position, ZNet.instance.GetReferencePosition()))
			{
				return true;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null && ZNetScene.InActiveArea(position, peer.GetRefPos()))
				{
					return true;
				}
			}
			return false;
		}

		internal static List<ZDO> CollectTraders(MerchantDef def)
		{
			List<ZDO> list = new List<ZDO>();
			if (def == null || string.IsNullOrEmpty(def.TraderPrefabName))
			{
				return list;
			}
			if (ZDOMan.instance == null)
			{
				return list;
			}
			try
			{
				int num = 0;
				while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative(def.TraderPrefabName, list, ref num))
				{
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Trader ZDO query for '" + def.TraderPrefabName + "' failed: " + ex.Message));
			}
			return list;
		}

		private static void DestroyTrader(ZDO zdo)
		{
			zdo.SetOwner(ZDOMan.GetSessionID());
			ZDOMan.instance.DestroyZDO(zdo);
		}

		private static bool SpawnTrader(MerchantDef def, Vector2s zoneId, Vector3 campPosition)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(def.TraderPrefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				if (_warnedMissingPrefab.Add(def.TraderPrefabName))
				{
					Plugin.Log.LogWarning((object)("No prefab named '" + def.TraderPrefabName + "' exists, so " + def.DisplayName + " cannot be placed provisionally. Merchant placement will do nothing for this trader."));
				}
				return false;
			}
			Pose val = Recall(def, zoneId, campPosition);
			Object.Instantiate<GameObject>(prefab, val.position, val.rotation);
			return true;
		}

		private static void Remember(MerchantDef def, Vector2s zoneId, Vector3 position, Quaternion rotation)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (!_lastSeen.TryGetValue(def.LocationName, out var value))
			{
				value = new Dictionary<Vector2s, Pose>();
				_lastSeen[def.LocationName] = value;
			}
			value[zoneId] = new Pose(position, rotation);
		}

		private static Pose Recall(MerchantDef def, Vector2s zoneId, Vector3 campPosition)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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)
			if (_lastSeen.TryGetValue(def.LocationName, out var value) && value.TryGetValue(zoneId, out var value2))
			{
				return value2;
			}
			return new Pose(campPosition, Quaternion.identity);
		}

		internal static void CollectPlacedCamps(ZoneSystem zones, MerchantDef def, List<Vector3> into)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance in zones.m_locationInstances)
			{
				if (locationInstance.Value.m_placed && !(PrefabName(locationInstance.Value) != def.LocationName))
				{
					into.Add(locationInstance.Value.m_position);
				}
			}
		}

		private static string PrefabName(LocationInstance instance)
		{
			//IL_0000: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (instance.m_location == null)
			{
				return "";
			}
			if (!string.IsNullOrEmpty(instance.m_location.m_prefabName))
			{
				return instance.m_location.m_prefabName;
			}
			return instance.m_location.m_name ?? "";
		}
	}
	internal static class MysteryLocationCatalog
	{
		private static readonly string[][] Entries = new string[3][]
		{
			new string[2] { "PlaceofMystery1", "Mysterious Location 1" },
			new string[2] { "PlaceofMystery2", "Mysterious Location 2" },
			new string[2] { "PlaceofMystery3", "Mysterious Location 3" }
		};

		internal static string LabelFor(string locationName)
		{
			if (string.IsNullOrEmpty(locationName))
			{
				return "";
			}
			string[][] entries = Entries;
			foreach (string[] array in entries)
			{
				if (string.Equals(locationName, array[0], StringComparison.OrdinalIgnoreCase))
				{
					return array[1];
				}
			}
			return "";
		}
	}
	[HarmonyPatch(typeof(ObjectDB))]
	internal static class ObjectDbPatches
	{
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		[HarmonyPatch("Awake")]
		internal static void AwakePostfix(ObjectDB __instance)
		{
			try
			{
				CompassItem.EnsureRegistered(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("ObjectDB.Awake registration failed: " + ex));
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(800)]
		[HarmonyPatch("CopyOtherDB")]
		internal static void CopyOtherDbPostfix(ObjectDB __instance)
		{
			try
			{
				CompassItem.EnsureRegistered(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("ObjectDB.CopyOtherDB registration failed: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetSceneAwakePatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		internal static void Postfix(ZNetScene __instance)
		{
			try
			{
				CompassItem.EnsureRegistered(ObjectDB.instance);
				CompassItem.EnsureNetworkRegistered(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to register the compass with ZNetScene: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class GameStartPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(800)]
		internal static void Postfix()
		{
			try
			{
				CompassRpc.Register();
				CompassItem.EnsureRegistered(ObjectDB.instance);
				CompassItem.EnsureNetworkRegistered(ZNetScene.instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to initialise the compass on game start: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	internal static class ZNetShutdownPatch
	{
		[HarmonyPostfix]
		internal static void Postfix()
		{
			LootCooldownRegistry.Reset();
			CompassRpc.Reset();
			MerchantPlacement.Reset();
			CompassPan.Reset();
		}
	}
	[HarmonyPatch(typeof(Vegvisir), "Interact")]
	internal static class VegvisirInteractPatch
	{
		[HarmonyPrefix]
		internal static bool Prefix(Vegvisir __instance, Humanoid character, bool hold, ref bool __result)
		{
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			if (hold)
			{
				__result = false;
				return false;
			}
			Player val = (Player)(object)((character is Player) ? character : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				__result = false;
				return false;
			}
			ApplyStoneKeys(__instance, val);
			Inventory inventory = ((Humanoid)val).GetInventory();
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			foreach (VegvisrLocation location in __instance.m_locations)
			{
				if (!string.IsNullOrEmpty(location.m_locationName))
				{
					List<string> locationNames = new List<string> { location.m_locationName };
					if (CompassItem.FindCompassForAny(inventory, locationNames) != null)
					{
						list2.Add(location.m_locationName);
					}
					else
					{
						list.Add(CompassRpc.Encode(location.m_locationName, location.m_pinName));
					}
				}
			}
			if (list.Count == 0)
			{
				((Character)val).Message((MessageType)2, (list2.Count > 1) ? "You already carry every Vegvisir Compass from this stone" : "You already carry that Vegvisir Compass", 0, (Sprite)null, false);
				Plugin.Debug($"All {list2.Count} target(s) already carried; nothing requested.");
				__result = true;
				return false;
			}
			if (list2.Count > 0)
			{
				Plugin.Debug($"Skipping {list2.Count} target(s) already carried; requesting {list.Count}.");
			}
			CompassRpc.RequestCompass(((Component)__instance).transform.position, list, val);
			__result = true;
			return false;
		}

		private static void ApplyStoneKeys(Vegvisir stone, Player player)
		{
			try
			{
				if (!string.IsNullOrEmpty(stone.m_setsGlobalKey) && (Object)(object)ZoneSystem.instance != (Object)null)
				{
					ZoneSystem.instance.SetGlobalKey(stone.m_setsGlobalKey);
					Plugin.Debug("Stone set the global key '" + stone.m_setsGlobalKey + "'.");
				}
				if (!string.IsNullOrEmpty(stone.m_setsPlayerKey))
				{
					((Humanoid)player).AddUniqueKey(stone.m_setsPlayerKey);
					Plugin.Debug("Stone set the player key '" + stone.m_setsPlayerKey + "'.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not apply the stone's keys: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "RemoveUnplacedLocations")]
	internal static class RemoveUnplacedLocationsPatch
	{
		[HarmonyPrefix]
		internal static bool Prefix(ZoneLocation location)
		{
			try
			{
				if (!MerchantPlacement.IsActive)
				{
					return true;
				}
				MerchantDef merchantDef = MerchantLocator.ForLocation(SafePrefabName(location));
				if (merchantDef == null)
				{
					return true;
				}
				return MerchantPlacement.IsLocked(merchantDef);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("RemoveUnplacedLocations guard failed: " + ex.Message));
				return true;
			}
		}

		internal static string SafePrefabName(ZoneLocation location)
		{
			if (location == null)
			{
				return "";
			}
			if (!string.IsNullOrEmpty(location.m_prefabName))
			{
				return location.m_prefabName;
			}
			return location.m_name ?? "";
		}
	}
	[HarmonyPatch(typeof(StoreGui), "Show")]
	internal static class StoreGuiShowPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(Trader trader)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)trader == (Object)null || !MerchantPlacement.IsActive)
			{
				return;
			}
			try
			{
				MerchantDef merchantDef = MerchantCatalog.ResolveForTrader(trader);
				if (merchantDef != null && !MerchantPlacement.IsLocked(merchantDef))
				{
					CompassRpc.RequestLockIn(merchantDef, ((Component)trader).transform.position);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Merchant lock-in failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "Interact")]
	internal static class CraftingStationInteractLockPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(CraftingStation __instance, Humanoid user, bool repeat)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (repeat || (Object)(object)__instance == (Object)null || !__instance.m_upgrader || !MerchantPlacement.IsActive || (Object)(object)user == (Object)null || (Object)(object)user != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			try
			{
				MerchantDef merchantDef = MerchantCatalog.ResolveForUpgrader(__instance);
				if (merchantDef != null && !MerchantPlacement.IsLocked(merchantDef))
				{
					CompassRpc.RequestLockIn(merchantDef, ((Component)__instance).transform.position);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Forge of Potential lock-in failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "Update")]
	internal static class ZoneSystemUpdatePatch
	{
		[HarmonyPostfix]
		internal static void Postfix(ZoneSystem __instance)
		{
			try
			{
				MerchantPlacement.ServerTick(__instance);
				MerchantPlacement.SyncClientState(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Merchant placement tick failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(RuneStone), "Interact")]
	internal static class RuneStoneInteractPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(RuneStone __instance, Humanoid character, bool hold)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (hold)
			{
				return;
			}
			Player val = (Player)(object)((character is Player) ? character : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			try
			{
				Vector3 position = ((Component)__instance).transform.position;
				MerchantDef merchantDef = MerchantCatalog.ResolveForStone(position);
				if (merchantDef == null)
				{
					return;
				}
				if (!MerchantCatalog.IsPastGuidanceGate(merchantDef, position))
				{
					Plugin.Debug("Lorestone for " + merchantDef.DisplayName + " is inside its gate; lore only.");
					return;
				}
				List<string> locationNames = new List<string> { merchantDef.LocationName };
				if (CompassItem.FindCompassForAny(((Humanoid)val).GetInventory(), locationNames) != null)
				{
					((Character)val).Message((MessageType)2, "You already carry a Vegvisir Compass - " + merchantDef.DisplayName, 0, (Sprite)null, false);
					return;
				}
				CompassRpc.RequestCompass(position, new List<string> { CompassRpc.Encode(merchantDef.LocationName, merchantDef.DisplayName) }, val);
				Plugin.Debug("Lorestone guidance requested for " + merchantDef.DisplayName + ".");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Merchant lorestone handling failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "CreateItemTooltip")]
	internal static class InventoryTooltipNamePatch
	{
		[HarmonyPrefix]
		internal static bool Prefix(InventoryGrid __instance, ItemData item, UITooltip tooltip)
		{
			//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)
			if ((Object)(object)tooltip == (Object)null || !CompassItem.IsCompass(item))
			{
				return true;
			}
			try
			{
				tooltip.Set(CompassItem.GetDisplayName(item), item.GetTooltip(-1), __instance.m_tooltipAnchor, default(Vector2));
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not label the compass tooltip: " + ex.Message));
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "GetHoverName")]
	internal static class ItemDropHoverNamePatch
	{
		[HarmonyPostfix]
		internal static void Postfix(ItemDrop __instance, ref string __result)
		{
			if (__instance?.m_itemData == null || !CompassItem.IsCompass(__instance.m_itemData))
			{
				return;
			}
			try
			{
				__result = CompassItem.GetDisplayName(__instance.m_itemData);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not label the dropped compass: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "Pickup")]
	internal static class PickupPatch
	{
		private const float MessageInterval = 2f;

		private static float _lastMessageTime = -999f;

		[HarmonyPrefix]
		internal static bool Prefix(Humanoid __instance, GameObject go, ref bool __result)
		{
			try
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer || (Object)(object)go == (Object)null)
				{
					return true;
				}
				ItemDrop component = go.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					return true;
				}
				component.Load();
				if (!CompassItem.IsCompass(component.m_itemData))
				{
					return true;
				}
				string locationName = CompassItem.GetLocationName(component.m_itemData);
				if (string.IsNullOrEmpty(locationName))
				{
					return true;
				}
				if (CompassItem.FindCompassForAny(((Humanoid)val).GetInventory(), new List<string> { locationName }) == null)
				{
					return true;
				}
				if (Time.time - _lastMessageTime >= 2f)
				{
					_lastMessageTime = Time.time;
					string localizedBossName = CompassItem.GetLocalizedBossName(component.m_itemData);
					((Character)val).Message((MessageType)2, string.IsNullOrEmpty(localizedBossName) ? "You already carry that Vegvisir Compass" : ("You already carry a Vegvisir Compass - " + localizedBossName), 0, (Sprite)null, false);
				}
				Plugin.Debug("Refused pickup: already carrying a compass for " + locationName + ".");
				__result = false;
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Compass pickup guard failed: " + ex.Message));
				return true;
			}
		}
	}
	internal static class CompassPan
	{
		private const float MaxLockoutSeconds = 6f;

		private const float LockoutGrace = 0.25f;

		private static bool _active;

		private static float _startPitch;

		private static float _expiresAt;

		internal static bool LookLocked
		{
			get
			{
				if (_active)
				{
					return Time.time < _expiresAt;
				}
				return false;
			}
		}

		internal static void Begin(Player player, float seconds)
		{
			_active = true;
			_startPitch = player.m_lookPitch;
			_expiresAt = Time.time + Mathf.Min(seconds, 6f) + 0.25f;
			Plugin.Debug($"Panning the camera over {seconds:0.#}s; look input held until then.");
		}

		internal static void Reset()
		{
			_active = false;
			_expiresAt = 0f;
		}

		internal static void Advance(Player player)
		{
			if (_active)
			{
				float lookTransitionTimeTotal = ((Character)player).m_lookTransitionTimeTotal;
				float lookTransitionTime = ((Character)player).m_lookTransitionTime;
				if (lookTransitionTime <= 0f || lookTransitionTimeTotal <= 0f || Time.time >= _expiresAt)
				{
					player.m_lookPitch = 0f;
					Plugin.Debug($"Pan finished; pitch {_startPitch:0.#} -> 0.");
					Reset();
				}
				else
				{
					player.m_lookPitch = Mathf.Lerp(0f, _startPitch, Mathf.SmoothStep(0f, 1f, lookTransitionTime / lookTransitionTimeTotal));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "UpdateLookTransition")]
	internal static class LookTransitionPatch
	{
		[HarmonyPostfix]
		internal static void Postfix(Character __instance)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			try
			{
				CompassPan.Advance(val);
			}
			catch (Exception ex)
			{
				CompassPan.Reset();
				Plugin.Log.LogWarning((object)("Compass pan failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetMouseLook")]
	internal static class MouseLookPatch
	{
		[HarmonyPrefix]
		internal static bool Prefix(Player __instance)
		{
			if (!CompassPan.LookLocked)
			{
				return true;
			}
			return (Object)(object)__instance != (Object)(object)Player.m_localPlayer;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UseItem")]
	internal static class UseItemPatch
	{
		private const float SpamGuardSeconds = 1f;

		private static float _lastUseTime = -999f;

		[HarmonyPrefix]
		internal static bool Prefix(Humanoid __instance, Inventory inventory, ItemData item)
		{
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			if (!CompassItem.IsCompass(item))
			{
				return true;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (Time.time - _lastUseTime < 1f)
			{
				Plugin.Debug("Compass use ignored - within the spam guard window.");
				return false;
			}
			_lastUseTime = Time.time;
			if (!CompassItem.TryGetTarget(item, out var target))
			{
				((Character)val).Message((MessageType)2, "The runes are blank", 0, (Sprite)null, false);
				Plugin.Log.LogWarning((object)"A compass had no usable target stored.");
				return false;
			}
			float range = CompassItem.GetRange(item);
			if (range > 0f && CompassItem.TryGetOrigin(item, out var origin))
			{
				float num = CompassItem.HorizontalDistance(origin, ((Component)val).transform.position);
				if (num > range)
				{
					((Character)val).Message((MessageType)2, "Nothing happens, you are too far from the vegvisir", 0, (Sprite)null, false);
					Plugin.Debug($"Compass out of range: {num:0}m from its stone, limit {range:0}m.");
					return false;
				}
				Plugin.Debug($"Compass in range: {num:0}m from its stone, limit {range:0}m.");
			}
			Vector3 val2 = target - ((Component)val).transform.position;
			val2.y = 0f;
			float num2 = Mathf.Max(0f, Plugin.LookSmoothing.Value);
			if (((Vector3)(ref val2)).sqrMagnitude > 0.001f)
			{
				if (num2 > 0f)
				{
					CompassPan.Begin(val, num2);
				}
				((Character)val).SetLookDir(((Vector3)(ref val2)).normalized, num2);
				Plugin.Debug($"Aimed at {target}, {((Vector3)(ref val2)).magnitude:0}m away on the horizontal.");
			}
			else
			{
				Plugin.Debug("Target is directly above or below; skipping the camera swing.");
			}
			if (num2 <= 0f)
			{
				float lookPitch = val.m_lookPitch;
				val.m_lookPitch = 0f;
				Plugin.Debug($"Levelled the view, pitch {lookPitch:0.#} -> 0.");
			}
			string text = Localization.instance.Localize(CompassItem.GetBossName(item));
			Inventory val3 = inventory ?? ((Humanoid)val).GetInventory();
			item.m_durability -= 1f;
			if (item.m_durability <= 0f)
			{
				if (val3 != null)
				{
					val3.RemoveItem(item);
				}
				((Character)val).Message((MessageType)2, string.IsNullOrEmpty(text) ? "The compass crumbles to dust" : (text + " - the compass crumbles to dust"), 0, (Sprite)null, false);
				Plugin.Debug("Compass spent on " + text + "; item destroyed.");
			}
			else
			{
				int num3 = Mathf.Max(0, Mathf.RoundToInt(item.m_durability));
				((Character)val).Message((MessageType)2, string.IsNullOrEmpty(text) ? $"{num3} use(s) remaining" : $"{text} - {num3} use(s) remaining", 0, (Sprite)null, false);
				Plugin.Debug($"Compass used on {text}; {num3} use(s) remaining.");
			}
			return false;
		}
	}
	[BepInPlugin("com.dhobbs.vegvisircompass", "Vegvisir Compass", "1.7.1")]
	public class Plugin : BaseUnityPlugin
	{
		private const string ConfigFileName = "vegvisircompass.cfg";

		private Harmony _harmony;

		private ConfigFile _config;

		internal static ConfigEntry<float> LootCooldownSeconds;

		internal static ConfigEntry<int> UsesPerCompass;

		internal static ConfigEntry<float> RangeMeters;

		internal static ConfigEntry<float> LookSmoothing;

		internal static ConfigEntry<bool> VerboseLogging;

		internal static ManualLogSource Log { get; private set; }

		internal static Plugin Instance { get; private set; }

		private ConfigFile OpenConfigFile()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, "vegvisircompass.cfg");
			string text2 = Path.Combine(Paths.ConfigPath, "com.dhobbs.vegvisircompass.cfg");
			try
			{
				if (!File.Exists(text) && File.Exists(text2))
				{
					File.Move(text2, text);
					Log.LogInfo((object)"Renamed com.dhobbs.vegvisircompass.cfg to vegvisircompass.cfg; your settings were kept.");
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not carry com.dhobbs.vegvisircompass.cfg over to vegvisircompass.cfg: " + ex.Message));
			}
			return new ConfigFile(text, true, ((BaseUnityPlugin)this).Info.Metadata);
		}

		private void Awake()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			_config = OpenConfigFile();
			LootCooldownSeconds = _config.Bind<float>("Vegvisir", "LootCooldownSeconds", 0f, new ConfigDescription("Seconds after a Vegvisir is looted before ANY player can loot it again. Enforced by the server. Zero disables the cooldown entirely, and the server then tracks nothing at all.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 86400f), Array.Empty<object>()));
			UsesPerCompass = _config.Bind<int>("Compass", "UsesPerCompass", 1, new ConfigDescription("How many times a compass can be used before it is destroyed.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			RangeMeters = _config.Bind<float>("Compass", "RangeMeters", 350f, new ConfigDescription("How far from the Vegvisir it was looted from a compass still works, measured on the X/Z plane so height is ignored. Zero removes the limit. Server-authoritative: the value is baked into each compass when it is granted.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100000f), Array.Empty<object>()));
			LookSmoothing = _config.Bind<float>("Compass", "LookSmoothing", 3.5f, new ConfigDescription("Seconds the camera takes to pan towards the target, matching the vanilla Vegvisir. Mouse look is held off for the duration of the pan: vanilla applies an eased turn and mouse input to the same field, so without that the pan is cancelled the moment the mouse moves. The lockout is capped at 6 seconds however high this is set, and expires on a deadline so an interrupted pan can never leave you without camera control. Zero turns instantly instead, with no lockout.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			VerboseLogging = _config.Bind<bool>("Debug", "VerboseLogging", false, "Log detailed information about looting, RPCs and compass use.");
			_harmony = new Harmony("com.dhobbs.vegvisircompass");
			int num = PatchIsolation.PatchAllIsolated(_harmony, typeof(Plugin).Assembly, Log);
			Log.LogInfo((object)("Vegvisir Compass 1.7.1 loaded." + ((num > 0) ? $" {num} patch class(es) skipped - see the errors above." : string.Empty)));
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"Vegvisir Compass unloaded.");
			}
		}

		internal static void Debug(string message)
		{
			if (VerboseLogging != null && VerboseLogging.Value)
			{
				Log.LogInfo((object)message);
			}
		}
	}
	internal static class ModInfo
	{
		internal const string Guid = "com.dhobbs.vegvisircompass";

		internal const string Name = "Vegvisir Compass";

		internal const string Version = "1.7.1";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}