Decompiled source of OdinsPins v0.1.0

OdinsPins.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("OdinsPins")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ce1fe04c1e0d3a2923c660dc5676ceccf87de5a5")]
[assembly: AssemblyProduct("OdinsPins")]
[assembly: AssemblyTitle("OdinsPins")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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;
		}
	}
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace OdinsMissingPatch
{
	[BepInPlugin("rauschekuh.odinspins", "Odin's Pins", "0.1.0")]
	public class OdinsPinsPlugin : BaseUnityPlugin
	{
		public const string GUID = "rauschekuh.odinspins";

		public const string NAME = "Odin's Pins";

		public const string VERSION = "0.1.0";

		private static readonly Tweak[] Tweaks = new Tweak[4]
		{
			SharedMapTable.Instance,
			AutoPins.Instance,
			PinLooks.Instance,
			DeathPins.Instance
		};

		private void Awake()
		{
			TweakHost.Start((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config, "rauschekuh.odinspins", Tweaks);
		}
	}
	internal static class PinBroadcast
	{
		[HarmonyPatch(typeof(Game), "Start")]
		[Serves(new Type[] { typeof(AutoPins) }, Optional = true)]
		[LoadHook]
		private static class Register
		{
			private static void Postfix()
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null && instance != registeredOn)
				{
					registeredOn = instance;
					instance.Register<ZPackage>("OdinsMissingPatch_Pins", (Action<long, ZPackage>)RPC_Pins);
					instance.Register("OdinsMissingPatch_PinsRequest", (Action<long>)RPC_Request);
				}
			}
		}

		private const string PinsRpc = "OdinsMissingPatch_Pins";

		private const string RequestRpc = "OdinsMissingPatch_PinsRequest";

		private const int Version = 1;

		private static ZRoutedRpc registeredOn;

		private static ZRoutedRpc requestedOn;

		private static bool Sharing => AutoPins.Instance.Sharing;

		internal static void Send(UniversalPins.Category category, Vector3 pos, string name)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && Sharing)
			{
				ZPackage val = new ZPackage();
				val.Write(1);
				val.Write(true);
				val.Write(1);
				WritePin(val, category, name, pos);
				instance.InvokeRoutedRPC(0L, "OdinsMissingPatch_Pins", new object[1] { val });
			}
		}

		internal static void RequestOnce()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != requestedOn && Sharing && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				requestedOn = instance;
				instance.InvokeRoutedRPC(0L, "OdinsMissingPatch_PinsRequest", Array.Empty<object>());
			}
		}

		private static void WritePin(ZPackage package, UniversalPins.Category category, string name, Vector3 pos)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			package.Write((int)category);
			package.Write(name ?? "");
			package.Write(pos);
		}

		private static bool FromSelf(long sender)
		{
			if (ZRoutedRpc.instance != null)
			{
				return sender == ZRoutedRpc.instance.m_id;
			}
			return true;
		}

		private static void RPC_Pins(long sender, ZPackage package)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if (FromSelf(sender) || !Sharing || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Minimap.instance == (Object)null || package == null || package.ReadInt() != 1)
			{
				return;
			}
			bool announce = package.ReadBool();
			int num = package.ReadInt();
			for (int i = 0; i < num; i++)
			{
				int num2 = package.ReadInt();
				string name = package.ReadString();
				Vector3 pos = package.ReadVector3();
				if (num2 >= 0 && num2 < UniversalPins.CategoryCount)
				{
					AutoPins.Instance.Receive((UniversalPins.Category)num2, pos, name, announce);
				}
			}
		}

		private static void RPC_Request(long sender)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			if (FromSelf(sender) || !Sharing || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Minimap.instance == (Object)null)
			{
				return;
			}
			List<PinData> list = new List<PinData>();
			foreach (PinData pin in Minimap.instance.m_pins)
			{
				if (pin.m_save && UniversalPins.TryGetCategory(pin, out var _))
				{
					list.Add(pin);
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(false);
			val.Write(list.Count);
			foreach (PinData item in list)
			{
				UniversalPins.TryGetCategory(item, out var category2);
				WritePin(val, category2, item.m_name, item.m_pos);
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "OdinsMissingPatch_Pins", new object[1] { val });
		}
	}
	internal sealed class PinSweep
	{
		private readonly HashSet<PinData> missing = new HashSet<PinData>();

		internal void Run(Minimap map, Vector3 origin, float range, Func<PinData, bool> watched, Func<Vector3, bool> present, Action<PinData> gone)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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)
			if ((Object)(object)map == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			for (int num = map.m_pins.Count - 1; num >= 0; num--)
			{
				PinData val = map.m_pins[num];
				if (watched(val))
				{
					Vector3 val2 = val.m_pos - origin;
					if (!(((Vector3)(ref val2)).sqrMagnitude > range * range))
					{
						if (!ZNetScene.instance.IsAreaReady(val.m_pos) || present(val.m_pos))
						{
							missing.Remove(val);
						}
						else if (!missing.Add(val))
						{
							missing.Remove(val);
							gone(val);
						}
					}
				}
			}
		}
	}
	internal sealed class AutoPins : Tweak
	{
		private sealed class Site
		{
			public string PrefabName;

			public bool GameIcon;

			public Teleport Entrance;

			public PlaceKind Kind;

			public string KindName;
		}

		internal enum PlaceKind
		{
			None,
			Treasure,
			Generated,
			Tar
		}

		internal enum MinedOut
		{
			Tick,
			Remove,
			Keep
		}

		[HarmonyPatch(typeof(Player), "Update")]
		private static class Sweep
		{
			private static void Postfix(Player __instance)
			{
				//IL_0047: 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_0056: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
				if (!Instance.On || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || Time.time < nextSweep)
				{
					return;
				}
				nextSweep = Time.time + 3f;
				PinBroadcast.RequestOnce();
				Minimap instance = Minimap.instance;
				Vector3 position = ((Component)__instance).transform.position;
				if (!((Object)(object)instance == (Object)null) && !Character.InInterior(position))
				{
					if (Instance.dungeons.Value || Instance.places.Value)
					{
						Instance.SweepLocations(position);
					}
					if (Instance.minedOut.Value != MinedOut.Keep)
					{
						Instance.SweepMinedOut(instance, position);
					}
				}
			}
		}

		[HarmonyPatch(typeof(MineRock5), "Damage")]
		private static class StrikeMineRock5
		{
			private static void Postfix(MineRock5 __instance, HitData hit)
			{
				Strike((Component)(object)__instance, __instance.m_nview, hit);
			}
		}

		[HarmonyPatch(typeof(MineRock), "Damage")]
		private static class StrikeMineRock
		{
			private static void Postfix(MineRock __instance, HitData hit)
			{
				Strike((Component)(object)__instance, __instance.m_nview, hit);
			}
		}

		[HarmonyPatch(typeof(Destructible), "Damage")]
		private static class StrikeDestructible
		{
			private static void Postfix(Destructible __instance, HitData hit)
			{
				Strike((Component)(object)__instance, __instance.m_nview, hit);
			}
		}

		internal static readonly AutoPins Instance = new AutoPins();

		private const float SweepInterval = 3f;

		private static readonly Dictionary<string, string> DefaultPlaces = new Dictionary<string, string>
		{
			{ "Mistlands_Excavation1", "$omp_place_excavation" },
			{ "Mistlands_Excavation2", "$omp_place_excavation" },
			{ "Mistlands_Excavation3", "$omp_place_excavation" },
			{ "Mistlands_GuardTower1_new", "$omp_place_guardtower" },
			{ "Mistlands_GuardTower2_new", "$omp_place_guardtower" },
			{ "Mistlands_GuardTower3_new", "$omp_place_guardtower" },
			{ "Mistlands_GuardTower1_ruined_new", "$omp_place_guardtower" },
			{ "Mistlands_GuardTower1_ruined_new2", "$omp_place_guardtower" },
			{ "Mistlands_GuardTower3_ruined_new", "$omp_place_guardtower" },
			{ "Mistlands_Lighthouse1_new", "$omp_place_lighthouse" },
			{ "FireHole", "$omp_place_firehole" }
		};

		private static readonly PinType[] AllowedIcons;

		private ConfigEntry<bool> dungeons;

		private ConfigEntry<bool> ore;

		private ConfigEntry<bool> places;

		private ConfigEntry<float> discoverRange;

		private ConfigEntry<float> pinSpacing;

		private ConfigEntry<PinType> dungeonIcon;

		private ConfigEntry<PinType> oreIcon;

		private ConfigEntry<PinType> placeIcon;

		private ConfigEntry<string> extraOre;

		private ConfigEntry<string> skipOre;

		private ConfigEntry<bool> showMessage;

		private ConfigEntry<MinedOut> minedOut;

		private ConfigEntry<bool> share;

		private Dictionary<string, string> placeNames = new Dictionary<string, string>();

		private static readonly ConditionalWeakTable<Location, Site> Sites;

		private static Dictionary<string, string> oreMetals;

		private static readonly Dictionary<string, string> RockLabels;

		private const float MinedOutCheckRange = 32f;

		private const float DepositRadius = 12f;

		private static readonly PinSweep DepositSweep;

		private static readonly HashSet<PinData> Ticked;

		private static readonly Collider[] Hits;

		private static float nextSweep;

		internal override string Section => "Auto Pins";

		protected override string Summary => "Dungeon entrances, ore deposits you strike and places get a map pin by themselves when you get there. The pins belong to nobody: map tables share them once, the large map's shared-map button hides them, right click removes one for good.";

		internal bool Sharing
		{
			get
			{
				if (base.On)
				{
					return share.Value;
				}
				return false;
			}
		}

		private AutoPins()
		{
		}

		protected override void Bind(ConfigFile config)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			dungeons = config.Bind<bool>(Section, "Dungeons", true, "Pin dungeon and cave entrances, named the way the game names them when you enter.");
			ore = config.Bind<bool>(Section, "Ore", true, "Pin an ore deposit when you strike it: any rock that drops something a smelter or blast furnace takes (copper, muddy scrap piles, silver, giant armour, flametal, ...), plus what ExtraOre adds and minus what SkipOre leaves out.");
			places = config.Bind<bool>(Section, "Places", true, "Pin places without an interior: camps and villages the game generates (fuling villages, the Ashlands ruins, ...), tar pits, places holding an item that cannot be teleported (dragon eggs), and the places in PlaceList. Ruins holding a vegvisir are left out: reading the stone pins the boss already.");
			discoverRange = config.Bind<float>(Section, "DiscoverRange", 40f, new ConfigDescription("Metres you have to come within for a dungeon or place to be pinned - you have to have been there, not merely seen it from a hill. 0 pins whatever is loaded around you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			pinSpacing = config.Bind<float>(Section, "PinSpacing", 10f, new ConfigDescription("No pin is added within this many metres of a pin already on your map - one you placed by hand, one another player shared, one another mod made - so a place marked once is not marked twice. Death pins do not count. 0 turns this off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			dungeonIcon = config.Bind<PinType>(Section, "DungeonIcon", (PinType)1, "Map icon of a dungeon pin. Icon0 is the fire, Icon1 the house, Icon2 the hammer, Icon3 the orb, Icon4 the portal; each category having its own icon is what lets the legend hide one.");
			oreIcon = config.Bind<PinType>(Section, "OreIcon", (PinType)3, "Map icon of an ore pin. Icon0 is the fire, Icon1 the house, Icon2 the hammer, Icon3 the orb, Icon4 the portal; each category having its own icon is what lets the legend hide one.");
			placeIcon = config.Bind<PinType>(Section, "PlaceIcon", (PinType)0, "Map icon of a place pin. Icon0 is the fire, Icon1 the house, Icon2 the hammer, Icon3 the orb, Icon4 the portal; each category having its own icon is what lets the legend hide one.");
			BindList(config, "PlaceList", string.Join(", ", DefaultPlaces.Keys), "Comma separated location names to pin besides the ones found by themselves. A name may be followed by =$token to name the pin by a translation token, which also renames a place found by itself; a name the mod does not know is pinned under the game's own label for the place, or the location name when it has none. The dev build's omp_locations console command lists every location of the current game.", ParsePlaces);
			extraOre = BindList(config, "ExtraOre", "Softtissue", "Comma separated item names that count as ore besides what a smelter takes, the pin named after the item. Soft tissue, which the eitr refinery burns, comes from the giant remains of the Mistlands. Obsidian or BlackMarble could go here too.", delegate
			{
				ForgetOre();
			});
			skipOre = BindList(config, "SkipOre", "TinOre", "Comma separated item names that do not count as ore although a smelter takes them. Tin is left out by default: its deposits line every Black Forest shore and would carpet the map.", delegate
			{
				ForgetOre();
			});
			minedOut = config.Bind<MinedOut>(Section, "MinedOut", MinedOut.Tick, "What happens to an ore pin once its deposit is mined out, by you or anyone, noticed when you come by: Tick crosses it off the way a click does, Remove takes it off the map for good, Keep leaves it as it was.");
			showMessage = config.Bind<bool>(Section, "ShowMessage", true, "Say so in the top left corner when a pin is added, the way the game does for a boss altar.");
			share = config.Bind<bool>(Section, "Share", true, "Send every pin added here to every player online the moment it is made, and take the pins they send, without a map table in between; on joining, ask everyone for theirs once. Only players with the mod take part, and each applies their own settings to what they get. Off, the pins travel on map tables only.");
		}

		private void ParsePlaces(List<string> entries)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (string entry in entries)
			{
				string text = entry;
				string value = null;
				int num = entry.IndexOf('=');
				if (num >= 0)
				{
					text = entry.Substring(0, num).Trim();
					value = entry.Substring(num + 1).Trim();
				}
				if (text.Length == 0)
				{
					TweakHost.Log.LogWarning((object)(Section + ": '" + entry + "' has no location name and is ignored"));
					continue;
				}
				if (string.IsNullOrEmpty(value))
				{
					DefaultPlaces.TryGetValue(text, out value);
				}
				dictionary[text] = value;
			}
			placeNames = dictionary;
		}

		private static PinType Icon(ConfigEntry<PinType> entry)
		{
			//IL_0006: 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_0019: Unknown result type (might be due to invalid IL or missing references)
			if (Array.IndexOf(AllowedIcons, entry.Value) < 0)
			{
				return (PinType)((ConfigEntryBase)entry).DefaultValue;
			}
			return entry.Value;
		}

		private bool Wants(UniversalPins.Category category, out PinType type)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected I4, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected I4, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected I4, but got Unknown
			switch (category)
			{
			case UniversalPins.Category.Dungeon:
				type = (PinType)(int)Icon(dungeonIcon);
				return dungeons.Value;
			case UniversalPins.Category.Ore:
				type = (PinType)(int)Icon(oreIcon);
				return ore.Value;
			case UniversalPins.Category.Place:
				type = (PinType)(int)Icon(placeIcon);
				return places.Value;
			default:
				type = (PinType)6;
				return false;
			}
		}

		private bool InRange(Vector3 origin, Vector3 pos)
		{
			//IL_0014: 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_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)
			float value = discoverRange.Value;
			if (!(value <= 0f))
			{
				Vector3 val = pos - origin;
				return ((Vector3)(ref val)).sqrMagnitude <= value * value;
			}
			return true;
		}

		private bool NearOtherPin(Minimap map, Vector3 pos)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			float value = pinSpacing.Value;
			if (value <= 0f)
			{
				return false;
			}
			foreach (PinData pin in map.m_pins)
			{
				if (pin.m_save && (int)pin.m_type != 4 && !UniversalPins.IsUniversal(pin) && Utils.DistanceXZ(pos, pin.m_pos) < value)
				{
					return true;
				}
			}
			return false;
		}

		private void TryPin(UniversalPins.Category category, Vector3 pos, string name, PinType type)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: 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)
			if (Place(category, pos, name, type, showMessage.Value))
			{
				PinBroadcast.Send(category, pos, name);
			}
		}

		internal void Receive(UniversalPins.Category category, Vector3 pos, string name, bool announce)
		{
			//IL_000d: 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)
			if (Wants(category, out var type))
			{
				Place(category, pos, name, type, announce && showMessage.Value);
			}
		}

		private bool Place(UniversalPins.Category category, Vector3 pos, string name, PinType type, bool announce)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_003e: 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)
			Minimap instance = Minimap.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || UniversalPins.Find(instance, pos, category) != null || UniversalPins.IsDismissed(category, pos) || NearOtherPin(instance, pos))
			{
				return false;
			}
			PinData val = UniversalPins.Add(instance, pos, category, type, name);
			if (announce)
			{
				Sprite val2 = PinLooks.IconOf(val);
				((Character)localPlayer).Message((MessageType)1, string.IsNullOrEmpty(name) ? "$msg_pin_added" : ("$msg_pin_added: " + name), 0, ((Object)(object)val2 != (Object)null) ? val2 : val.m_icon, false);
			}
			return true;
		}

		private static Site Describe(Location location)
		{
			if (Sites.TryGetValue(location, out var value))
			{
				if (location.m_hasInterior && (Object)(object)value.Entrance == (Object)null)
				{
					value.Entrance = FindEntrance(location);
				}
				return value;
			}
			value = new Site
			{
				PrefabName = Utils.GetPrefabName(((Component)location).gameObject)
			};
			LocationProxy componentInParent = ((Component)location).GetComponentInParent<LocationProxy>();
			ZDO val = (((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.m_nview != (Object)null) ? componentInParent.m_nview.GetZDO() : null);
			int num = ((val != null) ? val.GetInt(ZDOVars.s_location, 0) : 0);
			ZoneLocation val2 = ((num != 0 && (Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.GetLocation(num) : null);
			if (val2 != null)
			{
				value.PrefabName = val2.m_prefabName;
				value.GameIcon = val2.m_iconAlways || val2.m_iconPlaced;
			}
			if (location.m_hasInterior)
			{
				value.Entrance = FindEntrance(location);
			}
			else
			{
				Classify(location, value);
			}
			Sites.Add(location, value);
			return value;
		}

		private static void Classify(Location location, Site site)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Invalid comparison between Unknown and I4
			if ((Object)(object)((Component)location).GetComponentInChildren<Vegvisir>(true) != (Object)null)
			{
				return;
			}
			Pickable[] componentsInChildren = ((Component)location).GetComponentsInChildren<Pickable>(true);
			foreach (Pickable val in componentsInChildren)
			{
				ItemDrop val2 = (((Object)(object)val.m_itemPrefab != (Object)null) ? val.m_itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 != (Object)null && !val2.m_itemData.m_shared.m_teleportable)
				{
					site.Kind = PlaceKind.Treasure;
					site.KindName = val2.m_itemData.m_shared.m_name;
					return;
				}
			}
			DungeonGenerator val3 = (((Object)(object)location.m_generator != (Object)null) ? location.m_generator : ((Component)location).GetComponentInChildren<DungeonGenerator>(true));
			if ((Object)(object)val3 != (Object)null)
			{
				site.Kind = PlaceKind.Generated;
				site.KindName = ThemeName(val3.m_themes);
				return;
			}
			LiquidVolume[] componentsInChildren2 = ((Component)location).GetComponentsInChildren<LiquidVolume>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				if ((int)componentsInChildren2[i].m_liquidType == 1)
				{
					site.Kind = PlaceKind.Tar;
					site.KindName = "$omp_place_tarpit";
					break;
				}
			}
		}

		internal static string RuleFor(Location location)
		{
			Site site = new Site();
			if (!location.m_hasInterior)
			{
				Classify(location, site);
			}
			if (site.Kind != PlaceKind.None)
			{
				return site.Kind.ToString() + " " + site.KindName;
			}
			return "";
		}

		private static string ThemeName(Theme theme)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: 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_0012: 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_001e: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			if ((theme & 0x10) != 0)
			{
				return "$omp_place_fulingvillage";
			}
			if ((theme & 0x10020) != 0)
			{
				return "$omp_place_village";
			}
			if ((theme & 0x40) != 0)
			{
				return "$omp_place_farm";
			}
			if ((theme & 0x2000) != 0)
			{
				return "$omp_place_fortress";
			}
			return "$omp_place_ruins";
		}

		private string PlaceName(Location location, Site site)
		{
			string text = ((!string.IsNullOrEmpty(location.m_discoverLabel)) ? location.m_discoverLabel : null);
			if (placeNames.TryGetValue(site.PrefabName, out var value))
			{
				string text2;
				if (string.IsNullOrEmpty(value))
				{
					text2 = text;
					if (text2 == null)
					{
						text2 = site.KindName;
						if (text2 == null)
						{
							return site.PrefabName;
						}
					}
				}
				else
				{
					text2 = value;
				}
				return text2;
			}
			if (site.Kind == PlaceKind.None)
			{
				return null;
			}
			return text ?? site.KindName;
		}

		private static Teleport FindEntrance(Location location)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Teleport[] componentsInChildren = ((Component)location).GetComponentsInChildren<Teleport>();
			foreach (Teleport val in componentsInChildren)
			{
				if (!Character.InInterior(((Component)val).transform.position))
				{
					return val;
				}
			}
			return null;
		}

		private void SweepLocations(Vector3 origin)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_007c: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			foreach (Location s_allLocation in Location.s_allLocations)
			{
				if ((Object)(object)s_allLocation == (Object)null)
				{
					continue;
				}
				Site site = Describe(s_allLocation);
				if (site.GameIcon)
				{
					continue;
				}
				if (s_allLocation.m_hasInterior)
				{
					if (dungeons.Value)
					{
						Vector3 pos = (((Object)(object)site.Entrance != (Object)null) ? ((Component)site.Entrance).transform.position : ((Component)s_allLocation).transform.position);
						if (InRange(origin, pos))
						{
							TryPin(UniversalPins.Category.Dungeon, pos, DungeonName(s_allLocation, site), Icon(dungeonIcon));
						}
					}
				}
				else if (places.Value && InRange(origin, ((Component)s_allLocation).transform.position))
				{
					string text = PlaceName(s_allLocation, site);
					if (text != null)
					{
						TryPin(UniversalPins.Category.Place, ((Component)s_allLocation).transform.position, text, Icon(placeIcon));
					}
				}
			}
		}

		private static string DungeonName(Location location, Site site)
		{
			if ((Object)(object)site.Entrance != (Object)null && !string.IsNullOrEmpty(site.Entrance.m_enterText))
			{
				return site.Entrance.m_enterText;
			}
			if (string.IsNullOrEmpty(location.m_discoverLabel))
			{
				return "$omp_dungeon";
			}
			return location.m_discoverLabel;
		}

		private static Dictionary<string, string> OreMetals()
		{
			if (oreMetals != null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return oreMetals;
			}
			oreMetals = new Dictionary<string, string>();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				Smelter val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Smelter>() : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val.m_fuelItem == (Object)null)
				{
					continue;
				}
				foreach (ItemConversion item in val.m_conversion)
				{
					if ((Object)(object)item.m_from != (Object)null && !oreMetals.ContainsKey(((Object)((Component)item.m_from).gameObject).name))
					{
						ItemDrop val2 = (((Object)(object)item.m_to != (Object)null) ? item.m_to : item.m_from);
						oreMetals[((Object)((Component)item.m_from).gameObject).name] = val2.m_itemData.m_shared.m_name;
					}
				}
			}
			foreach (string item2 in Tweak.Items(Instance.extraOre.Value))
			{
				GameObject val3 = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(item2) : null);
				ItemDrop val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val4 != (Object)null)
				{
					oreMetals[item2] = val4.m_itemData.m_shared.m_name;
				}
				else
				{
					TweakHost.Log.LogWarning((object)(Instance.Section + ": ExtraOre names '" + item2 + "', which is no item"));
				}
			}
			foreach (string item3 in Tweak.Items(Instance.skipOre.Value))
			{
				oreMetals.Remove(item3);
			}
			return oreMetals;
		}

		private static void ForgetOre()
		{
			oreMetals = null;
			RockLabels.Clear();
		}

		private static string OreLabel(Component rock)
		{
			string prefabName = Utils.GetPrefabName(rock.gameObject);
			if (RockLabels.TryGetValue(prefabName, out var value))
			{
				return value;
			}
			Dictionary<string, string> dictionary = OreMetals();
			if (dictionary == null)
			{
				return "";
			}
			value = "";
			MineRock5 val = (MineRock5)(object)((rock is MineRock5) ? rock : null);
			if (val == null)
			{
				MineRock val2 = (MineRock)(object)((rock is MineRock) ? rock : null);
				if (val2 == null)
				{
					Destructible val3 = (Destructible)(object)((rock is Destructible) ? rock : null);
					if (val3 != null && OnlyPickaxe(val3))
					{
						DropOnDestroyed component = ((Component)val3).GetComponent<DropOnDestroyed>();
						value = (((Object)(object)component != (Object)null) ? DropLabel(component.m_dropWhenDestroyed, dictionary) : "");
						GameObject spawnWhenDestroyed = val3.m_spawnWhenDestroyed;
						if (value.Length == 0 && (Object)(object)spawnWhenDestroyed != (Object)null)
						{
							MineRock5 component2 = spawnWhenDestroyed.GetComponent<MineRock5>();
							MineRock component3 = spawnWhenDestroyed.GetComponent<MineRock>();
							value = (((Object)(object)component2 != (Object)null) ? DropLabel(component2.m_dropItems, dictionary) : (((Object)(object)component3 != (Object)null) ? DropLabel(component3.m_dropItems, dictionary) : ""));
						}
					}
				}
				else
				{
					value = DropLabel(val2.m_dropItems, dictionary);
				}
			}
			else
			{
				value = DropLabel(val.m_dropItems, dictionary);
			}
			RockLabels[prefabName] = value;
			return value;
		}

		private static bool OnlyPickaxe(Destructible destructible)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0019: 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_0020: Invalid comparison between Unknown and I4
			DamageModifiers damages = destructible.m_damages;
			if ((int)damages.m_chop == 3 && (int)damages.m_blunt == 3)
			{
				return (int)damages.m_pickaxe != 3;
			}
			return false;
		}

		private void SweepMinedOut(Minimap map, Vector3 origin)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			bool remove = minedOut.Value == MinedOut.Remove;
			DepositSweep.Run(map, origin, 32f, (PinData pin) => UniversalPins.TryGetCategory(pin, out var category) && category == UniversalPins.Category.Ore && (remove || (!pin.m_checked && !Ticked.Contains(pin))), DepositAt, delegate(PinData pin)
			{
				if (remove)
				{
					UniversalPins.Discard(map, pin, UniversalPins.Category.Ore);
				}
				else
				{
					pin.m_checked = true;
					Ticked.Add(pin);
				}
			});
		}

		private static bool DepositAt(Vector3 pos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			int num = Physics.OverlapSphereNonAlloc(pos, 12f, Hits, -1, (QueryTriggerInteraction)2);
			for (int i = 0; i < num; i++)
			{
				Collider val = Hits[i];
				Component componentInParent = (Component)(object)((Component)val).GetComponentInParent<MineRock5>();
				if ((Object)(object)componentInParent == (Object)null)
				{
					componentInParent = (Component)(object)((Component)val).GetComponentInParent<MineRock>();
				}
				if ((Object)(object)componentInParent == (Object)null)
				{
					componentInParent = (Component)(object)((Component)val).GetComponentInParent<Destructible>();
				}
				if ((Object)(object)componentInParent != (Object)null && OreLabel(componentInParent).Length > 0)
				{
					return true;
				}
			}
			return num == Hits.Length;
		}

		private static string DropLabel(DropTable table, Dictionary<string, string> metals)
		{
			//IL_0019: 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_001f: 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)
			if (table == null)
			{
				return "";
			}
			foreach (DropData drop in table.m_drops)
			{
				if ((Object)(object)drop.m_item != (Object)null && metals.TryGetValue(((Object)drop.m_item).name, out var value))
				{
					return value;
				}
			}
			return "";
		}

		private static void Strike(Component rock, ZNetView view, HitData hit)
		{
			//IL_0045: 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)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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)
			if (!Instance.On || !Instance.ore.Value || hit == null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)view == (Object)null || !view.IsValid())
			{
				return;
			}
			Vector3 position = rock.transform.position;
			if (!Character.InInterior(position) && !((Object)(object)hit.GetAttacker() != (Object)(object)Player.m_localPlayer))
			{
				string text = OreLabel(rock);
				if (text.Length > 0)
				{
					Instance.TryPin(UniversalPins.Category.Ore, position, text, Icon(Instance.oreIcon));
				}
			}
		}

		static AutoPins()
		{
			PinType[] array = new PinType[5];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			AllowedIcons = (PinType[])(object)array;
			Sites = new ConditionalWeakTable<Location, Site>();
			RockLabels = new Dictionary<string, string>();
			DepositSweep = new PinSweep();
			Ticked = new HashSet<PinData>();
			Hits = (Collider[])(object)new Collider[256];
		}
	}
	internal sealed class DeathPins : Tweak
	{
		[HarmonyPatch(typeof(Player), "Update")]
		private static class Sweep
		{
			private static void Postfix(Player __instance)
			{
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				if (Instance.On && Instance.removeWithGrave.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !(Time.time < nextSweep))
				{
					nextSweep = Time.time + 2f;
					if ((Object)(object)Minimap.instance != (Object)null)
					{
						SweepDeathPins(Minimap.instance, ((Component)__instance).transform.position);
					}
				}
			}
		}

		[HarmonyPatch(typeof(TombStone), "Awake")]
		[LoadHook]
		private static class RegisterGrave
		{
			private static void Postfix(TombStone __instance)
			{
				if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.GetZDO() != null)
				{
					Graves.Add(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(TombStone), "Setup")]
		private static class NoteGrave
		{
			private static void Postfix(long ownerUID)
			{
				if ((Object)(object)Game.instance != (Object)null && ownerUID == Game.instance.GetPlayerProfile().GetPlayerID())
				{
					graveMade = true;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static class DeathWithoutGrave
		{
			private static void Prefix(Player __instance)
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					graveMade = false;
				}
			}

			private static void Postfix(Player __instance)
			{
				//IL_0062: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: 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_008c: Invalid comparison between Unknown and I4
				//IL_008f: 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)
				Minimap instance = Minimap.instance;
				if (!Instance.On || !Instance.onlyWithGrave.Value || graveMade || (Object)(object)instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)((Character)__instance).m_nview == (Object)null || !((Character)__instance).m_nview.IsOwner())
				{
					return;
				}
				Vector3 position = ((Component)__instance).transform.position;
				for (int num = instance.m_pins.Count - 1; num >= 0; num--)
				{
					PinData val = instance.m_pins[num];
					if ((int)val.m_type == 4 && Utils.DistanceXZ(val.m_pos, position) < 1f)
					{
						instance.RemovePin(val);
						break;
					}
				}
			}
		}

		internal static readonly DeathPins Instance = new DeathPins();

		private const float SweepInterval = 2f;

		private const float CheckRange = 32f;

		private const float GraveRadius = 8f;

		private ConfigEntry<bool> removeWithGrave;

		private ConfigEntry<bool> onlyWithGrave;

		private static readonly List<TombStone> Graves = new List<TombStone>();

		private static readonly PinSweep GraveSweep = new PinSweep();

		private static float nextSweep;

		private static bool graveMade;

		internal override string Section => "Death Pins";

		protected override string Summary => "A death pin disappears once your grave is gone, whoever emptied it, and a death that leaves no grave leaves no pin.";

		private DeathPins()
		{
		}

		protected override void Bind(ConfigFile config)
		{
			removeWithGrave = config.Bind<bool>(Section, "RemoveWithGrave", true, "Remove a death pin once its grave is gone - emptied by you or anyone else - noticed within a few seconds when you are near it. Also clears old death pins whose grave is long gone.");
			onlyWithGrave = config.Bind<bool>(Section, "OnlyWithGrave", true, "Place no death pin when the death left no grave, because there was nothing to leave behind.");
		}

		private static bool GraveNear(Vector3 pos, long owner)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			for (int num = Graves.Count - 1; num >= 0; num--)
			{
				TombStone val = Graves[num];
				if ((Object)(object)val == (Object)null)
				{
					Graves.RemoveAt(num);
				}
				else if (!flag && (Object)(object)val.m_nview != (Object)null && val.m_nview.IsValid() && val.GetOwner() == owner && Utils.DistanceXZ(((Component)val).transform.position, pos) < 8f)
				{
					flag = true;
				}
			}
			return flag;
		}

		private static void SweepDeathPins(Minimap map, Vector3 origin)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Game.instance == (Object)null))
			{
				long me = Game.instance.GetPlayerProfile().GetPlayerID();
				GraveSweep.Run(map, origin, 32f, (PinData pin) => (int)pin.m_type == 4 && pin.m_save, (Vector3 pos) => GraveNear(pos, me), delegate(PinData pin)
				{
					map.RemovePin(pin);
				});
			}
		}
	}
	internal sealed class PinLooks : Tweak
	{
		[HarmonyPatch(typeof(Minimap), "UpdatePins")]
		private static class Tint
		{
			private static void Postfix(Minimap __instance)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Invalid comparison between Unknown and I4
				//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
				//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
				//IL_0185: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
				bool flag = Instance.On;
				bool flag2 = (int)__instance.m_mode == 2;
				float sharedMapDataFade = __instance.m_sharedMapDataFade;
				foreach (PinData pin in __instance.m_pins)
				{
					if ((Object)(object)pin.m_uiElement == (Object)null || !UniversalPins.TryGetCategory(pin, out var category))
					{
						continue;
					}
					ConfigEntry<float> val = Instance.zoom[(int)category];
					ConfigEntry<bool> val2 = Instance.show[(int)category];
					bool flag3 = flag && ((flag2 && val != null && __instance.LargeZoom > val.Value) || (val2 != null && !val2.Value));
					if (((Component)pin.m_uiElement).gameObject.activeSelf == flag3)
					{
						((Component)pin.m_uiElement).gameObject.SetActive(!flag3);
					}
					PinNameData namePinData = pin.m_NamePinData;
					if (flag3)
					{
						if (namePinData != null && (Object)(object)namePinData.PinNameGameObject != (Object)null)
						{
							namePinData.PinNameGameObject.SetActive(false);
						}
					}
					else
					{
						if ((Object)(object)pin.m_iconElement == (Object)null)
						{
							continue;
						}
						Sprite val3 = IconOf(pin);
						Sprite val4 = (((Object)(object)val3 != (Object)null) ? val3 : pin.m_icon);
						if ((Object)(object)pin.m_iconElement.sprite != (Object)(object)val4)
						{
							pin.m_iconElement.sprite = val4;
						}
						if (!flag)
						{
							continue;
						}
						if ((Object)(object)val3 != (Object)null)
						{
							((Graphic)pin.m_iconElement).color = new Color(1f, 1f, 1f, sharedMapDataFade);
							if (namePinData != null && (Object)(object)namePinData.PinNameGameObject != (Object)null)
							{
								namePinData.PinNameGameObject.SetActive(false);
							}
							continue;
						}
						Color color = Instance.ColourOf(BiomeOf(pin));
						color.a = sharedMapDataFade;
						((Graphic)pin.m_iconElement).color = color;
						Graphic val5 = NameText(namePinData);
						if ((Object)(object)val5 != (Object)null)
						{
							val5.color = color;
						}
					}
				}
			}
		}

		private static class Toggles
		{
			private const float Step = 51f;

			private static readonly Toggle[] Built = (Toggle[])(object)new Toggle[4];

			private static readonly string[] Tokens = new string[4] { "$omp_map_dungeons", "$omp_map_ore", "$omp_map_places", null };

			internal static void Build(Minimap map)
			{
				//IL_0098: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				//IL_011f: Expected O, but got Unknown
				Toggle publicPosition = map.m_publicPosition;
				RectTransform val = (RectTransform)(((Object)(object)publicPosition != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				int num = 2;
				for (int i = 0; i < Built.Length; i++)
				{
					ConfigEntry<bool> shown = Instance.show[i];
					if (shown == null)
					{
						continue;
					}
					GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Transform)val).parent);
					UniversalPins.Category category = (UniversalPins.Category)i;
					((Object)val2).name = "OMP_Show" + category;
					((RectTransform)val2.transform).anchoredPosition = val.anchoredPosition + new Vector2(0f, 51f * (float)num++);
					UIGamePad[] componentsInChildren = val2.GetComponentsInChildren<UIGamePad>(true);
					foreach (UIGamePad val3 in componentsInChildren)
					{
						if ((Object)(object)val3.m_hint != (Object)null)
						{
							Object.DestroyImmediate((Object)(object)val3.m_hint);
						}
						Object.DestroyImmediate((Object)(object)val3);
					}
					Toggle componentInChildren = val2.GetComponentInChildren<Toggle>(true);
					componentInChildren.onValueChanged = new ToggleEvent();
					componentInChildren.isOn = shown.Value;
					((UnityEvent<bool>)(object)componentInChildren.onValueChanged).AddListener((UnityAction<bool>)delegate(bool value)
					{
						shown.Value = value;
					});
					Transform val4 = ((Component)componentInChildren).transform.Find("Label");
					if ((Object)(object)val4 != (Object)null && Localization.instance != null)
					{
						SetText(val4, Localization.instance.Localize(Tokens[i]));
					}
					Built[i] = componentInChildren;
				}
				Refresh();
			}

			private static void SetText(Transform label, string text)
			{
				Component[] components = ((Component)label).GetComponents<Component>();
				foreach (Component val in components)
				{
					PropertyInfo property = ((object)val).GetType().GetProperty("text", typeof(string));
					if (property != null && property.CanWrite)
					{
						property.SetValue(val, text, null);
					}
				}
			}

			internal static void Refresh()
			{
				bool flag = Instance.On && Instance.mapToggles.Value;
				for (int i = 0; i < Built.Length; i++)
				{
					Toggle val = Built[i];
					if (!((Object)(object)val == (Object)null))
					{
						Transform parent = ((Component)val).transform.parent;
						if (((Component)parent).gameObject.activeSelf != flag)
						{
							((Component)parent).gameObject.SetActive(flag);
						}
						val.SetIsOnWithoutNotify(Instance.show[i].Value);
					}
				}
				if ((Object)(object)Minimap.instance != (Object)null)
				{
					Minimap.instance.m_pinUpdateRequired = true;
				}
			}
		}

		[HarmonyPatch(typeof(Minimap), "Start")]
		[LoadHook]
		private static class BuildToggles
		{
			private static void Postfix(Minimap __instance)
			{
				Toggles.Build(__instance);
			}
		}

		[HarmonyPatch(typeof(Minimap), "Update")]
		private static class Hover
		{
			private static PinData hovered;

			private static void Postfix(Minimap __instance)
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Invalid comparison between Unknown and I4
				PinData val = null;
				if (Instance.On && Instance.icons.Value && (int)__instance.m_mode == 2 && ZInput.IsMouseActive())
				{
					val = PinUnderPointer(__instance);
				}
				if (val != hovered)
				{
					hovered = val;
					if (val != null)
					{
						Show(val);
					}
				}
			}

			private static PinData PinUnderPointer(Minimap map)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: 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_0065: Unknown result type (might be due to invalid IL or missing references)
				Vector2 val = Vector2.op_Implicit(ZInput.pointerPosition);
				foreach (PinData pin in map.m_pins)
				{
					RectTransform uiElement = pin.m_uiElement;
					if ((Object)(object)uiElement != (Object)null && ((Component)uiElement).gameObject.activeInHierarchy && (Object)(object)pin.m_iconElement != (Object)null && (Object)(object)pin.m_iconElement.sprite != (Object)(object)pin.m_icon && RectTransformUtility.RectangleContainsScreenPoint(uiElement, val) && (Object)(object)IconOf(pin) != (Object)null)
					{
						return pin;
					}
				}
				return null;
			}

			private static void Show(PinData pin)
			{
				GameObject gameObject = ((Component)pin.m_uiElement).gameObject;
				UITooltip val = gameObject.GetComponent<UITooltip>();
				if ((Object)(object)val == (Object)null)
				{
					GameObject val2 = (((Object)(object)InventoryGui.instance != (Object)null) ? UiAssets.TooltipPrefab(InventoryGui.instance) : null);
					if ((Object)(object)val2 == (Object)null)
					{
						return;
					}
					val = gameObject.AddComponent<UITooltip>();
					val.m_tooltipPrefab = val2;
				}
				val.m_topic = pin.m_name;
				val.m_text = "";
				val.OnHoverStart(gameObject);
				GameObject value = Traverse.Create(typeof(UITooltip)).Field("m_tooltip").GetValue<GameObject>();
				Canvas componentInParent = gameObject.GetComponentInParent<Canvas>();
				if ((Object)(object)value != (Object)null && (Object)(object)componentInParent != (Object)null && (Object)(object)((Component)componentInParent.rootCanvas).transform != (Object)(object)value.transform.parent)
				{
					value.transform.SetParent(((Component)componentInParent.rootCanvas).transform, false);
				}
			}
		}

		internal static readonly PinLooks Instance = new PinLooks();

		private ConfigEntry<Color> meadows;

		private ConfigEntry<Color> blackForest;

		private ConfigEntry<Color> swamp;

		private ConfigEntry<Color> mountain;

		private ConfigEntry<Color> plains;

		private ConfigEntry<Color> mistlands;

		private ConfigEntry<Color> ashLands;

		private ConfigEntry<Color> deepNorth;

		private ConfigEntry<Color> ocean;

		private readonly ConfigEntry<float>[] zoom = new ConfigEntry<float>[4];

		private readonly ConfigEntry<bool>[] show = new ConfigEntry<bool>[4];

		private ConfigEntry<bool> mapToggles;

		private ConfigEntry<bool> icons;

		private static readonly ConditionalWeakTable<PinData, object> Biomes = new ConditionalWeakTable<PinData, object>();

		private static readonly ConditionalWeakTable<GameObject, Graphic> NameTexts = new ConditionalWeakTable<GameObject, Graphic>();

		private static readonly Dictionary<string, string> IconsByName = new Dictionary<string, string>
		{
			{ "$location_forestcrypt", "map_chamber" },
			{ "$location_forestcave", "map_troll" },
			{ "$location_sunkencrypt", "map_crypt" },
			{ "$location_mountaincave", "map_ice_cave" },
			{ "$location_thehole", "map_winding_tunnels" },
			{ "$item_dragonegg", "map_dragons_nest" },
			{ "$omp_place_fulingvillage", "map_fuling_village" },
			{ "$omp_place_tarpit", "map_tar_pit" }
		};

		private const string EntranceIcon = "map_entrance";

		internal override string Section => "Pin Looks";

		protected override string Summary => "Pins that belong to nobody (auto pins, and what map tables share of them) are coloured by their biome, dungeons and some places get icons of their own, ore and place pins hide when the large map is zoomed far out, and toggles on the large map show or hide dungeon, ore and place pins.";

		private PinLooks()
		{
		}

		protected override void Bind(ConfigFile config)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_0089: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			meadows = BindColour(config, "Meadows", new Color(0.62f, 0.9f, 0.45f));
			blackForest = BindColour(config, "BlackForest", new Color(0.35f, 0.75f, 0.45f));
			swamp = BindColour(config, "Swamp", new Color(0.8f, 0.6f, 0.38f));
			mountain = BindColour(config, "Mountain", new Color(0.72f, 0.88f, 1f));
			plains = BindColour(config, "Plains", new Color(1f, 0.9f, 0.4f));
			mistlands = BindColour(config, "Mistlands", new Color(0.8f, 0.75f, 0.9f));
			ashLands = BindColour(config, "AshLands", new Color(1f, 0.45f, 0.35f));
			deepNorth = BindColour(config, "DeepNorth", new Color(0.85f, 0.97f, 1f));
			ocean = BindColour(config, "Ocean", new Color(0.45f, 0.75f, 1f));
			zoom[0] = BindZoom(config, "DungeonZoom", 1f, "dungeon");
			zoom[1] = BindZoom(config, "OreZoom", 0.5f, "ore");
			zoom[2] = BindZoom(config, "PlaceZoom", 0.5f, "place");
			mapToggles = config.Bind<bool>(Section, "MapToggles", true, "Show a toggle for dungeon, ore and place pins at the bottom right of the large map, above the game's own for your position.");
			show[0] = BindShow(config, "ShowDungeons", "dungeon");
			show[1] = BindShow(config, "ShowOre", "ore");
			show[2] = BindShow(config, "ShowPlaces", "place");
			icons = config.Bind<bool>(Section, "Icons", true, "Draw dungeon pins with an icon of their own - burial chambers, a troll cave, a crypt, a frost cave, winding tunnels, any other entrance - and dragon eggs, fuling villages and tar pits with theirs, without a name on the map; hovering one on the large map names it.");
			OnSettingChanged(config, Toggles.Refresh);
		}

		private ConfigEntry<bool> BindShow(ConfigFile config, string key, string category)
		{
			return config.Bind<bool>(Section, key, true, "Whether " + category + " pins are shown on the map and the minimap; the large map's toggle sets this.");
		}

		private ConfigEntry<Color> BindColour(ConfigFile config, string biome, Color value)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return config.Bind<Color>(Section, biome + "Colour", value, "Colour of a pin that belongs to nobody standing in the " + biome + ".");
		}

		private ConfigEntry<float> BindZoom(ConfigFile config, string key, float value, string category)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			return config.Bind<float>(Section, key, value, new ConfigDescription("The large map hides " + category + " pins when zoomed out further than this. The map zooms from 0.01 (closest) to 1 (whole world); 1 never hides them, and 0.5 is where the game stops showing pin names.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>()));
		}

		private Color ColourOf(Biome biome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected I4, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_001f: Invalid comparison between Unknown and I4
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Invalid comparison between Unknown and I4
			//IL_009c: 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)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: 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)
			if ((int)biome <= 16)
			{
				switch (biome - 1)
				{
				default:
					if ((int)biome != 8)
					{
						if ((int)biome != 16)
						{
							break;
						}
						return plains.Value;
					}
					return blackForest.Value;
				case 0:
					return meadows.Value;
				case 1:
					return swamp.Value;
				case 3:
					return mountain.Value;
				case 2:
					break;
				}
			}
			else if ((int)biome <= 64)
			{
				if ((int)biome == 32)
				{
					return ashLands.Value;
				}
				if ((int)biome == 64)
				{
					return deepNorth.Value;
				}
			}
			else
			{
				if ((int)biome == 256)
				{
					return ocean.Value;
				}
				if ((int)biome == 512)
				{
					return mistlands.Value;
				}
			}
			return Color.white;
		}

		private static Biome BiomeOf(PinData pin)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_002f: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			if (Biomes.TryGetValue(pin, out var value))
			{
				return (Biome)value;
			}
			if (WorldGenerator.instance == null)
			{
				return (Biome)0;
			}
			Biome biome = WorldGenerator.instance.GetBiome(pin.m_pos);
			Biomes.Add(pin, biome);
			return biome;
		}

		private static Graphic NameText(PinNameData name)
		{
			GameObject val = ((name != null) ? name.PinNameGameObject : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if (!NameTexts.TryGetValue(val, out var value))
			{
				Graphic[] componentsInChildren = val.GetComponentsInChildren<Graphic>(true);
				foreach (Graphic val2 in componentsInChildren)
				{
					if (((object)val2).GetType().Name.StartsWith("TextMeshPro"))
					{
						value = val2;
						break;
					}
				}
				NameTexts.Add(val, value);
			}
			return value;
		}

		internal static Sprite IconOf(PinData pin)
		{
			if (!Instance.On || !Instance.icons.Value || !UniversalPins.TryGetCategory(pin, out var category))
			{
				return null;
			}
			if (IconsByName.TryGetValue(pin.m_name, out var value))
			{
				return UiAssets.Icon(value);
			}
			if (category != UniversalPins.Category.Dungeon)
			{
				return null;
			}
			return UiAssets.Icon("map_entrance");
		}
	}
	internal sealed class SharedMapTable : Tweak
	{
		private sealed class TableState
		{
			public bool UpToDate;

			public uint Revision;

			public float LastSync = float.NegativeInfinity;

			public int Generation;

			public byte[] Sent;
		}

		[HarmonyPatch(typeof(MapTable), "Start")]
		[LoadHook]
		private static class Register
		{
			private static void Postfix(MapTable __instance)
			{
				if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.GetZDO() != null)
				{
					Tables.Add(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "Update")]
		private static class Tick
		{
			private static void Postfix(Player __instance)
			{
				if (Instance.On && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)Minimap.instance == (Object)null) && !(Time.time < nextCheck))
				{
					nextCheck = Time.time + 1f;
					Instance.Check(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(Minimap), "AddPin")]
		private static class PinAdded
		{
			private static void Postfix(bool save)
			{
				if (save && !syncing)
				{
					generation++;
				}
			}
		}

		[HarmonyPatch(typeof(Minimap), "RemovePin", new Type[] { typeof(PinData) })]
		private static class PinRemoved
		{
			private static void Postfix(PinData pin)
			{
				if (pin != null && pin.m_save && !syncing)
				{
					generation++;
				}
			}
		}

		internal static readonly SharedMapTable Instance = new SharedMapTable();

		private const float CheckInterval = 1f;

		private const float LeaveMargin = 8f;

		private const int BitmapOffset = 8;

		private ConfigEntry<float> syncRange;

		private ConfigEntry<float> minInterval;

		private static readonly List<MapTable> Tables = new List<MapTable>();

		private static readonly ConditionalWeakTable<MapTable, TableState> States = new ConditionalWeakTable<MapTable, TableState>();

		private static int generation;

		private static bool syncing;

		private static float nextCheck;

		internal override string Section => "Shared Map Table";

		protected override string Summary => "A cartography table shares by itself: come near one and your map is written onto it and its map onto yours, without a click. Tables behind a ward you have no access to are only read.";

		private SharedMapTable()
		{
		}

		protected override void Bind(ConfigFile config)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			syncRange = config.Bind<float>(Section, "SyncRange", 64f, new ConfigDescription("Metres from a table within which it syncs. 64 is one zone.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 256f), Array.Empty<object>()));
			minInterval = config.Bind<float>(Section, "MinInterval", 30f, new ConfigDescription("Seconds between two syncs of the same table. A write is as heavy as writing the table by hand and sends the whole map to everyone in the zone, so a low value makes the game stutter for everybody near a table.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 600f), Array.Empty<object>()));
		}

		private void Check(Player player)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)player).transform.position;
			float value = syncRange.Value;
			for (int num = Tables.Count - 1; num >= 0; num--)
			{
				MapTable val = Tables[num];
				if ((Object)(object)val == (Object)null)
				{
					Tables.RemoveAt(num);
				}
				else if (!((Object)(object)val.m_nview == (Object)null) && val.m_nview.IsValid())
				{
					TableState orCreateValue = States.GetOrCreateValue(val);
					float num2 = Vector3.Distance(position, ((Component)val).transform.position);
					if (num2 > value + 8f)
					{
						orCreateValue.UpToDate = false;
					}
					else if (!(num2 > value) && !(Time.time - orCreateValue.LastSync < minInterval.Value))
					{
						ZDO zDO = val.m_nview.GetZDO();
						bool flag = Unread(zDO, orCreateValue);
						bool flag2 = (!orCreateValue.UpToDate || orCreateValue.Generation != generation) && PrivateArea.CheckAccess(((Component)val).transform.position, 0f, false, false);
						if (flag || flag2)
						{
							Sync(val, orCreateValue, zDO, flag, flag2);
							break;
						}
						orCreateValue.UpToDate = true;
					}
				}
			}
		}

		private static bool Unread(ZDO zdo, TableState state)
		{
			if (zdo.DataRevision == state.Revision)
			{
				return false;
			}
			byte[] byteArray = zdo.GetByteArray(ZDOVars.s_data, (byte[])null);
			if (state.Sent != null && byteArray != null && SameBytes(byteArray, state.Sent))
			{
				state.Revision = zdo.DataRevision;
				return false;
			}
			return true;
		}

		private static bool SameBytes(byte[] a, byte[] b)
		{
			if (a.Length != b.Length)
			{
				return false;
			}
			for (int i = 0; i < a.Length; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}

		private static void Sync(MapTable table, TableState state, ZDO zdo, bool read, bool write)
		{
			byte[] array = zdo.GetByteArray(ZDOVars.s_data, (byte[])null);
			if (array != null)
			{
				array = Utils.Decompress(array);
			}
			if (read && array != null)
			{
				syncing = true;
				try
				{
					Minimap.instance.AddSharedMapData(array);
				}
				finally
				{
					syncing = false;
				}
			}
			state.Revision = zdo.DataRevision;
			if (write && (array == null || Lacks(array)))
			{
				ZPackage mapData = table.GetMapData(array);
				state.Sent = mapData.GetArray();
				table.m_nview.InvokeRPC("MapData", new object[1] { mapData });
			}
			state.UpToDate = true;
			state.LastSync = Time.time;
			state.Generation = generation;
		}

		private static bool Lacks(byte[] data)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Invalid comparison between Unknown and I4
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			int length = instance.m_explored.Length;
			if (data.Length < 8 + length)
			{
				return true;
			}
			ZPackage val = new ZPackage(data);
			int num = val.ReadInt();
			if (val.ReadInt() != length)
			{
				return true;
			}
			int[] array = new int[(length + 31) / 32];
			int[] array2 = new int[array.Length];
			instance.m_explored.CopyTo(array, 0);
			instance.m_exploredOthers.CopyTo(array2, 0);
			for (int i = 0; i < array.Length; i++)
			{
				int num2 = array[i] | array2[i];
				if (num2 == 0)
				{
					continue;
				}
				int num3 = 8 + i * 32;
				for (int j = 0; j < 32; j++)
				{
					if ((num2 & (1 << j)) != 0 && data[num3 + j] == 0)
					{
						return true;
					}
				}
			}
			if (num < 2)
			{
				return true;
			}
			val.SetPos(8 + length);
			int num4 = val.ReadInt();
			List<Vector3> list = new List<Vector3>(num4);
			for (int k = 0; k < num4; k++)
			{
				val.ReadLong();
				val.ReadString();
				list.Add(val.ReadVector3());
				val.ReadInt();
				val.ReadBool();
				if (num >= 3)
				{
					val.ReadString();
				}
			}
			bool[] array3 = new bool[num4];
			foreach (PinData pin in instance.m_pins)
			{
				if (!pin.m_save || (int)pin.m_type == 4)
				{
					continue;
				}
				bool flag = false;
				for (int l = 0; l < num4; l++)
				{
					if (Utils.DistanceXZ(list[l], pin.m_pos) < 1f)
					{
						array3[l] = true;
						flag = true;
					}
				}
				if (!flag)
				{
					return true;
				}
			}
			for (int m = 0; m < num4; m++)
			{
				if (!array3[m])
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class UniversalPins
	{
		internal enum Category
		{
			Dungeon,
			Ore,
			Place,
			Portal
		}

		private struct Dismissal
		{
			public long World;

			public Category Category;

			public Vector3 Pos;
		}

		[HarmonyPatch(typeof(Minimap), "Update")]
		[Serves(new Type[]
		{
			typeof(AutoPins),
			typeof(SharedMapTable)
		})]
		private static class UndoClaims
		{
			private static void Postfix(Minimap __instance)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Invalid comparison between Unknown and I4
				if (Active && (int)__instance.m_mode == 2)
				{
					Normalize(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(Minimap), "AddSharedMapData")]
		[Serves(new Type[]
		{
			typeof(AutoPins),
			typeof(SharedMapTable)
		})]
		private static class KeepOnTableRead
		{
			private static readonly List<PinData> held = new List<PinData>();

			private static void Prefix(Minimap __instance)
			{
				held.Clear();
				if (!Active || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				Normalize(__instance);
				foreach (PinData pin in __instance.m_pins)
				{
					if (pin.m_ownerID == Owner)
					{
						pin.m_ownerID = 0L;
						held.Add(pin);
					}
				}
			}

			private static void PruneNearDuplicates(Minimap map)
			{
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				for (int num = map.m_pins.Count - 1; num >= 0; num--)
				{
					PinData val = map.m_pins[num];
					if (!held.Contains(val) && TryGetCategory(val, out var category))
					{
						float num2 = MergeRadius(category);
						foreach (PinData item in held)
						{
							if (Utils.DistanceXZ(item.m_pos, val.m_pos) < num2 && TryGetCategory(item, out var category2) && category2 == category)
							{
								map.RemovePin(val);
								break;
							}
						}
					}
				}
			}

			private static Exception Finalizer(Minimap __instance, Exception __exception)
			{
				if (held.Count == 0 && !Active)
				{
					return __exception;
				}
				foreach (PinData item in held)
				{
					item.m_ownerID = Owner;
				}
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					Normalize(__instance);
					PruneDismissed(__instance);
					PruneNearDuplicates(__instance);
				}
				held.Clear();
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Minimap), "RemovePin", new Type[]
		{
			typeof(Vector3),
			typeof(float)
		})]
		[Serves(new Type[]
		{
			typeof(AutoPins),
			typeof(SharedMapTable)
		})]
		private static class RecordRemoval
		{
			private static void Prefix(Minimap __instance, Vector3 pos, float radius)
			{
				//IL_0009: 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)
				if (Active)
				{
					PinData closestPin = __instance.GetClosestPin(pos, radius, true);
					if (TryGetCategory(closestPin, out var category))
					{
						Dismiss(category, closestPin.m_pos);
					}
				}
			}
		}

		internal const string AuthorPlatform = "OdinsMissingPatch";

		internal static readonly long Owner = ((long)StringExtensionMethods.GetStableHashCode("OdinsMissingPatch") << 1) | 1;

		private static readonly string[] CategoryIds = new string[4] { "dungeon", "ore", "place", "portal" };

		private const string DismissedKey = "omp.pins.dismissed";

		private static List<Dismissal> dismissed;

		private static Player dismissedOwner;

		internal static int CategoryCount => CategoryIds.Length;

		private static bool Active
		{
			get
			{
				if (!AutoPins.Instance.On)
				{
					return SharedMapTable.Instance.On;
				}
				return true;
			}
		}

		private static long CurrentWorld
		{
			get
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					return 0L;
				}
				return ZNet.instance.GetWorldUID();
			}
		}

		internal static float MergeRadius(Category category)
		{
			if (category != Category.Ore)
			{
				return 1f;
			}
			return 8f;
		}

		internal static bool IsUniversal(PinData pin)
		{
			//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)
			if (pin != null)
			{
				Platform platform = pin.m_author.m_platform;
				return ((Platform)(ref platform)).Equals("OdinsMissingPatch");
			}
			return false;
		}

		internal static bool TryGetCategory(PinData pin, out Category category)
		{
			category = Category.Dungeon;
			if (!IsUniversal(pin))
			{
				return false;
			}
			int num = Array.IndexOf(CategoryIds, pin.m_author.m_userID);
			if (num < 0)
			{
				return false;
			}
			category = (Category)num;
			return true;
		}

		internal static PinData Find(Minimap map, Vector3 pos, Category category)
		{
			//IL_0025: 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)
			float num = MergeRadius(category);
			foreach (PinData pin in map.m_pins)
			{
				if (pin.m_save && Utils.DistanceXZ(pos, pin.m_pos) < num && TryGetCategory(pin, out var category2) && category2 == category)
				{
					return pin;
				}
			}
			return null;
		}

		internal static PinData Add(Minimap map, Vector3 pos, Category category, PinType type, string name, bool isChecked = false)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			//IL_0011: 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_0032: 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)
			//IL_004e: 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)
			bool num = (int)type < map.m_visibleIconTypes.Length && !map.m_visibleIconTypes[type];
			PinData result = map.AddPin(pos, type, name, true, isChecked, Owner, new PlatformUserID("OdinsMissingPatch", CategoryIds[(int)category]));
			if (num)
			{
				map.m_visibleIconTypes[type] = false;
				if (map.m_selectedIcons.TryGetValue(type, out var value) && (Object)(object)value != (Object)null)
				{
					((Graphic)((Component)((Component)value).transform.parent).GetComponent<Image>()).color = Color.gray;
				}
			}
			return result;
		}

		internal static void Normalize(Minimap map)
		{
			foreach (PinData pin in map.m_pins)
			{
				if (pin.m_ownerID != Owner && pin.m_save && IsUniversal(pin))
				{
					if (pin.m_ownerID == 0L)
					{
						pin.m_checked = !pin.m_checked;
					}
					pin.m_ownerID = Owner;
					map.m_pinUpdateRequired = true;
				}
			}
		}

		private static List<Dismissal> Dismissed()
		{
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (dismissed != null && (Object)(object)dismissedOwner == (Object)(object)localPlayer)
			{
				return dismissed;
			}
			dismissedOwner = localPlayer;
			dismissed = new List<Dismissal>();
			if ((Object)(object)localPlayer != (Object)null && localPlayer.m_customData.TryGetValue("omp.pins.dismissed", out var value))
			{
				string[] array = value.Split(new char[1] { '|' });
				for (int i = 0; i < array.Length; i++)
				{
					string[] array2 = array[i].Split(new char[1] { ';' });
					int num = Array.IndexOf(CategoryIds, (array2.Length == 4) ? array2[1] : null);
					if (num >= 0 && long.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
					{
						dismissed.Add(new Dismissal
						{
							World = result,
							Category = (Category)num,
							Pos = new Vector3(result2, 0f, result3)
						});
					}
				}
			}
			return dismissed;
		}

		private static void SaveDismissed()
		{
			//IL_007a: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			foreach (Dismissal item in dismissed)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append('|');
				}
				long world = item.World;
				StringBuilder stringBuilder2 = stringBuilder.Append(world.ToString(CultureInfo.InvariantCulture)).Append(';').Append(CategoryIds[(int)item.Category])
					.Append(';');
				float x = item.Pos.x;
				StringBuilder stringBuilder3 = stringBuilder2.Append(x.ToString("0.#", CultureInfo.InvariantCulture)).Append(';');
				x = item.Pos.z;
				stringBuilder3.Append(x.ToString("0.#", CultureInfo.InvariantCulture));
			}
			if (stringBuilder.Length > 0)
			{
				localPlayer.m_customData["omp.pins.dismissed"] = stringBuilder.ToString();
			}
			else
			{
				localPlayer.m_customData.Remove("omp.pins.dismissed");
			}
		}

		internal static bool IsDismissed(Category category, Vector3 pos)
		{
			//IL_0035: 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)
			long currentWorld = CurrentWorld;
			float num = MergeRadius(category);
			foreach (Dismissal item in Dismissed())
			{
				if (item.World == currentWorld && item.Category == category && Utils.DistanceXZ(item.Pos, pos) < num)
				{
					return true;
				}
			}
			return false;
		}

		internal static void Discard(Minimap map, PinData pin, Category category)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			map.RemovePin(pin);
			Dismiss(category, pin.m_pos);
		}

		private static void Dismiss(Category category, Vector3 pos)
		{
			//IL_000e: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !IsDismissed(category, pos))
			{
				Dismissed().Add(new Dismissal
				{
					World = CurrentWorld,
					Category = category,
					Pos = pos
				});
				SaveDismissed();
			}
		}

		internal static int ForgetDismissed()
		{
			long world = CurrentWorld;
			int result = Dismissed().RemoveAll((Dismissal d) => d.World == world);
			SaveDismissed();
			return result;
		}

		private static void PruneDismissed(Minimap map)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			for (int num = map.m_pins.Count - 1; num >= 0; num--)
			{
				PinData val = map.m_pins[num];
				if (TryGetCategory(val, out var category) && IsDismissed(category, val.m_pos))
				{
					map.RemovePin(val);
				}
			}
		}
	}
	internal static class Danger
	{
		[HarmonyPatch(typeof(Player), "RPC_OnTargeted")]
		[Serves(new Type[] { typeof(Danger) })]
		private static class NoticeEnragedEnemy
		{
			private static void Postfix(Player __instance, bool alerted)
			{
				if (alerted && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					lastEnraged = Time.time;
				}
			}
		}

		private const float CheckInterval = 0.25f;

		private const float EnragedMemory = 1.5f;

		internal const string Section = "General";

		internal static ConfigEntry<float> ThreatRadius;

		internal static ConfigEntry<bool> EnragedEnemies;

		internal static ConfigEntry<bool> BossFights;

		private static float lastCheck = float.NegativeInfinity;

		private static bool inDanger;

		private static float lastEnraged = float.NegativeInfinity;

		internal static void Bind(ConfigFile config, string effect)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			ThreatRadius = config.Bind<float>("General", "ThreatRadius", 25f, new ConfigDescription("Metres. A hostile creature closer than this puts you in combat, whether it has noticed you or not: " + effect, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>()));
			EnragedEnemies = config.Bind<bool>("General", "EnragedEnemies", true, "An enemy that has noticed you and is coming for you puts you in combat at any distance, not only inside ThreatRadius. Off means only the radius counts.");
			BossFights = config.Bind<bool>("General", "BossFights", true, "A boss health bar on screen puts you in combat, whoever the boss is after and however far away it is.");
		}

		internal static bool Near(Player player)
		{
			if (Time.time - lastCheck < 0.25f)
			{
				return inDanger;
			}
			lastCheck = Time.time;
			inDanger = Enraged() || BossBarShowing() || HostileWithin(player, ThreatRadius.Value);
			return inDanger;
		}

		private static bool Enraged()
		{
			if (EnragedEnemies.Value)
			{
				return Time.time - lastEnraged < 1.5f;
			}
			return false;
		}

		private static bool BossBarShowing()
		{
			if (BossFights.Value && (Object)(object)EnemyHud.instance != (Object)null)
			{
				return EnemyHud.instance.ShowingBossHud();
			}
			return false;
		}

		private static bool HostileWithin(Player player, float radius)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0059: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)player).transform.position;
			float num = radius * radius;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)player) && !allCharacter.IsDead() && BaseAI.IsEnemy((Character)(object)player, allCharacter))
				{
					Vector3 val = ((Component)allCharacter).transform.position - position;
					if (((Vector3)(ref val)).sqrMagnitude <= num)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	internal static class FavoriteMark
	{
		internal const string Key = "OMP_Favorite";

		internal static bool IsSet(ItemData item)
		{
			if (item != null && item.m_customData != null && item.m_customData.TryGetValue("OMP_Favorite", out var value))
			{
				return value == "1";
			}
			return false;
		}
	}
	internal static class Hotkeys
	{
		internal static bool Pressed(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false))
			{
				return false;
			}
			return ModifiersHeld(shortcut);
		}

		internal static bool Held(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey, false))
			{
				return false;
			}
			return ModifiersHeld(shortcut);
		}

		internal static string Describe(params KeyboardShortcut[] chord)
		{
			//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_0016: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			for (int i = 0; i < chord.Length; i++)
			{
				KeyboardShortcut val = chord[i];
				if ((int)((KeyboardShortcut)(ref val)).MainKey == 0)
				{
					continue;
				}
				foreach (KeyCode modifier in ((KeyboardShortcut)(ref val)).Modifiers)
				{
					list.Add(KeyName(modifier));
				}
				list.Add(KeyName(((KeyboardShortcut)(ref val)).MainKey));
			}
			return string.Join(" + ", list.ToArray());
		}

		private unsafe static string KeyName(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			string text = ZInput.KeyCodeToDisplayName(key);
			if (!string.IsNullOrEmpty(text) && !text.StartsWith("$"))
			{
				return text;
			}
			return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
		}

		private static bool ModifiersHeld(KeyboardShortcut shortcut)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, false))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class LegacyConfig
	{
		private const string OldFile = "rauschekuh.odinsmissingpatch.cfg";

		internal static HashSet<string> Sections(string path)
		{
			HashSet<string> hashSet = new HashSet<string>();
			try
			{
				if (File.Exists(path))
				{
					foreach (ConfigDefinition key in Read(path).Keys)
					{
						hashSet.Add(key.Section);
					}
				}
			}
			catch (Exception ex)
			{
				TweakHost.Log.LogWarning((object)("could not read " + path + ": " + ex.Message));
			}
			return hashSet;
		}

		internal static void Import(ConfigFile config, HashSet<string> known)
		{
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "rauschekuh.odinsmissingpatch.cfg");
				if (!File.Exists(path))
				{
					return;
				}
				Dictionary<ConfigDefinition, string> dictionary = Read(path);
				int num = 0;
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
				{
					if (!known.Contains(item.Key.Section) && dictionary.TryGetValue(item.Key, out var value))
					{
						item.Value.SetSerializedValue(value);
						num++;
					}
				}
				if (num > 0)
				{
					config.Save();
					TweakHost.Log.LogInfo((object)("took " + num + " setting(s) over from rauschekuh.odinsmissingpatch.cfg"));
				}
			}
			catch (Exception ex)
			{
				TweakHost.Log.LogWarning((object)("could not read the settings of rauschekuh.odinsmissingpatch.cfg: " + ex.Message));
			}
		}

		private static Dictionary<ConfigDefinition, string> Read(string path)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			Dictionary<ConfigDefinition, string> dictionary = new Dictionary<ConfigDefinition, string>();
			string text = "";
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0 || text2.StartsWith("#"))
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]"))
				{
					text = text2.Substring(1, text2.Length - 2);
					continue;
				}
				int num = text2.IndexOf('=');
				if (num > 0 && text.Length > 0)
				{
					dictionary[new ConfigDefinition(text, text2.Substring(0, num).Trim())] = text2.Substring(num + 1).Trim();
				}
			}
			return dictionary;
		}
	}
	internal static class Palette
	{
		internal static readonly Color Gold = new Color(1f, 0.8f, 0.3f, 1f);

		internal static readonly Color ChestYellow = new Color(1f, 0.84f, 0.3f, 1f);

		internal const string ChestYellowTag = "<color=#ffd64d>";
	}
	[AttributeUsage(AttributeTargets.Class)]
	internal sealed class ServesAttribute : Attribute
	{
		internal readonly Type[] Tweaks;

		public bool Optional { get; set; }

		public ServesAttribute(params Type[] tweaks)
		{
			Tweaks = tweaks;
		}
	}
	[AttributeUsage(AttributeTargets.Class)]
	internal sealed class AlwaysAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Class)]
	internal sealed class LoadHookAttribute : Attribute
	{
	}
	internal static class Patcher
	{
		private sealed class PatchClass
		{
			internal Type Type;

			internal Tweak[] Serves;

			internal bool Optional;

			internal bool LoadHook;

			internal bool Applied;

			internal bool Failed;
		}

		private static readonly List<PatchClass> classes = new List<PatchClass>();

		private static Harmony harmony;

		private static ManualLogSource log;

		internal static void Map(IList<Tweak> tweaks, ManualLogSource logSource)
		{
			log = logSource;
			Dictionary<Type, Tweak> byType = tweaks.ToDictionary((Tweak t) => t.GetType());
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly());
			foreach (Type type in typesFromAssembly)
			{
				if (!type.IsDefined(typeof(HarmonyPatch), inherit: false))
				{
					continue;
				}
				Tweak[] array = Owners(type, byType);
				if (array != null)
				{
					classes.Add(new PatchClass
					{
						Type = type,
						Serves = array,
						Optional = type.GetCustomAttributes(typeof(ServesAttribute), inherit: false).Cast<ServesAttribute>().Any((ServesAttribute s) => s.Optional),
						LoadHook = type.IsDefined(typeof(LoadHookAttribute), inherit: false)
					});
				}
			}
		}

		private static Tweak[] Owners(Type type, Dictionary<Type, Tweak> byType)
		{
			if (type.IsDefined(typeof(AlwaysAttribute), inherit: false))
			{
				return new Tweak[0];
			}
			ServesAttribute servesAttribute = (ServesAttribute)type.GetCustomAttributes(typeof(ServesAttribute), inherit: false).FirstOrDefault();
			if (servesAttribute != null)
			{
				List<Tweak> list = new List<Tweak>();
				Type[] tweaks = servesAttribute.Tweaks;
				foreach (Type tweak in tweaks)
				{
					if (byType.TryGetValue(tweak, out var value))
					{
						list.Add(value);
						continue;
					}
					if (typeof(Tweak).IsAssignableFrom(tweak))
					{
						log.LogWarning((object)(type.FullName + " serves " + tweak.Name + ", which is not in the Tweaks list"));
						continue;
					}
					list.AddRange(byType.Values.Where((Tweak t) => t.Uses.Contains(tweak)));
				}
				if (list.Count <= 0)
				{
					return null;
				}
				return list.Distinct().ToArray();
			}
			Type declaringType = type.DeclaringType;
			while (declaringType != null)
			{
				if (byType.TryGetValue(declaringType, out var value2))
				{
					return new Tweak[1] { value2 };
				}
				declaringType = declaringType.DeclaringType;
			}
			log.LogWarning((object)(type.FullName + " belongs to no tweak and names none, so it is always applied"));
			return new Tweak[0];
		}

		internal static bool AnyServedOn(Type patchClass)
		{
			foreach (PatchClass @class in classes)
			{
				if (@class.Type != patchClass)
				{
					continue;
				}
				Tweak[] serves = @class.Serves;
				for (int i = 0; i < serves.Length; i++)
				{
					if (serves[i].On)
					{
						return true;
					}
				}
				return false;
			}
			return false;
		}

		internal static bool MayNeedRestart(Tweak tweak)
		{
			return classes.Any((PatchClass c) => c.LoadHook && c.Serves.Contains(tweak));
		}

		internal static void Apply(Harmony instance, IList<Tweak> tweaks)
		{
			harmony = instance;
			foreach (PatchClass @class in classes)
			{
				if (@class.Serves.Length == 0 || @class.Serves.Any((Tweak t) => t.Wanted && !t.Broken))
				{
					TryApply(@class);
				}
			}
			foreach (Tweak tweak in tweaks)
			{
				if (tweak.Wanted && !tweak.Broken)
				{
					GoLive(tweak);
				}
				tweak.Enabled.SettingChanged += delegate
				{
					SwitchedOn(tweak);
				};
			}
		}

		private static void SwitchedOn(Tweak tweak)
		{
			if (!tweak.Wanted || tweak.Patched || tweak.Broken)
			{
				return;
			}
			List<PatchClass> list = classes.Where((PatchClass c) => c.Serves.Contains(tweak) && !c.Applied && !c.Failed).ToList();
			if (list.Any((PatchClass c) => c.LoadHook))
			{
				log.LogInfo((object)(tweak.Section + " was off at launch and takes effect at the next start of the game"));
				return;
			}
			foreach (PatchClass item in list)
			{
				TryApply(item);
			}
			if (!tweak.Broken)
			{
				GoLive(tweak);
				log.LogInfo((object)(tweak.Section + " switched on"));
			}
		}

		private static void GoLive(Tweak tweak)
		{
			tweak.Patched = true;
			tweak.Refresh();
		}

		private static void TryApply(PatchClass patch)
		{
			try
			{
				harmony.CreateClassProcessor(patch.Type).Patch();
				patch.Applied = true;
			}
			catch (Exception ex)
			{
				patch.Failed = true;
				Unpatch(patch);
				string message = (ex.InnerException ?? ex).Message;
				if (patch.Serves.Length == 0 || patch.Optional)
				{
					log.LogError((object)(patch.Type.FullName + " could not be patched, carrying on without it: " + message));
					return;
				}
				Tweak[] serves = patch.Serves;
				for (int i = 0; i < serves.Length; i++)
				{
					Break(serves[i], patch.Type, message);
				}
			}
		}

		private static void Break(Tweak tweak, Type failed, string cause)
		{
			if (tweak.Broken)
			{
				return;
			}
			tweak.Broken = true;
			tweak.Patched = false;
			log.LogError((object)(tweak.Section + " is switched off: " + failed.FullName + " could not be patched, most likely because a game update changed it. " + cause));
			foreach (PatchClass @class in classes)
			{
				if (@class.Applied && @class.Serves.Contains(tweak) && !@class.Serves.Any((Tweak t) => !t.Broken && (t.Patched || t.Wanted)))
				{
					Unpatch(@class);
					@class.Applied = false;
				}
			}
			tweak.Refresh();
		}

		private static void Unpatch(PatchClass patch)
		{
			try
			{
				foreach (MethodBase item in harmony.GetPatchedMethods().ToList())
				{
					Patches patchInfo = Harmony.GetPatchInfo(item);
					if (patchInfo == null)
					{
						continue;
					}
					foreach (Patch item2 in patchInfo.Prefixes.Concat(patchInfo.Postfixes).Concat(patchInfo.Transpilers).Concat(patchInfo.Finalizers)
						.Concat(patchInfo.ILManipulators)
						.ToList())
					{
						if (item2.owner == harmony.Id && item2.PatchMethod.DeclaringType == patch.Type)
						{
							harmony.Unpatch(item, item2.PatchMethod);
						}
					}
				}
			}
			catch (Exception ex)
			{
				log.LogError((object)("could not take out " + patch.Type.FullName + ": " + ex.Message));
			}
		}
	}
	internal static class Translations
	{
		[HarmonyPatch(typeof(Localization), "SetupLanguage")]
		[Always]
		private static class Load
		{
			private static void Postfix(Localization __instance, string language)
			{
				if (__instance == null || !Read() || !Reader())
				{
					return;
				}
				object obj = null;
				try
				{
					obj = textAsset.Invoke(new object[1] { csv });
					loadCsv.Invoke(__instance, new object[2]
					{
						obj,
						Has(language) ? language : "English"
					});
					Revision++;
				}
				catch (Exception ex)
				{
					TweakHost.Log.LogWarning((object)("could not load translations.csv: " + ex.Message));
				}
				finally
				{
					Object val = (Object)((obj is Object) ? obj : null);
					if (val != (Object)null)
					{
						Object.Destroy(val);
					}
				}
			}
		}

		private const string FileName = "translations.csv";

		private const string Fallback = "English";

		private static string csv;

		private static string[] languages;

		private static ConstructorInfo textAsset;

		private static MethodInfo loadCsv;

		private static bool looked;

		internal static int Revision { get; private set; }

		private static bool Read()
		{
			if (languages != null)
			{
				return csv != null;
			}
			languages = new string[0];
			try
			{
				string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "translations.csv");
				if (!File.Exists(text))
				{
					TweakHost.Log.LogDebug((object)("no translations.csv beside the DLL: " + text));
					return false;
				}
				csv = File.ReadAllText(text);
				using (StringReader stringReader = new StringReader(csv))
				{
					string text2 = stringReader.ReadLine();
					languages = ((text2 != null) ? text2.Split(new char[1] { ',' }) : new string[0]);
				}
				for (int i = 0; i < languages.Length; i++)
				{
					languages[i] = languages[i].Trim().Trim(new char[1] { '"' });
				}
			}
			catch (Exception ex)
			{
				csv = null;
				TweakHost.Log.LogWarning((object)("could not read translations.csv: " + ex.Message));
			}
			return csv != null;
		}

		private static bool Has(string language)
		{
			string[] array = languages;
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == language)
				{
					return true;
				}
			}
			return false;
		}

		private static bool Reader()
		{
			if (looked)
			{
				if (loadCsv != null)
				{
					return textAsset != null;
				}
				return false;
			}
			looked = true;
			Type type = Type.GetType("UnityEngine.TextAsset, UnityEngine.CoreModule");
			if (type != null)
			{
				textAsset = type.GetConstructor(new Type[1] { typeof(string) });
				loadCsv = typeof(Localization).GetMethod("LoadCSV", new Type[2]
				{
					type,
					typeof(string)
				});
			}
			if (loadCsv == null || textAsset == null)
			{
				TweakHost.Log.LogWarning((object)"Localization.LoadCSV not found; the mod's words stay untranslated");
				return false;
			}
			return true;
		}
	}
	internal abstract class Tweak
	{
		private const float MinMultiplier = 0.1f;

		private const float MaxMultiplier = 20f;

		private const string RestartNote = " If it was off when the game started, switching it on may only take effect after a restart.";

		private ConfigEntry<bool> enabled;

		private readonly List<Action> settingHandlers = new List<Action>();

		internal abstract string Section { get; }

		protected abstract string Summary { get; }

		internal virtual Type[] Uses => Type.EmptyTypes;

		internal bool On
		{
			get
			{
				if (Patched)
				{
					return Wanted;
				}
				return false