Decompiled source of ZariRules v1.3.2

BepInEx/plugins/ZariRules/ZariRules.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ZariRules")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.2.0")]
[assembly: AssemblyInformationalVersion("1.3.2")]
[assembly: AssemblyProduct("ZariRules")]
[assembly: AssemblyTitle("ZariRules")]
[assembly: AssemblyVersion("1.3.2.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace ZariRules
{
	internal static class CompatibilityProbe
	{
		internal const string EaqsGuid = "randyknapp.mods.equipmentandquickslots";

		internal const string EpicLootGuid = "randyknapp.mods.epicloot";

		internal static bool VerifyGameBuild(out string reason)
		{
			Guid moduleVersionId = typeof(Player).Assembly.ManifestModule.ModuleVersionId;
			if (!GameBuildContracts.IsSupported(moduleVersionId))
			{
				reason = $"unsupported assembly_valheim MVID {moduleVersionId}; expected verified Valheim 1.0.12 or 1.0.7 Windows/Linux build";
				return false;
			}
			reason = string.Empty;
			return true;
		}

		internal static bool VerifyMethod(Type type, string name, BindingFlags flags, Type returnType, Type[] parameters, string expectedIlSha256, out MethodInfo? method, out string reason)
		{
			method = type.GetMethod(name, flags, null, parameters, null);
			if (method == null || method.ReturnType != returnType)
			{
				reason = "missing or changed " + type.FullName + "." + name + "(" + string.Join(",", parameters.Select((Type x) => x.FullName)) + ")";
				return false;
			}
			byte[] array = method.GetMethodBody()?.GetILAsByteArray();
			if (array == null)
			{
				reason = "no IL body for " + type.FullName + "." + name;
				return false;
			}
			using SHA256 sHA = SHA256.Create();
			string text = BitConverter.ToString(sHA.ComputeHash(array)).Replace("-", string.Empty).ToLowerInvariant();
			string b = GameBuildContracts.ExpectedIlHash(type.Assembly.ManifestModule.ModuleVersionId, expectedIlSha256);
			if (!string.Equals(text, b, StringComparison.Ordinal))
			{
				reason = "IL contract mismatch for " + type.FullName + "." + name + ": " + text;
				return false;
			}
			reason = string.Empty;
			return true;
		}

		internal static bool VerifyEaqs(out bool installed, out string reason)
		{
			installed = Chainloader.PluginInfos.TryGetValue("randyknapp.mods.equipmentandquickslots", out var value);
			if (!installed)
			{
				reason = "EAQS absent; base-inventory mode";
				return true;
			}
			string text = value.Metadata.Version.ToString();
			reason = "EAQS " + text + "; game death contracts and patch owners checked separately";
			return true;
		}

		internal static string InstalledPluginVersion(string guid)
		{
			if (!Chainloader.PluginInfos.TryGetValue(guid, out var value))
			{
				return "absent";
			}
			return value.Metadata.Version.ToString();
		}

		internal static bool HasOnlyAllowedPatchOwners(MethodBase method, IReadOnlyCollection<string> allowed, out string reason)
		{
			Patches patchInfo = Harmony.GetPatchInfo(method);
			if (patchInfo == null)
			{
				reason = string.Empty;
				return true;
			}
			string[] array = patchInfo.Owners.Where((string owner) => !allowed.Contains(owner)).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string owner) => owner, StringComparer.Ordinal)
				.ToArray();
			if (array.Length == 0)
			{
				reason = string.Empty;
				return true;
			}
			reason = "unexpected Harmony owner(s) on " + method.DeclaringType?.FullName + "." + method.Name + ": " + string.Join(",", array);
			return false;
		}
	}
	internal static class DeathContext
	{
		internal const string HarmonyId = "zari.rules.death-context";

		private static readonly DeathScopeTracker<Player> Scopes = new DeathScopeTracker<Player>();

		private static Harmony? harmony;

		internal static bool Ready { get; private set; }

		internal static bool Install(out string reason)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0051: 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_0062: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			if (!CompatibilityProbe.VerifyMethod(typeof(Player), "OnDeath", BindingFlags.Instance | BindingFlags.Public, typeof(void), Type.EmptyTypes, "76b17b9f1894281bde973d01f97a8f330b955e275d4556ceea1ead4ed56f2c21", out MethodInfo method, out reason))
			{
				return false;
			}
			try
			{
				harmony = new Harmony("zari.rules.death-context");
				HarmonyMethod val = new HarmonyMethod(typeof(DeathContext), "Prefix", (Type[])null)
				{
					priority = 800
				};
				HarmonyMethod val2 = new HarmonyMethod(typeof(DeathContext), "Finalizer", (Type[])null);
				val2.priority = 0;
				val2.after = new string[1] { "randyknapp.mods.equipmentandquickslots" };
				HarmonyMethod val3 = val2;
				harmony.Patch((MethodBase)method, val, (HarmonyMethod)null, (HarmonyMethod)null, val3, (HarmonyMethod)null);
				Ready = true;
				reason = "Player.OnDeath scope";
				return true;
			}
			catch (Exception ex)
			{
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				reason = ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		internal static bool TryGet(Player player, out DeathScopeTracker<Player>.Scope? scope)
		{
			return Scopes.TryGetCurrent(player, out scope);
		}

		internal static void Reset()
		{
			Scopes.Clear();
		}

		internal static void Uninstall()
		{
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Ready = false;
			Reset();
		}

		private static void Prefix(Player __instance, out DeathScopeTracker<Player>.Scope? __state)
		{
			__state = null;
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !ZariRulesPlugin.Instance.Rules.IsServerConfigurationReady)
			{
				return;
			}
			__state = Scopes.Open(__instance, ZariRulesPlugin.Instance.Rules.KeepInventoryOnDeath.Value, ZariRulesPlugin.Instance.Rules.KeepSkillsOnDeath.Value);
			try
			{
				Diagnostics.OnDeathStarted(__instance);
			}
			catch (Exception ex)
			{
				ZariRulesPlugin.LogError("ZR901", "feature=Diagnostics phase=death-start reason=" + ex.GetType().Name);
			}
		}

		private static Exception? Finalizer(Player __instance, Exception? __exception, DeathScopeTracker<Player>.Scope? __state)
		{
			if (__state != null)
			{
				try
				{
					Diagnostics.OnDeathFinished(__instance, __state, __exception == null);
				}
				catch (Exception ex)
				{
					ZariRulesPlugin.LogError("ZR901", "feature=Diagnostics phase=death-finish reason=" + ex.GetType().Name);
				}
				finally
				{
					try
					{
						Scopes.Close(__instance, __state);
					}
					catch (Exception ex2)
					{
						ZariRulesPlugin.LogError("ZR901", "feature=DeathContext phase=close reason=" + ex2.GetType().Name);
					}
				}
			}
			return __exception;
		}
	}
	internal static class DeathInventoryFeature
	{
		internal const string HarmonyId = "zari.rules.keep-inventory";

		private static Harmony? harmony;

		internal static bool Ready { get; private set; }

		internal static bool Install(out string reason)
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Expected O, but got Unknown
			if (!DeathContext.Ready)
			{
				reason = "death context unavailable";
				return false;
			}
			if (!CompatibilityProbe.VerifyEaqs(out bool installed, out string reason2))
			{
				reason = reason2;
				return false;
			}
			if (!CompatibilityProbe.VerifyMethod(typeof(Player), "CreateTombStone", BindingFlags.Instance | BindingFlags.Public, typeof(void), Type.EmptyTypes, "00f13b0d81783d11221910d3b2090f27918f53957ea98b114af49d151dacbd98", out MethodInfo method, out reason) || !CompatibilityProbe.VerifyMethod(typeof(Player), "OnDeath", BindingFlags.Instance | BindingFlags.Public, typeof(void), Type.EmptyTypes, "76b17b9f1894281bde973d01f97a8f330b955e275d4556ceea1ead4ed56f2c21", out MethodInfo method2, out reason) || !CompatibilityProbe.VerifyMethod(typeof(Character), "CheckDeath", BindingFlags.Instance | BindingFlags.NonPublic, typeof(void), Type.EmptyTypes, "fef54e1a3e6de1ea05ab81ffd5346eaea2c503b00ccefcc342c4ae862caaa095", out MethodInfo method3, out reason))
			{
				return false;
			}
			HashSet<string> allowed = new HashSet<string>(StringComparer.Ordinal) { "randyknapp.mods.equipmentandquickslots" };
			HashSet<string> allowed2 = new HashSet<string>(StringComparer.Ordinal) { "randyknapp.mods.equipmentandquickslots", "zari.rules.death-context" };
			if (!CompatibilityProbe.HasOnlyAllowedPatchOwners(method, allowed, out reason) || !CompatibilityProbe.HasOnlyAllowedPatchOwners(method2, allowed2, out reason) || !CompatibilityProbe.HasOnlyAllowedPatchOwners(method3, allowed, out reason))
			{
				return false;
			}
			try
			{
				harmony = new Harmony("zari.rules.keep-inventory");
				harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(DeathInventoryFeature), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Ready = true;
				reason = $"K1 Player.CreateTombStone; {reason2}; eaqsInstalled={installed}";
				return true;
			}
			catch (Exception ex)
			{
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				reason = ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		internal static void Uninstall()
		{
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Ready = false;
		}

		internal static bool VerifyPatchOwners(out string reason)
		{
			if (CompatibilityProbe.HasOnlyAllowedPatchOwners(AccessTools.Method(typeof(Player), "CreateTombStone", (Type[])null, (Type[])null), (IReadOnlyCollection<string>)(object)new string[2] { "zari.rules.keep-inventory", "randyknapp.mods.equipmentandquickslots" }, out reason) && CompatibilityProbe.HasOnlyAllowedPatchOwners(AccessTools.Method(typeof(Player), "OnDeath", (Type[])null, (Type[])null), (IReadOnlyCollection<string>)(object)new string[3] { "zari.rules.death-context", "zari.rules.keep-skills", "randyknapp.mods.equipmentandquickslots" }, out reason))
			{
				return CompatibilityProbe.HasOnlyAllowedPatchOwners(AccessTools.Method(typeof(Character), "CheckDeath", (Type[])null, (Type[])null), (IReadOnlyCollection<string>)(object)new string[2] { "zari.rules.diagnostics", "randyknapp.mods.equipmentandquickslots" }, out reason);
			}
			return false;
		}

		private static bool Prefix(Player __instance)
		{
			if (!Ready || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !DeathContext.TryGet(__instance, out DeathScopeTracker<Player>.Scope scope) || scope == null || !scope.KeepInventory)
			{
				return true;
			}
			scope.TombstoneSuppressed = true;
			Diagnostics.Info("ZR202", "tombstoneBranch=suppressed transferredByZariRules=0");
			return false;
		}
	}
	internal sealed class DeathScopeTracker<TKey> where TKey : class
	{
		internal sealed class Scope
		{
			internal bool KeepInventory { get; }

			internal bool KeepSkills { get; }

			internal bool TombstoneSuppressed { get; set; }

			internal Scope(bool keepInventory, bool keepSkills)
			{
				KeepInventory = keepInventory;
				KeepSkills = keepSkills;
			}
		}

		private readonly Dictionary<TKey, Stack<Scope>> scopes = new Dictionary<TKey, Stack<Scope>>();

		internal Scope Open(TKey key, bool keepInventory, bool keepSkills)
		{
			if (!scopes.TryGetValue(key, out Stack<Scope> value))
			{
				value = new Stack<Scope>();
				scopes.Add(key, value);
			}
			Scope scope = new Scope(keepInventory, keepSkills);
			value.Push(scope);
			return scope;
		}

		internal bool TryGetCurrent(TKey key, out Scope? scope)
		{
			if (scopes.TryGetValue(key, out Stack<Scope> value) && value.Count > 0)
			{
				scope = value.Peek();
				return true;
			}
			scope = null;
			return false;
		}

		internal void Close(TKey key, Scope scope)
		{
			if (!scopes.TryGetValue(key, out Stack<Scope> value) || value.Count == 0 || value.Peek() != scope)
			{
				throw new InvalidOperationException("Попытка закрыть неактивный death scope.");
			}
			value.Pop();
			if (value.Count == 0)
			{
				scopes.Remove(key);
			}
		}

		internal void Clear()
		{
			scopes.Clear();
		}
	}
	internal static class Diagnostics
	{
		private sealed class Snapshot
		{
			internal long Sequence { get; set; }

			internal int Stacks { get; private set; }

			internal int Units { get; private set; }

			internal string ItemDigest { get; private set; } = string.Empty;

			internal string SkillDigest { get; private set; } = string.Empty;

			internal HashSet<ItemData> References { get; } = new HashSet<ItemData>(ReferenceComparer<ItemData>.Instance);

			private Snapshot()
			{
			}

			internal static Snapshot Capture(Player player)
			{
				Snapshot snapshot = new Snapshot();
				List<string> list = new List<string>();
				foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
				{
					snapshot.References.Add(allItem);
					snapshot.Stacks++;
					snapshot.Units += allItem.m_stack;
					string text = string.Join(";", from pair in allItem.m_customData.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> pair) => pair.Key, StringComparer.Ordinal)
						select pair.Key + "=" + pair.Value);
					string text2 = (((Object)(object)allItem.m_dropPrefab == (Object)null) ? "<null>" : Utils.GetPrefabName(allItem.m_dropPrefab));
					list.Add(string.Join("|", text2, allItem.m_stack, allItem.m_quality, allItem.m_variant, allItem.m_durability.ToString("R"), allItem.m_gridPos.x, allItem.m_gridPos.y, allItem.m_equipped, text));
				}
				list.Sort(StringComparer.Ordinal);
				snapshot.ItemDigest = Hash(string.Join("\n", list));
				IEnumerable<string> values = from skill in ((Character)player).GetSkills().GetSkillList()
					orderby (int)skill.m_info.m_skill
					select $"{(int)skill.m_info.m_skill}:{FloatBits(skill.m_level):x8}:{FloatBits(skill.m_accumulator):x8}";
				snapshot.SkillDigest = Hash(string.Join(";", values));
				return snapshot;
			}

			private static int FloatBits(float value)
			{
				return BitConverter.ToInt32(BitConverter.GetBytes(value), 0);
			}

			private static string Hash(string value)
			{
				using SHA256 sHA = SHA256.Create();
				return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(value))).Replace("-", string.Empty).ToLowerInvariant();
			}
		}

		private sealed class ReferenceComparer<T> : IEqualityComparer<T> where T : class
		{
			internal static readonly ReferenceComparer<T> Instance = new ReferenceComparer<T>();

			public bool Equals(T x, T y)
			{
				return x == y;
			}

			public int GetHashCode(T obj)
			{
				return RuntimeHelpers.GetHashCode(obj);
			}
		}

		internal const string HarmonyId = "zari.rules.diagnostics";

		private static readonly Dictionary<Player, Snapshot> BeforeDeath = new Dictionary<Player, Snapshot>();

		private static Harmony? harmony;

		private static long sequence;

		internal static bool Install(out string reason)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			if (!CompatibilityProbe.VerifyMethod(typeof(Character), "CheckDeath", BindingFlags.Instance | BindingFlags.NonPublic, typeof(void), Type.EmptyTypes, "fef54e1a3e6de1ea05ab81ffd5346eaea2c503b00ccefcc342c4ae862caaa095", out MethodInfo method, out reason))
			{
				return false;
			}
			try
			{
				harmony = new Harmony("zari.rules.diagnostics");
				HarmonyMethod val = new HarmonyMethod(typeof(Diagnostics), "CheckDeathPrefix", (Type[])null);
				val.priority = 800;
				val.before = new string[1] { "randyknapp.mods.equipmentandquickslots" };
				HarmonyMethod val2 = val;
				harmony.Patch((MethodBase)method, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				reason = "read-only Character.CheckDeath observer";
				return true;
			}
			catch (Exception ex)
			{
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				reason = ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		internal static void Info(string code, string message)
		{
			try
			{
				ZariRulesPlugin.Instance.Logger.LogInfo((object)(code + " " + message));
			}
			catch
			{
			}
		}

		internal static void OnDeathStarted(Player player)
		{
			if (ZariRulesPlugin.Instance.Rules.DebugLogging.Value)
			{
				Snapshot snapshot = Snapshot.Capture(player);
				if (!BeforeDeath.TryGetValue(player, out Snapshot value))
				{
					value = snapshot;
				}
				long num = ++sequence;
				long num2 = (value.Sequence = num);
				BeforeDeath[player] = value;
				Info("ZR200", $"death={num2} started preEaqsStacks={value.Stacks} preEaqsUnits={value.Units} visibleStacks={snapshot.Stacks} visibleUnits={snapshot.Units}");
				Info("ZR201", string.Format("death={0} eaqsVersion={1} pendingCount=not-read", num2, CompatibilityProbe.InstalledPluginVersion("randyknapp.mods.equipmentandquickslots")));
			}
		}

		internal static void OnDeathFinished(Player player, DeathScopeTracker<Player>.Scope scope, bool successful)
		{
			if (ZariRulesPlugin.Instance.Rules.DebugLogging.Value && BeforeDeath.TryGetValue(player, out Snapshot value))
			{
				Snapshot snapshot = Snapshot.Capture(player);
				bool flag = value.ItemDigest == snapshot.ItemDigest && value.References.SetEquals(snapshot.References);
				bool flag2 = value.SkillDigest == snapshot.SkillDigest;
				Info("ZR204", $"death={value.Sequence} finished={successful.ToString().ToLowerInvariant()} stacks={snapshot.Stacks} units={snapshot.Units} itemDigestEqual={flag.ToString().ToLowerInvariant()} skillDigestEqual={flag2.ToString().ToLowerInvariant()} tombstoneSuppressed={scope.TombstoneSuppressed.ToString().ToLowerInvariant()}");
				BeforeDeath.Remove(player);
			}
		}

		internal static void ResetSession()
		{
			BeforeDeath.Clear();
			sequence = 0L;
		}

		internal static void Uninstall()
		{
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			ResetSession();
		}

		private static void CheckDeathPrefix(Character __instance)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_0055: Expected O, but got Unknown
			try
			{
				if (ZariRulesPlugin.Instance.Rules.DebugLogging.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !__instance.IsDead() && !(__instance.GetHealth() > 0f))
				{
					BeforeDeath[(Player)__instance] = Snapshot.Capture((Player)__instance);
				}
			}
			catch (Exception ex)
			{
				ZariRulesPlugin.LogError("ZR901", "feature=Diagnostics phase=pre-death reason=" + ex.GetType().Name);
			}
		}
	}
	internal static class GameBuildContracts
	{
		internal static readonly Guid WindowsClient = new Guid("b8a6fd30-3061-43b3-99f2-11c2e315bc54");

		internal static readonly Guid LinuxServer = new Guid("e8e9a680-25ea-4b87-bdd4-07412665a2ad");

		internal static readonly Guid LegacyWindowsClient = new Guid("db11c804-ebef-403d-8e77-ac219f14bf46");

		internal static readonly Guid LegacyLinuxServer = new Guid("0ee5b4bc-8894-42ff-b5dc-b038511e2bfc");

		internal static bool IsSupported(Guid mvid)
		{
			if (!(mvid == WindowsClient) && !(mvid == LinuxServer) && !(mvid == LegacyWindowsClient))
			{
				return mvid == LegacyLinuxServer;
			}
			return true;
		}

		internal static string ExpectedIlHash(Guid mvid, string clientHash)
		{
			if (mvid == WindowsClient)
			{
				return clientHash switch
				{
					"76b17b9f1894281bde973d01f97a8f330b955e275d4556ceea1ead4ed56f2c21" => "94a716985990cdfbef2dab44cde6a20c0ddc85ffe65be3b779670aa468339ba7", 
					"00f13b0d81783d11221910d3b2090f27918f53957ea98b114af49d151dacbd98" => "c4771274beb528d85938ca69e5cadb3d9a906a709a67c732c63d8a2ff02f1059", 
					"807a60cdbe58ac91d5620c9f1bbeb6d866a7b0085851c37edc19c08933c48591" => "e3196a9480de19a817132384a4947f5cff2a78d18c73779758bef4ff8e341e8d", 
					"146dc8c182d22edbcc9f2192f3edb621ec03f219bf327c7315ed6f910563a798" => "3715e25250ae63a5a476a287186fded73554c1da919f7d95c794bdb96561fb19", 
					"bf1b5c65eee0066748cbd0b77e326d2e5e3cd2786f3fa80c2d063dccd4522b7a" => "44735573fcec61a54b9e5d998e5ca5704899d4cda93e7246eb4427bce862b8ea", 
					"0d778f0dea28e91896454793f46dc19c0a456883b218ca2113747744a0dd6ba6" => "ade188b9421fb218374901295da958023f47db81d64729e302f0c43042e261af", 
					"0a477dbd4078ac2966eba61c40b1382a8b572b01e39bafeef72681da3a3522fa" => "6fbfa5d44d14c504ba016c163a807933de874e52621e28dde6b672f5a38b2151", 
					"3acb4e2da64e100830c9912ba3dceb86211cd08eccd7b4739fe1e2cf24e95c68" => "d580ea92edcb6c7597328c6762634f54f6977b12abb253bef693a85f7307d64e", 
					"1febeff1fc10e2e78fe1fa15b5ef3ac6dea400e0795c7e26969a1ebd25d64bfb" => "d07178720ccb367e61dee4cb30149ebb1ab37b20bd3a71df15bb42c82c24fc7e", 
					_ => clientHash, 
				};
			}
			if (mvid == LegacyLinuxServer)
			{
				return clientHash switch
				{
					"0d778f0dea28e91896454793f46dc19c0a456883b218ca2113747744a0dd6ba6" => "2d5538172782de67019d39e75b0e7ef3de1138b0714c3611822c256c38525076", 
					"76b17b9f1894281bde973d01f97a8f330b955e275d4556ceea1ead4ed56f2c21" => "d7a0608475b68e73155b6b013d2fb120054a0b546f58a14365c7f00ccbbdf23f", 
					"00f13b0d81783d11221910d3b2090f27918f53957ea98b114af49d151dacbd98" => "f5c48166fceba41b88101c905968f2c262500a96c99a7b13bcb71fce5db07db8", 
					"807a60cdbe58ac91d5620c9f1bbeb6d866a7b0085851c37edc19c08933c48591" => "af82486e3d28756396e11d0fc467fb5dc76abd9abb1c8b25665d67599247dc06", 
					"146dc8c182d22edbcc9f2192f3edb621ec03f219bf327c7315ed6f910563a798" => "f50af2280c1f078c07be5b9c2a7e7ea3e0155d072a9d7e03f56c4236e50bd095", 
					"bf1b5c65eee0066748cbd0b77e326d2e5e3cd2786f3fa80c2d063dccd4522b7a" => "4fa96ee109e0f3c7cc47e51e716707f5092db65df236c4232e6789098dff9e82", 
					"0a477dbd4078ac2966eba61c40b1382a8b572b01e39bafeef72681da3a3522fa" => "98b819c45679502cd6cdf3ed277e82ce4ef406ec512cb3c2ea4c69f3f673d2c8", 
					"3acb4e2da64e100830c9912ba3dceb86211cd08eccd7b4739fe1e2cf24e95c68" => "249b687a4a7c042248fd71eaf8603f7689de523e9391fa00b75ba632bc985f6e", 
					"1febeff1fc10e2e78fe1fa15b5ef3ac6dea400e0795c7e26969a1ebd25d64bfb" => "2d858424cc340899fcd60b5e2a6cae6e5ecdf83740d46b06524bdaf7e5ec8018", 
					_ => clientHash, 
				};
			}
			if (mvid == LinuxServer)
			{
				return clientHash switch
				{
					"76b17b9f1894281bde973d01f97a8f330b955e275d4556ceea1ead4ed56f2c21" => "67755886372660c97917c425b7044392277d616b08d0e4b650204d822d12a57c", 
					"00f13b0d81783d11221910d3b2090f27918f53957ea98b114af49d151dacbd98" => "879a784c42f298551ce6bc35b7df42021f51ea647080db4b83d947cee7a793d5", 
					"146dc8c182d22edbcc9f2192f3edb621ec03f219bf327c7315ed6f910563a798" => "cbd7faf66c4a1f1dd70790e57adddc157aa5a90e35ab9a32627b901e0d2e561d", 
					"bf1b5c65eee0066748cbd0b77e326d2e5e3cd2786f3fa80c2d063dccd4522b7a" => "e377de4c79ca316e14e6c0dccfbd264f0d4d8db421b8af7d37b4604aafef033d", 
					"0d778f0dea28e91896454793f46dc19c0a456883b218ca2113747744a0dd6ba6" => "374ff6598caba1aa57a9269740c94d88e1847f124e8393a20e0ace4e04f6c857", 
					"0a477dbd4078ac2966eba61c40b1382a8b572b01e39bafeef72681da3a3522fa" => "7ef5ab570cb12616af2f282a0912cc94a2981ad3efec04e68ddbca4f3991b34d", 
					"3acb4e2da64e100830c9912ba3dceb86211cd08eccd7b4739fe1e2cf24e95c68" => "cff6ba19bd1e26fcd3aa27fcba87be6dcb894f67caa0c1e17273a40313729ada", 
					"1febeff1fc10e2e78fe1fa15b5ef3ac6dea400e0795c7e26969a1ebd25d64bfb" => "c1812adc1cab5ae250f487c6dfa9ee9d41d41a38dc205ec842018b4beab79ee1", 
					_ => clientHash, 
				};
			}
			return clientHash;
		}
	}
	internal static class HammerDurabilityFeature
	{
		internal const string HarmonyId = "zari.rules.hammer-durability";

		private static readonly HammerDurabilityTracker<SharedData> Tracker = new HammerDurabilityTracker<SharedData>();

		private static Harmony? harmony;

		internal static bool Ready { get; private set; }

		internal static bool Install(out string reason)
		{
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Expected O, but got Unknown
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Expected O, but got Unknown
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Expected O, but got Unknown
			if (Ready)
			{
				reason = "unbreakable building hammer and tools active";
				return true;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "GetPlaceDurability", new Type[1] { typeof(ItemData) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Humanoid), "DrainEquipedItemDurability", new Type[2]
			{
				typeof(ItemData),
				typeof(float)
			}, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.DeclaredMethod(typeof(ItemDrop), "Awake", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.DeclaredMethod(typeof(ObjectDB), "Awake", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo5 = AccessTools.DeclaredMethod(typeof(ObjectDB), "CopyOtherDB", new Type[1] { typeof(ObjectDB) }, (Type[])null);
			if (methodInfo == null || methodInfo2 == null || methodInfo3 == null || methodInfo4 == null || methodInfo5 == null)
			{
				reason = "missing or changed Player/Humanoid/ObjectDB durability contracts";
				return false;
			}
			try
			{
				harmony = new Harmony("zari.rules.hammer-durability");
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(HammerDurabilityFeature), "GetPlaceDurabilityPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(HammerDurabilityFeature), "DrainEquipedItemDurabilityPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(HammerDurabilityFeature), "ItemAwakePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				HarmonyMethod val = new HarmonyMethod(typeof(HammerDurabilityFeature), "DatabasePostfix", (Type[])null)
				{
					priority = 0
				};
				harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Ready = true;
				reason = "Player.GetPlaceDurability + Humanoid.DrainEquipedItemDurability + ItemDrop/ObjectDB postfixes";
				return true;
			}
			catch (Exception ex)
			{
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				harmony = null;
				Tracker.Restore(SetUseDurability);
				reason = ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		internal static void Apply(string source)
		{
			if (!Ready)
			{
				return;
			}
			RegisterDatabase(ObjectDB.instance);
			ItemDrop[] array = Resources.FindObjectsOfTypeAll<ItemDrop>();
			for (int i = 0; i < array.Length; i++)
			{
				TrackInstance(array[i]);
			}
			bool flag = IsActive();
			int num = Tracker.Apply(flag, SetUseDurability);
			if (flag && (Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).GetInventory() != null)
			{
				foreach (ItemData allItem in ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems())
				{
					if (IsEligible(allItem))
					{
						float maxDurability = allItem.GetMaxDurability();
						if (allItem.m_durability < maxDurability)
						{
							allItem.m_durability = maxDurability;
						}
					}
				}
			}
			Diagnostics.Info("ZR330", $"hammerDurability=applied active={flag} tracked={num} source={source}");
		}

		internal static void ResetSession()
		{
			Tracker.Restore(SetUseDurability);
		}

		internal static void Uninstall()
		{
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			harmony = null;
			Ready = false;
			Tracker.Restore(SetUseDurability);
		}

		internal static bool IsEligible(ItemData? item)
		{
			if (item?.m_shared == null)
			{
				return false;
			}
			string prefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
			string name = item.m_shared.m_name;
			string pieceTableName = (((Object)(object)item.m_shared.m_buildPieces != (Object)null) ? ((Object)item.m_shared.m_buildPieces).name : null);
			bool hasBuildPieces = (Object)(object)item.m_shared.m_buildPieces != (Object)null;
			return HammerDurabilityRules.IsEligible(prefabName, name, pieceTableName, hasBuildPieces, ZariRulesPlugin.Instance.Rules.UnbreakableHammer.Value, ZariRulesPlugin.Instance.Rules.UnbreakableBuildingTools.Value);
		}

		private static bool IsActive()
		{
			if (ZariRulesPlugin.Instance.Rules.IsServerConfigurationReady)
			{
				if (!ZariRulesPlugin.Instance.Rules.UnbreakableHammer.Value)
				{
					return ZariRulesPlugin.Instance.Rules.UnbreakableBuildingTools.Value;
				}
				return true;
			}
			return false;
		}

		private static void SetUseDurability(SharedData shared, bool useDurability)
		{
			shared.m_useDurability = useDurability;
		}

		private static bool GetPlaceDurabilityPrefix(ItemData __0, ref float __result)
		{
			if (IsActive() && IsEligible(__0))
			{
				__result = 0f;
				return false;
			}
			return true;
		}

		private static bool DrainEquipedItemDurabilityPrefix(ItemData __0)
		{
			if (IsActive() && IsEligible(__0))
			{
				return false;
			}
			return true;
		}

		private static void ItemAwakePostfix(ItemDrop __instance)
		{
			SharedData val = TrackInstance(__instance);
			if (val != null && IsActive() && IsEligible(__instance.m_itemData))
			{
				val.m_useDurability = false;
			}
		}

		private static void DatabasePostfix(ObjectDB __instance)
		{
			RegisterDatabase(__instance);
			Tracker.Apply(IsActive(), SetUseDurability);
		}

		private static void RegisterDatabase(ObjectDB? database)
		{
			if (database?.m_items == null)
			{
				return;
			}
			foreach (GameObject item in database.m_items)
			{
				ItemDrop val = (((Object)(object)item != (Object)null) ? item.GetComponent<ItemDrop>() : null);
				SharedData val2 = val?.m_itemData?.m_shared;
				if (val2 != null)
				{
					bool isEligible = IsEligible(val?.m_itemData);
					Tracker.TrackCanonical(val2, val2.m_useDurability, isEligible);
				}
			}
		}

		private static SharedData? TrackInstance(ItemDrop? itemDrop)
		{
			SharedData val = itemDrop?.m_itemData?.m_shared;
			object obj;
			if (itemDrop == null)
			{
				obj = null;
			}
			else
			{
				ItemData itemData = itemDrop.m_itemData;
				if (itemData == null)
				{
					obj = null;
				}
				else
				{
					GameObject dropPrefab = itemData.m_dropPrefab;
					obj = ((dropPrefab != null) ? dropPrefab.GetComponent<ItemDrop>() : null);
				}
			}
			SharedData val2 = ((ItemDrop)(obj?)).m_itemData?.m_shared;
			if (val != null && val2 != null && Tracker.TrackInstance(val, val2))
			{
				return val;
			}
			return null;
		}
	}
	internal static class HammerDurabilityRules
	{
		internal static bool IsHammer(string? prefabName, string? itemName, string? pieceTableName)
		{
			if (!string.IsNullOrEmpty(prefabName) && (string.Equals(prefabName, "Hammer", StringComparison.OrdinalIgnoreCase) || prefabName.IndexOf("Hammer", StringComparison.OrdinalIgnoreCase) >= 0))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(itemName) && (string.Equals(itemName, "$item_hammer", StringComparison.OrdinalIgnoreCase) || itemName.IndexOf("hammer", StringComparison.OrdinalIgnoreCase) >= 0))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(pieceTableName) && (string.Equals(pieceTableName, "_PieceTableHammer", StringComparison.OrdinalIgnoreCase) || pieceTableName.IndexOf("Hammer", StringComparison.OrdinalIgnoreCase) >= 0))
			{
				return true;
			}
			return false;
		}

		internal static bool IsEligible(string? prefabName, string? itemName, string? pieceTableName, bool hasBuildPieces, bool unbreakableHammer, bool unbreakableBuildingTools)
		{
			if (!hasBuildPieces)
			{
				return false;
			}
			if (unbreakableBuildingTools)
			{
				return true;
			}
			if (unbreakableHammer && IsHammer(prefabName, itemName, pieceTableName))
			{
				return true;
			}
			return false;
		}
	}
	internal sealed class HammerDurabilityTracker<T> where T : class
	{
		private sealed class CanonicalEntry
		{
			internal T Canonical { get; }

			internal CanonicalEntry(T canonical)
			{
				Canonical = canonical;
			}
		}

		private sealed class ReferenceComparer : IEqualityComparer<T>
		{
			internal static readonly ReferenceComparer Instance = new ReferenceComparer();

			public bool Equals(T? x, T? y)
			{
				return x == y;
			}

			public int GetHashCode(T obj)
			{
				return RuntimeHelpers.GetHashCode(obj);
			}
		}

		private readonly Dictionary<T, bool> baselines = new Dictionary<T, bool>(ReferenceComparer.Instance);

		private readonly Dictionary<T, bool> eligibility = new Dictionary<T, bool>(ReferenceComparer.Instance);

		private readonly List<WeakReference<T>> instances = new List<WeakReference<T>>();

		private ConditionalWeakTable<T, CanonicalEntry> entriesByInstance = new ConditionalWeakTable<T, CanonicalEntry>();

		internal void TrackCanonical(T canonical, bool baselineUseDurability, bool isEligible)
		{
			eligibility[canonical] = isEligible;
			if (!baselines.ContainsKey(canonical))
			{
				baselines.Add(canonical, baselineUseDurability);
			}
			TrackInstance(canonical, canonical);
		}

		internal bool TrackInstance(T instance, T canonical)
		{
			if (!baselines.ContainsKey(canonical))
			{
				return false;
			}
			if (entriesByInstance.TryGetValue(instance, out CanonicalEntry _))
			{
				return true;
			}
			entriesByInstance.Add(instance, new CanonicalEntry(canonical));
			instances.Add(new WeakReference<T>(instance));
			return true;
		}

		internal int Apply(bool active, Action<T, bool> setUseDurability)
		{
			int num = 0;
			bool value3 = default(bool);
			for (int num2 = instances.Count - 1; num2 >= 0; num2--)
			{
				CanonicalEntry value;
				bool value2;
				if (!instances[num2].TryGetTarget(out var target))
				{
					instances.RemoveAt(num2);
				}
				else if (entriesByInstance.TryGetValue(target, out value) && baselines.TryGetValue(value.Canonical, out value2))
				{
					if (active && eligibility.TryGetValue(value.Canonical, out value3) && value3)
					{
						setUseDurability(target, arg2: false);
						num++;
					}
					else
					{
						setUseDurability(target, value2);
					}
				}
			}
			return num;
		}

		internal void Restore(Action<T, bool> setUseDurability)
		{
			Apply(active: false, setUseDurability);
			baselines.Clear();
			eligibility.Clear();
			instances.Clear();
			entriesByInstance = new ConditionalWeakTable<T, CanonicalEntry>();
		}
	}
	internal static class HealthHudFeature
	{
		private static readonly Color NormalColor = new Color(0.16f, 0.88f, 0.67f, 1f);

		private static readonly Color WoundedColor = new Color(0.92f, 0.68f, 0.2f, 1f);

		private static readonly Color CriticalColor = new Color(1f, 0.18f, 0.16f, 1f);

		private static GameObject? canvasObject;

		private static GameObject? panelObject;

		private static RectTransform? panelRect;

		private static CanvasGroup? panelGroup;

		private static Image? fillImage;

		private static TMP_Text? valueText;

		private static TMP_Text? statusText;

		private static Image? edgeVignetteImage;

		private static Sprite? edgeVignetteSprite;

		private static Image[] edgeImages = Array.Empty<Image>();

		private static Hud? ownerHud;

		private static HealthHudSnapshot lastSnapshot;

		private static string lastLanguage = string.Empty;

		private static float lastPositionX = float.NaN;

		private static float lastPositionY = float.NaN;

		private static float lastScale = float.NaN;

		internal static bool Install(out string reason)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			ResetSession();
			if ((int)SystemInfo.graphicsDeviceType == 4)
			{
				reason = "headless graphics device; UI disabled";
				return true;
			}
			reason = "independent overlay; no Hud or BetterUI patches";
			return true;
		}

		internal static void Tick()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)SystemInfo.graphicsDeviceType == 4)
			{
				return;
			}
			Hud instance = Hud.instance;
			Player localPlayer = Player.m_localPlayer;
			if (!ZariRulesPlugin.Instance.Rules.HealthIndicatorEnabled.Value || !((Object)(object)instance != (Object)null) || !((Object)(object)localPlayer != (Object)null) || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || !instance.IsVisible() || Hud.IsUserHidden() || InventoryGui.IsVisible() || Menu.IsVisible())
			{
				SetVisible(visible: false);
				return;
			}
			if ((Object)(object)canvasObject == (Object)null || (Object)(object)ownerHud != (Object)(object)instance)
			{
				DestroyUi();
				if (!CreateUi(instance))
				{
					return;
				}
			}
			HealthHudSnapshot healthHudSnapshot = HealthHudRules.Evaluate(((Character)localPlayer).GetHealth(), ((Character)localPlayer).GetMaxHealth());
			if (!healthHudSnapshot.IsValid)
			{
				SetVisible(visible: false);
				return;
			}
			UpdateLayout();
			Localization instance2 = Localization.instance;
			string text = ((instance2 != null) ? instance2.GetSelectedLanguage() : null) ?? "English";
			if (!SameHealth(healthHudSnapshot, lastSnapshot) || !string.Equals(text, lastLanguage, StringComparison.Ordinal))
			{
				UpdateContent(healthHudSnapshot, text);
				lastSnapshot = healthHudSnapshot;
				lastLanguage = text;
			}
			bool flag = healthHudSnapshot.State == HealthHudState.Critical && ZariRulesPlugin.Instance.Rules.HealthIndicatorPulse.Value;
			float num = (flag ? (0.5f + 0.5f * Mathf.Sin(Time.unscaledTime * (float)Math.PI * 2.5f)) : 1f);
			panelGroup.alpha = (flag ? (0.78f + 0.22f * num) : 1f);
			SetEdges(healthHudSnapshot.State, num);
			SetVisible(visible: true);
		}

		internal static void ResetSession()
		{
			DestroyUi();
			lastSnapshot = default(HealthHudSnapshot);
			lastLanguage = string.Empty;
		}

		internal static void Uninstall()
		{
			ResetSession();
		}

		private static bool SameHealth(HealthHudSnapshot left, HealthHudSnapshot right)
		{
			if (left.IsValid == right.IsValid && left.Current == right.Current && left.Maximum == right.Maximum && left.Percent == right.Percent)
			{
				return left.State == right.State;
			}
			return false;
		}

		private static bool CreateUi(Hud hud)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: 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_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hud.m_healthText == (Object)null || (Object)(object)hud.m_healthText.font == (Object)null)
			{
				return false;
			}
			ownerHud = hud;
			canvasObject = new GameObject("ZariRules.HealthHud", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(CanvasGroup)
			});
			Object.DontDestroyOnLoad((Object)(object)canvasObject);
			Canvas component = canvasObject.GetComponent<Canvas>();
			component.renderMode = (RenderMode)0;
			component.sortingOrder = 50;
			CanvasScaler component2 = canvasObject.GetComponent<CanvasScaler>();
			component2.uiScaleMode = (ScaleMode)1;
			component2.referenceResolution = new Vector2(1920f, 1080f);
			component2.matchWidthOrHeight = 0.5f;
			CanvasGroup component3 = canvasObject.GetComponent<CanvasGroup>();
			component3.interactable = false;
			component3.blocksRaycasts = false;
			panelObject = ((Component)CreateImage("Panel", canvasObject.transform, new Color(0.025f, 0.035f, 0.045f, 0.92f), out Image image)).gameObject;
			((Graphic)image).raycastTarget = false;
			panelRect = panelObject.GetComponent<RectTransform>();
			panelRect.sizeDelta = new Vector2(280f, 44f);
			panelGroup = panelObject.AddComponent<CanvasGroup>();
			panelGroup.interactable = false;
			panelGroup.blocksRaycasts = false;
			Image image2;
			RectTransform val = CreateImage("Track", (Transform)(object)panelRect, new Color(0.12f, 0.14f, 0.16f, 1f), out image2);
			val.anchorMin = Vector2.zero;
			val.anchorMax = new Vector2(1f, 0f);
			val.offsetMin = new Vector2(10f, 6f);
			val.offsetMax = new Vector2(-10f, 12f);
			Image image3;
			RectTransform obj = CreateImage("Fill", (Transform)(object)val, NormalColor, out image3);
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			fillImage = image3;
			valueText = CreateText("Value", (Transform)(object)panelRect, hud.m_healthText.font, 17f, (TextAlignmentOptions)513);
			valueText.enableAutoSizing = true;
			valueText.fontSizeMin = 13f;
			valueText.fontSizeMax = 17f;
			SetRect(valueText.rectTransform, new Vector2(10f, 14f), new Vector2(-95f, -3f));
			statusText = CreateText("Status", (Transform)(object)panelRect, hud.m_healthText.font, 12f, (TextAlignmentOptions)516);
			SetRect(statusText.rectTransform, new Vector2(175f, 14f), new Vector2(-10f, -4f));
			edgeVignetteSprite = CreateVignetteSprite();
			RectTransform obj2 = CreateImage("EdgeVignette", canvasObject.transform, Color.clear, out edgeVignetteImage);
			obj2.anchorMin = Vector2.zero;
			obj2.anchorMax = Vector2.one;
			obj2.offsetMin = Vector2.zero;
			obj2.offsetMax = Vector2.zero;
			edgeVignetteImage.sprite = edgeVignetteSprite;
			((Graphic)edgeVignetteImage).raycastTarget = false;
			edgeImages = CreateEdges(canvasObject.transform);
			UpdateLayout();
			return true;
		}

		private static void UpdateContent(HealthHudSnapshot snapshot, string language)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			Color color = (Color)(snapshot.State switch
			{
				HealthHudState.Critical => CriticalColor, 
				HealthHudState.Wounded => WoundedColor, 
				_ => NormalColor, 
			});
			bool flag = language.StartsWith("Russian", StringComparison.OrdinalIgnoreCase) || language.StartsWith("Рус", StringComparison.OrdinalIgnoreCase);
			string text = snapshot.State switch
			{
				HealthHudState.Critical => flag ? "КРИТИЧНО" : "CRITICAL", 
				HealthHudState.Wounded => flag ? "РАНЕН" : "WOUNDED", 
				_ => flag ? "НОРМА" : "NORMAL", 
			};
			valueText.text = $"{Mathf.CeilToInt(snapshot.Current)} / {Mathf.CeilToInt(snapshot.Maximum)}  ·  {snapshot.Percent}%";
			((Graphic)valueText).color = Color.white;
			statusText.text = text;
			((Graphic)statusText).color = color;
			((Graphic)fillImage).color = color;
			((Graphic)fillImage).rectTransform.anchorMax = new Vector2(snapshot.Fraction, 1f);
		}

		private static void UpdateLayout()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Clamp01(ZariRulesPlugin.Instance.Rules.HealthIndicatorPositionX.Value);
			float num2 = Mathf.Clamp01(ZariRulesPlugin.Instance.Rules.HealthIndicatorPositionY.Value);
			float num3 = Mathf.Clamp(ZariRulesPlugin.Instance.Rules.HealthIndicatorScale.Value, 0.5f, 2f);
			if (num != lastPositionX || num2 != lastPositionY || num3 != lastScale)
			{
				RectTransform? obj = panelRect;
				RectTransform? obj2 = panelRect;
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(num, num2);
				obj2.anchorMax = val;
				obj.anchorMin = val;
				panelRect.pivot = new Vector2(0.5f, 0.5f);
				panelRect.anchoredPosition = Vector2.zero;
				((Transform)panelRect).localScale = Vector3.one * num3;
				lastPositionX = num;
				lastPositionY = num2;
				lastScale = num3;
			}
		}

		private static RectTransform CreateImage(string name, Transform parent, Color color, out Image image)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			val.transform.SetParent(parent, false);
			image = val.GetComponent<Image>();
			((Graphic)image).color = color;
			((Graphic)image).raycastTarget = false;
			return val.GetComponent<RectTransform>();
		}

		private static TMP_Text CreateText(string name, Transform parent, TMP_FontAsset font, float size, TextAlignmentOptions alignment)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer)
			});
			val.SetActive(false);
			val.transform.SetParent(parent, false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val2).font = font;
			if ((Object)(object)font != (Object)null && (Object)(object)((TMP_Asset)font).material != (Object)null)
			{
				((TMP_Text)val2).fontSharedMaterial = ((TMP_Asset)font).material;
			}
			((TMP_Text)val2).fontSize = size;
			((TMP_Text)val2).alignment = alignment;
			((Graphic)val2).raycastTarget = false;
			((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
			val.SetActive(true);
			return (TMP_Text)(object)val2;
		}

		private static Image[] CreateEdges(Transform parent)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: 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_00df: 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_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			Image[] array = (Image[])(object)new Image[4];
			for (int i = 0; i < array.Length; i++)
			{
				RectTransform val = CreateImage("CriticalEdge" + i, parent, new Color(1f, 0.05f, 0.03f, 0f), out array[i]);
				RectTransform val2 = val;
				val2.anchorMin = (Vector2)(i switch
				{
					0 => new Vector2(0f, 1f), 
					1 => Vector2.zero, 
					2 => Vector2.zero, 
					_ => new Vector2(1f, 0f), 
				});
				val2 = val;
				val2.anchorMax = (Vector2)(i switch
				{
					0 => Vector2.one, 
					1 => new Vector2(1f, 0f), 
					2 => new Vector2(0f, 1f), 
					_ => Vector2.one, 
				});
				val.pivot = val.anchorMin;
				val.anchoredPosition = Vector2.zero;
				val.sizeDelta = ((i < 2) ? new Vector2(0f, 3f) : new Vector2(3f, 0f));
			}
			return array;
		}

		private static Sprite CreateVignetteSprite()
		{
			//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_0016: 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_0025: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: 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_00dc: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false)
			{
				name = "ZariRules_EdgeGlow",
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1
			};
			Color32[] array = (Color32[])(object)new Color32[4096];
			for (int i = 0; i < 64; i++)
			{
				float num = (float)i / 63f;
				float num2 = Mathf.Min(num, 1f - num) * 2f;
				for (int j = 0; j < 64; j++)
				{
					float num3 = (float)j / 63f;
					float num4 = Mathf.Min(num3, 1f - num3) * 2f;
					float num5 = 1f - Mathf.Clamp01(Mathf.Min(num4, num2));
					byte b = (byte)(Mathf.Clamp01(Mathf.Pow(Mathf.SmoothStep(0f, 1f, num5), 2f)) * 255f);
					array[i * 64 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b);
				}
			}
			val.SetPixels32(array);
			val.Apply();
			return Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f));
		}

		private static void SetEdges(HealthHudState state, float pulseFactor)
		{
			//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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			Color clear = default(Color);
			float num;
			float num2;
			switch (state)
			{
			case HealthHudState.Critical:
				((Color)(ref clear))..ctor(1f, 0.12f, 0.1f, 1f);
				num = 0.22f + 0.16f * pulseFactor;
				num2 = 0.8f;
				break;
			case HealthHudState.Wounded:
				((Color)(ref clear))..ctor(0.95f, 0.65f, 0.15f, 1f);
				num = 0.04f;
				num2 = 0.4f;
				break;
			default:
				clear = Color.clear;
				num = 0f;
				num2 = 0f;
				break;
			}
			clear.a = num;
			if ((Object)(object)edgeVignetteImage != (Object)null)
			{
				((Graphic)edgeVignetteImage).color = clear;
			}
			Color color = clear;
			color.a = num * num2;
			Image[] array = edgeImages;
			for (int i = 0; i < array.Length; i++)
			{
				((Graphic)array[i]).color = color;
			}
		}

		private static void SetRect(RectTransform rect, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_0001: 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_0017: 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)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = offsetMin;
			rect.offsetMax = offsetMax;
		}

		private static void SetVisible(bool visible)
		{
			if ((Object)(object)canvasObject != (Object)null && canvasObject.activeSelf != visible)
			{
				canvasObject.SetActive(visible);
			}
		}

		private static void DestroyUi()
		{
			if ((Object)(object)edgeVignetteSprite != (Object)null)
			{
				if ((Object)(object)edgeVignetteSprite.texture != (Object)null)
				{
					Object.Destroy((Object)(object)edgeVignetteSprite.texture);
				}
				Object.Destroy((Object)(object)edgeVignetteSprite);
				edgeVignetteSprite = null;
			}
			edgeVignetteImage = null;
			if ((Object)(object)canvasObject != (Object)null)
			{
				Object.Destroy((Object)(object)canvasObject);
			}
			canvasObject = null;
			panelObject = null;
			panelRect = null;
			panelGroup = null;
			fillImage = null;
			valueText = null;
			statusText = null;
			edgeImages = Array.Empty<Image>();
			ownerHud = null;
			lastSnapshot = default(HealthHudSnapshot);
			lastLanguage = string.Empty;
			lastPositionX = float.NaN;
			lastPositionY = float.NaN;
			lastScale = float.NaN;
		}
	}
	internal enum HealthHudState
	{
		Normal,
		Wounded,
		Critical
	}
	internal readonly struct HealthHudSnapshot
	{
		internal bool IsValid { get; }

		internal float Current { get; }

		internal float Maximum { get; }

		internal float Fraction { get; }

		internal int Percent { get; }

		internal HealthHudState State { get; }

		internal HealthHudSnapshot(bool isValid, float current, float maximum, float fraction, int percent, HealthHudState state)
		{
			IsValid = isValid;
			Current = current;
			Maximum = maximum;
			Fraction = fraction;
			Percent = percent;
			State = state;
		}
	}
	internal static class HealthHudRules
	{
		internal static HealthHudSnapshot Evaluate(float current, float maximum)
		{
			if (float.IsNaN(current) || float.IsInfinity(current) || float.IsNaN(maximum) || float.IsInfinity(maximum) || maximum <= 0f)
			{
				return default(HealthHudSnapshot);
			}
			float num = Math.Min(Math.Max(current, 0f), maximum);
			float num2 = num / maximum;
			HealthHudState state = ((num2 <= 0.25f) ? HealthHudState.Critical : ((num2 <= 0.5f) ? HealthHudState.Wounded : HealthHudState.Normal));
			return new HealthHudSnapshot(isValid: true, num, maximum, num2, (int)Math.Round(num2 * 100f, MidpointRounding.AwayFromZero), state);
		}
	}
	internal enum MapPinCategory : byte
	{
		Boss,
		Trader,
		Dungeon
	}
	internal readonly struct MapPinData : IEquatable<MapPinData>
	{
		internal string Prefab { get; }

		internal float X { get; }

		internal float Y { get; }

		internal float Z { get; }

		internal MapPinCategory Category { get; }

		internal MapPinData(string prefab, float x, float y, float z, MapPinCategory category)
		{
			Prefab = prefab ?? string.Empty;
			X = x;
			Y = y;
			Z = z;
			Category = category;
		}

		public bool Equals(MapPinData other)
		{
			if (Prefab == other.Prefab && X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z))
			{
				return Category == other.Category;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is MapPinData other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (((((((Prefab.GetHashCode() * 397) ^ X.GetHashCode()) * 397) ^ Y.GetHashCode()) * 397) ^ Z.GetHashCode()) * 397) ^ (int)Category;
		}
	}
	internal static class MapPinRules
	{
		internal const int MaxLocations = 2048;

		internal const int MaxPayloadBytes = 262144;

		internal static bool TryClassify(string prefab, out MapPinCategory category, out string token, out string nameEn, out string nameRu)
		{
			category = MapPinCategory.Dungeon;
			token = string.Empty;
			nameEn = string.Empty;
			nameRu = string.Empty;
			if (string.IsNullOrEmpty(prefab))
			{
				return false;
			}
			if (string.Equals(prefab, "Eikthyrnir", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Boss;
				token = "$enemy_eikthyr";
				nameEn = "Eikthyr";
				nameRu = "Эйктюр";
				return true;
			}
			if (string.Equals(prefab, "GDKing", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Boss;
				token = "$enemy_gdking";
				nameEn = "The Elder";
				nameRu = "Древний";
				return true;
			}
			if (string.Equals(prefab, "Bonemass", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Boss;
				token = "$enemy_bonemass";
				nameEn = "Bonemass";
				nameRu = "Масса Костей";
				return true;
			}
			if (string.Equals(prefab, "Dragonqueen", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Boss;
				token = "$enemy_dragonqueen";
				nameEn = "Moder";
				nameRu = "Моудер";
				return true;
			}
			if (string.Equals(prefab, "GoblinKing", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Boss;
				token = "$enemy_goblinking";
				nameEn = "Yagluth";
				nameRu = "Яглут";
				return true;
			}
			if (string.Equals(prefab, "Mistlands_DvergrBossEntrance1", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Boss;
				token = "$enemy_seekerqueen";
				nameEn = "The Queen";
				nameRu = "Королева";
				return true;
			}
			if (string.Equals(prefab, "FaderLocation", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Boss;
				token = "$enemy_fader";
				nameEn = "Fader";
				nameRu = "Фейдер";
				return true;
			}
			if (string.Equals(prefab, "Vendor_BlackForest", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Trader;
				token = "$npc_haldor";
				nameEn = "Haldor";
				nameRu = "Хальдор";
				return true;
			}
			if (string.Equals(prefab, "Hildir_camp", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Trader;
				token = "$npc_hildir";
				nameEn = "Hildir";
				nameRu = "Хильдир";
				return true;
			}
			if (string.Equals(prefab, "BogWitch_Camp", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Trader;
				token = "$npc_bogwitch";
				nameEn = "Bog Witch";
				nameRu = "Болотная ведьма";
				return true;
			}
			if (prefab.StartsWith("ForestCryptHildir", StringComparison.OrdinalIgnoreCase) || prefab.StartsWith("CryptHildir", StringComparison.OrdinalIgnoreCase) || prefab.StartsWith("Hildir_crypt", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_hildir_crypt";
				nameEn = "Smoldering Tomb";
				nameRu = "Тлеющая гробница";
				return true;
			}
			if (prefab.StartsWith("CaveHildir", StringComparison.OrdinalIgnoreCase) || prefab.StartsWith("Hildir_cave", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_hildir_cave";
				nameEn = "Howling Cavern";
				nameRu = "Воющая пещера";
				return true;
			}
			if (prefab.StartsWith("PlainsFortHildir", StringComparison.OrdinalIgnoreCase) || prefab.StartsWith("Hildir_plainsfortress", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_hildir_tower";
				nameEn = "Sealed Tower";
				nameRu = "Запечатанная башня";
				return true;
			}
			if (prefab.StartsWith("SunkenCrypt", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_sunkencrypt";
				nameEn = "Sunken Crypt";
				nameRu = "Затонувшие склепы";
				return true;
			}
			if (prefab.StartsWith("Crypt", StringComparison.OrdinalIgnoreCase) || string.Equals(prefab, "ForestCrypt", StringComparison.OrdinalIgnoreCase) || string.Equals(prefab, "HalfBurried_ForestCrypt", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_forestcrypt";
				nameEn = "Burial Chambers";
				nameRu = "Погребальные склепы";
				return true;
			}
			if (prefab.StartsWith("TrollCave", StringComparison.OrdinalIgnoreCase) || string.Equals(prefab, "BearCave", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_trollcave";
				nameEn = "Troll Cave";
				nameRu = "Пещера тролля";
				return true;
			}
			if (prefab.StartsWith("MountainCave", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_mountaincave";
				nameEn = "Frost Cave";
				nameRu = "Морозная пещера";
				return true;
			}
			if (prefab.StartsWith("Mistlands_DvergrTownEntrance", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_infestedmine";
				nameEn = "Infested Mine";
				nameRu = "Зараженная шахта";
				return true;
			}
			if (prefab.StartsWith("CharredFortress", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_charredfortress";
				nameEn = "Charred Fortress";
				nameRu = "Обугленная крепость";
				return true;
			}
			if (prefab.StartsWith("MorgenHole", StringComparison.OrdinalIgnoreCase))
			{
				category = MapPinCategory.Dungeon;
				token = "$location_morgenhole";
				nameEn = "Putrid Hole";
				nameRu = "Зловонная яма";
				return true;
			}
			return false;
		}

		internal static byte[] Encode(IReadOnlyList<MapPinData> locations)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			int num = Math.Min(locations.Count, 2048);
			binaryWriter.Write(num);
			for (int i = 0; i < num; i++)
			{
				MapPinData mapPinData = locations[i];
				binaryWriter.Write((byte)mapPinData.Category);
				binaryWriter.Write(mapPinData.Prefab ?? string.Empty);
				binaryWriter.Write(mapPinData.X);
				binaryWriter.Write(mapPinData.Y);
				binaryWriter.Write(mapPinData.Z);
			}
			binaryWriter.Flush();
			return memoryStream.ToArray();
		}

		internal static List<MapPinData> Decode(byte[] data)
		{
			if (data == null || data.Length < 4 || data.Length > 262144)
			{
				throw new InvalidDataException("Invalid map pins payload size.");
			}
			using MemoryStream input = new MemoryStream(data, writable: false);
			using BinaryReader binaryReader = new BinaryReader(input);
			int num = binaryReader.ReadInt32();
			if (num < 0 || num > 2048)
			{
				throw new InvalidDataException("Invalid map pin count.");
			}
			List<MapPinData> list = new List<MapPinData>(num);
			for (int i = 0; i < num; i++)
			{
				byte category = binaryReader.ReadByte();
				string prefab = binaryReader.ReadString();
				float x = binaryReader.ReadSingle();
				float y = binaryReader.ReadSingle();
				float z = binaryReader.ReadSingle();
				list.Add(new MapPinData(prefab, x, y, z, (MapPinCategory)category));
			}
			return list;
		}
	}
	internal static class MapPinsFeature
	{
		internal const string RpcName = "ZariRules_MapPins_V1";

		internal const string RequestRpcName = "ZariRules_MapPins_Request_V1";

		private const float RequestIntervalSeconds = 5f;

		private static readonly HashSet<ZRpc> Registered = new HashSet<ZRpc>();

		private static readonly Dictionary<ZRpc, float> NextResponseAt = new Dictionary<ZRpc, float>();

		private static readonly List<MapPinData> ServerLocations = new List<MapPinData>();

		private static readonly List<MapPinData> ClientLocations = new List<MapPinData>();

		private static readonly List<PinData> ActivePins = new List<PinData>();

		private static FieldInfo? locationInstancesField;

		private static FieldInfo? locationsGeneratedField;

		private static FieldInfo? hasGeneratedField;

		private static FieldInfo? pinsField;

		private static bool serverLocationsCached;

		private static bool pinsApplied;

		private static bool locationsReceived;

		private static float nextRequestAt;

		internal static bool Install(out string reason)
		{
			locationInstancesField = typeof(ZoneSystem).GetField("m_locationInstances", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			locationsGeneratedField = typeof(ZoneSystem).GetField("m_locationsGenerated", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			hasGeneratedField = typeof(Minimap).GetField("m_hasGenerated", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			pinsField = typeof(Minimap).GetField("m_pins", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (locationInstancesField == null || locationsGeneratedField == null || hasGeneratedField == null || pinsField == null)
			{
				reason = "missing or changed ZoneSystem/Minimap location contracts";
				return false;
			}
			reason = "server-authoritative non-persistent map pins for bosses, traders, and dungeons";
			return true;
		}

		internal static void Tick()
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				RegisterPeers(instance);
				if (instance.IsServer())
				{
					EnsureServerLocations();
				}
				else
				{
					RequestLocations(instance);
				}
				UpdateClientPins();
			}
		}

		internal static void ReapplyPins()
		{
			pinsApplied = false;
			UpdateClientPins();
		}

		internal static void ResetSession()
		{
			foreach (ZRpc item in Registered)
			{
				item.Unregister("ZariRules_MapPins_V1");
				item.Unregister("ZariRules_MapPins_Request_V1");
			}
			Registered.Clear();
			NextResponseAt.Clear();
			ServerLocations.Clear();
			ClientLocations.Clear();
			serverLocationsCached = false;
			ClearActivePins();
			pinsApplied = false;
			locationsReceived = false;
			nextRequestAt = 0f;
		}

		internal static void Uninstall()
		{
			ResetSession();
			locationInstancesField = null;
			locationsGeneratedField = null;
			hasGeneratedField = null;
			pinsField = null;
		}

		internal static bool TryClassify(string prefab, out MapPinCategory category, out string token, out string nameEn, out string nameRu)
		{
			return MapPinRules.TryClassify(prefab, out category, out token, out nameEn, out nameRu);
		}

		internal static byte[] Encode(IReadOnlyList<MapPinData> locations)
		{
			return MapPinRules.Encode(locations);
		}

		internal static List<MapPinData> Decode(byte[] data)
		{
			return MapPinRules.Decode(data);
		}

		private static void EnsureServerLocations()
		{
			//IL_00aa: 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_00b1: 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_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: 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_0117: 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_0123: Unknown result type (might be due to invalid IL or missing references)
			if (serverLocationsCached)
			{
				return;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null || locationsGeneratedField == null || locationInstancesField == null)
			{
				return;
			}
			object value = locationsGeneratedField.GetValue(instance);
			if (!(value is bool) || !(bool)value)
			{
				return;
			}
			ServerLocations.Clear();
			object? value2 = locationInstancesField.GetValue(instance);
			IEnumerable enumerable = value2 as IEnumerable;
			if (value2 is IDictionary dictionary)
			{
				enumerable = dictionary.Values;
			}
			if (enumerable != null)
			{
				foreach (object item in enumerable)
				{
					if (item is LocationInstance val && val.m_location != null)
					{
						string text = val.m_location.m_prefabName ?? string.Empty;
						if (string.IsNullOrEmpty(text))
						{
							text = val.m_location.m_name ?? string.Empty;
						}
						if (TryClassify(text, out MapPinCategory category, out string _, out string _, out string _))
						{
							ServerLocations.Add(new MapPinData(text, val.m_position.x, val.m_position.y, val.m_position.z, category));
						}
					}
				}
			}
			ServerLocations.Sort((MapPinData a, MapPinData b) => a.Category.CompareTo(b.Category));
			serverLocationsCached = true;
			Diagnostics.Info("ZR115", $"mapPins=cached count={ServerLocations.Count}");
		}

		private static void RegisterPeers(ZNet net)
		{
			HashSet<ZRpc> current = new HashSet<ZRpc>();
			if (net.IsServer())
			{
				foreach (ZNetPeer peer in net.GetPeers())
				{
					if (peer != null && peer.IsReady())
					{
						current.Add(peer.m_rpc);
						Register(peer.m_rpc);
					}
				}
			}
			else
			{
				ZNetPeer serverPeer = net.GetServerPeer();
				if (serverPeer != null && serverPeer.IsReady())
				{
					current.Add(serverPeer.m_rpc);
					Register(serverPeer.m_rpc);
				}
			}
			Registered.RemoveWhere(delegate(ZRpc rpc)
			{
				if (current.Contains(rpc) && rpc.IsConnected())
				{
					return false;
				}
				rpc.Unregister("ZariRules_MapPins_V1");
				rpc.Unregister("ZariRules_MapPins_Request_V1");
				NextResponseAt.Remove(rpc);
				if (!net.IsServer())
				{
					locationsReceived = false;
					nextRequestAt = 0f;
					ClientLocations.Clear();
					ClearActivePins();
					pinsApplied = false;
				}
				return true;
			});
		}

		private static void Register(ZRpc rpc)
		{
			if (rpc != null && Registered.Add(rpc))
			{
				rpc.Register<ZPackage>("ZariRules_MapPins_V1", (Action<ZRpc, ZPackage>)OnMapPins);
				rpc.Register<ZPackage>("ZariRules_MapPins_Request_V1", (Action<ZRpc, ZPackage>)OnRequest);
			}
		}

		private static void RequestLocations(ZNet net)
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			if (!locationsReceived && !(Time.unscaledTime < nextRequestAt) && ZariRulesPlugin.Instance.Rules.IsServerConfigurationReady && ZariRulesPlugin.Instance.Rules.RevealMapPins.Value)
			{
				ZNetPeer serverPeer = net.GetServerPeer();
				if (serverPeer != null && serverPeer.IsReady() && Registered.Contains(serverPeer.m_rpc) && serverPeer.m_rpc.IsConnected())
				{
					nextRequestAt = Time.unscaledTime + 5f;
					serverPeer.m_rpc.Invoke("ZariRules_MapPins_Request_V1", new object[1] { (object)new ZPackage() });
				}
			}
		}

		private static void OnRequest(ZRpc rpc, ZPackage package)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance == (Object)null) && instance.IsServer() && package != null && package.Size() == 0 && serverLocationsCached && IsCurrentReadyPeer(instance, rpc) && ZariRulesPlugin.Instance.Rules.IsServerConfigurationReady && ZariRulesPlugin.Instance.Rules.RevealMapPins.Value)
			{
				float unscaledTime = Time.unscaledTime;
				if (!NextResponseAt.TryGetValue(rpc, out var value) || !(unscaledTime < value))
				{
					NextResponseAt[rpc] = unscaledTime + 5f;
					rpc.Invoke("ZariRules_MapPins_V1", new object[1] { (object)new ZPackage(Encode(ServerLocations)) });
				}
			}
		}

		private static bool IsCurrentReadyPeer(ZNet net, ZRpc rpc)
		{
			if (!Registered.Contains(rpc) || !rpc.IsConnected())
			{
				return false;
			}
			if (!net.IsServer())
			{
				ZNetPeer serverPeer = net.GetServerPeer();
				if (serverPeer != null && serverPeer.IsReady())
				{
					return serverPeer.m_rpc == rpc;
				}
				return false;
			}
			foreach (ZNetPeer peer in net.GetPeers())
			{
				if (peer != null && peer.IsReady() && peer.m_rpc == rpc)
				{
					return true;
				}
			}
			return false;
		}

		private static void OnMapPins(ZRpc rpc, ZPackage package)
		{
			try
			{
				if (package == null || package.Size() > 262144)
				{
					return;
				}
				ZNet instance = ZNet.instance;
				if (!((Object)(object)instance == (Object)null) && !instance.IsServer() && IsCurrentReadyPeer(instance, rpc))
				{
					ZNetPeer serverPeer = instance.GetServerPeer();
					if (serverPeer != null && serverPeer.m_rpc == rpc)
					{
						List<MapPinData> collection = Decode(package.GetArray());
						ClientLocations.Clear();
						ClientLocations.AddRange(collection);
						locationsReceived = true;
						pinsApplied = false;
						Diagnostics.Info("ZR116", $"mapPins=received count={ClientLocations.Count}");
					}
				}
			}
			catch (Exception ex)
			{
				ZariRulesPlugin.LogError("ZR917", "feature=MapPins error=" + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private static void UpdateClientPins()
		{
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: 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)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || hasGeneratedField == null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			object value = hasGeneratedField.GetValue(instance);
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			if (((uint)num & (flag ? 1u : 0u)) == 0 || !ZariRulesPlugin.Instance.Rules.IsServerConfigurationReady || !ZariRulesPlugin.Instance.Rules.RevealMapPins.Value)
			{
				if (ActivePins.Count > 0)
				{
					ClearActivePins();
				}
				pinsApplied = false;
			}
			else
			{
				if (pinsApplied)
				{
					return;
				}
				ZNet instance2 = ZNet.instance;
				IReadOnlyList<MapPinData> readOnlyList = ((instance2 != null && instance2.IsServer()) ? ServerLocations : ClientLocations);
				if (readOnlyList.Count == 0)
				{
					return;
				}
				ClearActivePins();
				Localization instance3 = Localization.instance;
				string text = ((instance3 != null) ? instance3.GetSelectedLanguage() : null) ?? "English";
				bool russian = text.StartsWith("Russian", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Рус", StringComparison.OrdinalIgnoreCase);
				Vector3 val = default(Vector3);
				foreach (MapPinData item in readOnlyList)
				{
					((Vector3)(ref val))..ctor(item.X, item.Y, item.Z);
					if (HasNearbyPin(instance, val, 25f) || !TryClassify(item.Prefab, out MapPinCategory category, out string token, out string nameEn, out string nameRu))
					{
						continue;
					}
					string text2 = LocalizePinName(token, nameEn, nameRu, russian);
					Sprite val2 = null;
					if (category == MapPinCategory.Trader && instance.m_locationIcons != null)
					{
						for (int i = 0; i < instance.m_locationIcons.Count; i++)
						{
							LocationSpriteData val3 = instance.m_locationIcons[i];
							if (string.Equals(val3.m_name, item.Prefab, StringComparison.OrdinalIgnoreCase))
							{
								val2 = val3.m_icon;
								break;
							}
						}
					}
					PinType val4 = (PinType)(category switch
					{
						MapPinCategory.Boss => 9, 
						MapPinCategory.Trader => ((Object)(object)val2 != (Object)null) ? 8 : 3, 
						_ => 3, 
					});
					PinData val5 = instance.AddPin(val, val4, text2, false, false, 0L, default(PlatformUserID));
					if ((Object)(object)val2 != (Object)null)
					{
						val5.m_icon = val2;
						val5.m_doubleSize = true;
					}
					ActivePins.Add(val5);
				}
				pinsApplied = true;
				Diagnostics.Info("ZR117", $"mapPins=applied count={ActivePins.Count}");
			}
		}

		private static string LocalizePinName(string token, string defaultEn, string defaultRu, bool russian)
		{
			if (Localization.instance != null && !string.IsNullOrEmpty(token))
			{
				string text = Localization.instance.Localize(token);
				if (!string.IsNullOrEmpty(text) && text != token && !text.StartsWith("$", StringComparison.Ordinal))
				{
					return text;
				}
			}
			if (!russian)
			{
				return defaultEn;
			}
			return defaultRu;
		}

		private static bool HasNearbyPin(Minimap minimap, Vector3 position, float radius)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			float num = radius * radius;
			foreach (PinData activePin in ActivePins)
			{
				if (Vector3.SqrMagnitude(activePin.m_pos - position) < num)
				{
					return true;
				}
			}
			try
			{
				if (pinsField?.GetValue(minimap) is List<PinData> list)
				{
					for (int i = 0; i < list.Count; i++)
					{
						PinData val = list[i];
						if (val != null && Vector3.SqrMagnitude(val.m_pos - position) < num)
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static void ClearActivePins()
		{
			Minimap instance = Minimap.instance;
			foreach (PinData activePin in ActivePins)
			{
				if ((Object)(object)instance != (Object)null)
				{
					instance.RemovePin(activePin);
				}
			}
			ActivePins.Clear();
		}
	}
	internal static class MapRevealFeature
	{
		private static FieldInfo? hasGeneratedField;

		private static FieldInfo? exploredField;

		private static FieldInfo? fogField;

		private static Minimap? revealedMap;

		internal static bool Install(out string reason)
		{
			hasGeneratedField = typeof(Minimap).GetField("m_hasGenerated", BindingFlags.Instance | BindingFlags.NonPublic);
			exploredField = typeof(Minimap).GetField("m_explored", BindingFlags.Instance | BindingFlags.NonPublic);
			fogField = typeof(Minimap).GetField("m_fogTexture", BindingFlags.Instance | BindingFlags.NonPublic);
			MethodInfo method = typeof(Minimap).GetMethod("ExploreAll", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);
			if (hasGeneratedField?.FieldType != typeof(bool) || method?.ReturnType != typeof(void) || exploredField?.FieldType != typeof(BitArray) || fogField?.FieldType != typeof(Texture2D))
			{
				reason = "missing or changed Minimap.m_hasGenerated/ExploreAll contract";
				hasGeneratedField = null;
				return false;
			}
			reason = "bulk fog reveal after profile map generation, preserving shared-map channels";
			return true;
		}

		internal static void Tick()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			if (hasGeneratedField == null || !ZariRulesPlugin.Instance.Rules.RevealFullMap.Value || !ZariRulesPlugin.Instance.Rules.IsServerConfigurationReady || (Object)(object)Player.m_localPlayer == (Object)null || (int)SystemInfo.graphicsDeviceType == 4)
			{
				return;
			}
			FieldInfo fieldInfo = hasGeneratedField;
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || instance == revealedMap)
			{
				return;
			}
			object value = fieldInfo.GetValue(instance);
			if (!(value is bool) || !(bool)value || !(exploredField.GetValue(instance) is BitArray bitArray))
			{
				return;
			}
			object? value2 = fogField.GetValue(instance);
			Texture2D val = (Texture2D)((value2 is Texture2D) ? value2 : null);
			if (val == null)
			{
				return;
			}
			Color32[] pixels = val.GetPixels32();
			if (pixels.Length != bitArray.Length)
			{
				revealedMap = instance;
				ZariRulesPlugin.LogError("ZR915", "MapReveal: fog and exploration dimensions differ");
				return;
			}
			for (int i = 0; i < pixels.Length; i++)
			{
				pixels[i].r = 0;
			}
			val.SetPixels32(pixels);
			val.Apply();
			bitArray.SetAll(value: true);
			revealedMap = instance;
			Diagnostics.Info("ZR110", "mapReveal=completed");
		}

		internal static void ResetSession()
		{
			revealedMap = null;
		}

		internal static void Uninstall()
		{
			ResetSession();
			hasGeneratedField = null;
			exploredField = null;
			fogField = null;
		}
	}
	internal static class PlantGrowthFeature
	{
		internal const string HarmonyId = "zari.rules.plant-growth";

		private static Harmony? harmony;

		internal static bool Ready { get; private set; }

		internal static bool Install(out string reason)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			if (Ready)
			{
				reason = "plant growth multiplier active";
				return true;
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Plant), "GetGrowTime", Type.EmptyTypes, (Type[])null);
			if (methodInfo == null || methodInfo.IsStatic || methodInfo.ReturnType != typeof(float))
			{
				reason = "missing Plant.GetGrowTime contract";
				return false;
			}
			try
			{
				harmony = new Harmony("zari.rules.plant-growth");
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(PlantGrowthFeature), "GetGrowTimePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Ready = true;
				reason = "Plant.GetGrowTime postfix";
				return true;
			}
			catch (Exception ex)
			{
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				harmony = null;
				reason = ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		internal static void Uninstall()
		{
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			harmony = null;
			Ready = false;
		}

		private static void GetGrowTimePostfix(ref float __result)
		{
			if (ZariRulesPlugin.Instance.Rules.IsServerConfigurationReady)
			{
				double value = ZariRulesPlugin.Instance.Rules.PlantGrowthMultiplier.Value;
				if (RuleMath.IsSupportedMultiplier(value))
				{
					__result = (float)((double)__result / value);
				}
			}
		}
	}
	internal static class ProductionSpeedController
	{
		internal const string HarmonyId = "zari.rules.production-speed";

		private static readonly HashSet<string> Prefabs = new HashSet<string>(StringComparer.Ordinal) { "smelter", "blastfurnace", "charcoal_kiln" };

		private static Harmony? harmony;

		internal static bool Ready { get; private set; }

		internal static bool Install(out string reason)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			if (!CompatibilityProbe.VerifyMethod(typeof(Smelter), "UpdateSmelter", BindingFlags.Instance | BindingFlags.NonPublic, typeof(void), Type.EmptyTypes, "146dc8c182d22edbcc9f2192f3edb621ec03f219bf327c7315ed6f910563a798", out MethodInfo method, out reason) || !CompatibilityProbe.HasOnlyAllowedPatchOwners(method, (IReadOnlyCollection<string>)(object)Array.Empty<string>(), out reason))
			{
				return false;
			}
			try
			{
				harmony = new Harmony("zari.rules.production-speed");
				harmony.Patch((MethodBase)method, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ProductionSpeedController), "Transpiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				Ready = true;
				reason = "Smelter.UpdateSmelter: whole simulation ticks; real-time ZDO remainder; native duration/fuel/queue unchanged";
				return true;
			}
			catch (Exception ex)
			{
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				reason = ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		internal static void ApplyToExisting(string source)
		{
			if (Ready)
			{
				double value = ZariRulesPlugin.Instance.Rules.ProductionSpeedMultiplier.Value;
				if (!RuleMath.TryProductionTick(value, out float tick, out string error))
				{
					ZariRulesPlugin.LogError("ZR900", "feature=ProductionSpeed status=unavailable reason=" + error);
				}
				else
				{
					Diagnostics.Info("ZR310", $"production prefabs=smelter,blastfurnace,charcoal_kiln multiplier={value:R} tickSeconds={tick:R} source={source}");
				}
			}
		}

		internal static void Uninstall()
		{
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Ready = false;
		}

		internal static bool VerifyPatchOwners(out string reason)
		{
			if (CompatibilityProbe.HasOnlyAllowedPatchOwners(AccessTools.Method(typeof(Smelter), "Awake", (Type[])null, (Type[])null), (IReadOnlyCollection<string>)(object)Array.Empty<string>(), out reason))
			{
				return CompatibilityProbe.HasOnlyAllowedPatchOwners(AccessTools.Method(typeof(Smelter), "UpdateSmelter", (Type[])null, (Type[])null), (IReadOnlyCollection<string>)(object)new string[1] { "zari.rules.production-speed" }, out reason);
			}
			return false;
		}

		private static float GetTickSeconds(Smelter smelter)
		{
			if (!Ready || !Prefabs.Contains(Utils.GetPrefabName(((Component)smelter).gameObject)) || !RuleMath.TryProductionTick(ZariRulesPlugin.Instance.Rules.ProductionSpeedMultiplier.Value, out float tick, out string _))
			{
				return 1f;
			}
			return tick;
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
		{
			return ProductionTimingPatch.Rewrite(instructions, generator, AccessTools.Method(typeof(ProductionSpeedController), "GetTickSeconds", (Type[])null, (Type[])null));
		}
	}
	internal static class ProductionTimingPatch
	{
		internal static IEnumerable<CodeInstruction> Rewrite(IEnumerable<CodeInstruction> instructions, ILGenerator generator, MethodInfo tickGetter)
		{
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Expected O, but got Unknown
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Expected O, but got Unknown
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Expected O, but got Unknown
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Expected O, but got Unknown
			//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Expected O, but got Unknown
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Expected O, but got Unknown
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Expected O, but got Unknown
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Expected O, but got Unknown
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Expected O, but got Unknown
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Expected O, but got Unknown
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Expected O, but got Unknown
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Expected O, but got Unknown
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			List<int> list2 = new List<int>();
			List<int> list3 = new List<int>();
			for (int i = 0; i + 1 < list.Count; i++)
			{
				if (!(list[i].opcode != OpCodes.Ldc_R4) && object.Equals(list[i].operand, 1f))
				{
					if (list[i + 1].opcode == OpCodes.Sub)
					{
						list2.Add(i);
					}
					else if (list[i + 1].opcode == OpCodes.Bge || list[i + 1].opcode == OpCodes.Bge_S)
					{
						list3.Add(i);
					}
				}
			}
			if (list2.Count != 1 || list3.Count != 1)
			{
				throw new InvalidOperationException($"Expected one accumulator subtraction and comparison, found {list2.Count}/{list3.Count}.");
			}
			int num = list2[0] - 1;
			int num2 = num - 1;
			int index = list3[0] - 1;
			if (num2 < 0 || (list[num2].opcode != OpCodes.Br && list[num2].opcode != OpCodes.Br_S) || !(list[num2].operand is Label item) || !list[index].labels.Contains(item) || !CodeInstructionExtensions.IsLdloc(list[num], (LocalBuilder)null) || !CodeInstructionExtensions.IsStloc(list[list2[0] + 2], (LocalBuilder)null) || list[num].opcode != list[index].opcode || !object.Equals(list[num].operand, list[index].operand))
			{
				throw new InvalidOperationException("Unexpected accumulator loop entry/local contract.");
			}
			LocalBuilder localBuilder = generator.DeclareLocal(typeof(float));
			CodeInstruction val = new CodeInstruction(list[num].opcode, list[num].operand);
			CodeInstruction val2 = new CodeInstruction(list[list2[0] + 2].opcode, list[list2[0] + 2].operand);
			list.InsertRange(list3[0] + 2, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[4]
			{
				new CodeInstruction(val.opcode, val.operand),
				new CodeInstruction(OpCodes.Ldloc, (object)localBuilder),
				new CodeInstruction(OpCodes.Mul, (object)null),
				new CodeInstruction(val2.opcode, val2.operand)
			});
			CodeInstruction[] array = (CodeInstruction[])(object)new CodeInstruction[7]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Call, (object)tickGetter),
				new CodeInstruction(OpCodes.Stloc, (object)localBuilder),
				new CodeInstruction(val.opcode, val.operand),
				new CodeInstruction(OpCodes.Ldloc, (object)localBuilder),
				new CodeInstruction(OpCodes.Div, (object)null),
				new CodeInstruction(val2.opcode, val2.operand)
			};
			array[0].labels.AddRange(list[num2].labels);
			list[num2].labels.Clear();
			list.InsertRange(num2, array);
			return list;
		}
	}
	internal static class RecipeNotificationFeature
	{
		private sealed class NotificationItem
		{
			internal string Author { get; }

			internal List<string> Recipes { get; }

			internal NotificationItem(string author, List<string> recipes)
			{
				Author = author;
				Recipes = recipes;
			}
		}

		private const int MaxQueueSize = 6;

		private const float FadeInDuration = 0.3f;

		private const float HoldDuration = 4.4f;

		private const float FadeOutDuration = 0.3f;

		private const float TotalDuration = 5.0000005f;

		private static readonly Queue<NotificationItem> Queue = new Queue<NotificationItem>();

		private static GameObject? canvasObject;

		private static GameObject? panelObject;

		private static CanvasGroup? panelGroup;

		private static TMP_Text? titleText;

		private static TMP_Text? bodyText;

		private static Hud? ownerHud;

		private static NotificationItem? currentItem;

		private static float currentTimer;

		internal static bool Install(out string reason)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			ResetSession();
			if ((int)SystemInfo.graphicsDeviceType == 4)
			{
				reason = "headless graphics device; UI disabled";
				return true;
			}
			reason = "independent HUD notification banner below minimap with localization and queue";
			return true;
		}

		internal static void QueueNotification(string author, IReadOnlyList<string> recipes)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			if (recipes != null && recipes.Count != 0 && (int)SystemInfo.graphicsDeviceType != 4)
			{
				if (Queue.Count >= 6)
				{
					Queue.Dequeue();
				}
				Queue.Enqueue(new NotificationItem(author, recipes.Distinct<string>(StringComparer.Ordinal).ToList()));
			}
		}

		internal static void Tick()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)SystemInfo.graphicsDeviceType == 4)
			{
				return;
			}
			Hud instance = Hud.instance;
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)instance != (Object)null) || !((Object)(object)localPlayer != (Object)null) || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || !instance.IsVisible() || Hud.IsUserHidden() || InventoryGui.IsVisible() || Menu.IsVisible())
			{
				SetVisible(visible: false);
				return;
			}
			if ((Object)(object)canvasObject == (Object)null || (Object)(object)ownerHud != (Object)(object)instance)
			{
				DestroyUi();
				if (!CreateUi(instance))
				{
					return;
				}
			}
			if (currentItem == null)
			{
				if (Queue.Count == 0)
				{
					SetVisible(visible: false);
					return;
				}
				currentItem = Queue.Dequeue();
				currentTimer = 0f;
				DisplayCurrent(currentItem);
				Diagnostics.Info("ZR124", $"recipeNotification=shown count={currentItem.Recipes.Count}");
			}
			currentTimer += Time.unscaledDeltaTime;
			float num;
			if (currentTimer < 0.3f)
			{
				num = Mathf.Clamp01(currentTimer / 0.3f);
			}
			else if (currentTimer < 4.7000003f)
			{
				num = 1f;
			}
			else if (currentTimer < 5.0000005f)
			{
				num = Mathf.Clamp01((5.0000005f - currentTimer) / 0.3f);
			}
			else
			{
				currentItem = null;
				num = 0f;
			}
			if ((Object)(object)panelGroup != (Object)null)
			{
				panelGroup.alpha = num;
			}
			SetVisible(num > 0.01f);
		}

		internal static void ResetSession()
		{
			Queue.Clear();
			currentItem = null;
			currentTimer = 0f;
			DestroyUi();
		}

		internal static void Uninstall()
		{
			ResetSession();
		}

		private static bool CreateUi(Hud hud)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: 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)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_031