Decompiled source of VeinProtector v0.5.1

BepInEx/plugins/VeinProtector/VeinProtector.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using VeinProtector.Core;
using VeinProtector.Patches;

[assembly: AssemblyInformationalVersion("0.5.1")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyProduct("VeinProtector")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyFileVersion("0.5.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[assembly: AssemblyTitle("VeinProtector")]
[assembly: AssemblyVersion("0.5.1.0")]
namespace VeinProtector
{
	[BepInPlugin("skye.dsp.veinprotector", "VeinProtector", "0.5.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "skye.dsp.veinprotector";

		public const string PluginName = "VeinProtector";

		public const string PluginVersion = "0.5.1";

		internal static ConfigEntry<bool> ProtectionEnabled;

		internal static ManualLogSource Log;

		private static ConfigFile _configFile;

		internal static Plugin Instance { get; private set; }

		internal static bool IsProtectionEnabled
		{
			get
			{
				if (ProtectionEnabled != null)
				{
					return ProtectionEnabled.Value;
				}
				return true;
			}
		}

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			_configFile = ((BaseUnityPlugin)this).Config;
			ProtectionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Vein Protection", "Enabled", true, "Keep ordinary solid veins at one remaining unit. Toggle this from DSP Settings.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Version = 0.5.1");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("ProtectionEnabled = " + ProtectionEnabled.Value));
			ApplyPatch("skye.dsp.veinprotector.OptionsToggle", typeof(OptionsProtectionTogglePatch), AccessTools.Method(typeof(UIOptionWindow), "_OnOpen", Type.EmptyTypes, (Type[])null), "UIOptionWindow._OnOpen options toggle");
			ApplyPatch("skye.dsp.veinprotector.MinerGuard", typeof(MinerAllProtectedPrefixPatch), GetInternalUpdateMethod(), "MinerComponent.InternalUpdate protection Prefix");
			ApplyPatch("skye.dsp.veinprotector.MinerLogic", typeof(MinerProtectionTranspilerPatch), GetInternalUpdateMethod(), "MinerComponent.InternalUpdate protection Transpiler");
			ApplyPatch("skye.dsp.veinprotector.MinerUI", typeof(UIMinerWindowPatch), AccessTools.Method(typeof(UIMinerWindow), "_OnUpdate", Type.EmptyTypes, (Type[])null), "UIMinerWindow._OnUpdate");
			ApplyPatch("skye.dsp.veinprotector.CollectorUI", typeof(UIVeinCollectorPanelPatch), AccessTools.Method(typeof(UIVeinCollectorPanel), "_OnUpdate", Type.EmptyTypes, (Type[])null), "UIVeinCollectorPanel._OnUpdate");
			ApplyPatch("skye.dsp.veinprotector.ReferenceDetails", typeof(ReferenceSpeedDetailExtensionPatch), AccessTools.Method(typeof(UIReferenceSpeedTip), "SetTip", (Type[])null, (Type[])null), "UIReferenceSpeedTip.SetTip read-only detail extension");
			if (!MinerProtectionTranspilerPatch.TimeMultiplierPatched || !MinerProtectionTranspilerPatch.ProtectedVeinSkipPatched || !MinerProtectionTranspilerPatch.CostBoundaryPatched || !UIMinerWindowPatch.SpeedMultiplierPatched)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Protection transpiler patch points incomplete: mining-rate=" + MinerProtectionTranspilerPatch.TimeMultiplierPatched + ", vein-skip=" + MinerProtectionTranspilerPatch.ProtectedVeinSkipPatched + ", cost-boundary=" + MinerProtectionTranspilerPatch.CostBoundaryPatched + ", miner-UI=" + UIMinerWindowPatch.SpeedMultiplierPatched));
			}
			if (!UIVeinCollectorPanelPatch.SpeedMultiplierPatched)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Advanced miner detail speed multiplier patch point was not found; vanilla UI is retained");
			}
			if (IsUxAssistLoaded())
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"UXAssist also contains a vein-protection feature. Do not enable both vein-protection implementations simultaneously.");
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		internal static void SetProtectionEnabled(bool enabled)
		{
			if (ProtectionEnabled != null && ProtectionEnabled.Value != enabled)
			{
				ProtectionEnabled.Value = enabled;
				if (_configFile != null)
				{
					_configFile.Save();
				}
			}
		}

		internal static void LogError(string message)
		{
			if (Log != null)
			{
				Log.LogError((object)message);
			}
		}

		internal static void LogWarning(string message)
		{
			if (Log != null)
			{
				Log.LogWarning((object)message);
			}
		}

		private static MethodInfo GetInternalUpdateMethod()
		{
			return AccessTools.Method(typeof(MinerComponent), "InternalUpdate", new Type[6]
			{
				typeof(PlanetFactory),
				typeof(VeinData[]),
				typeof(float),
				typeof(float),
				typeof(float),
				typeof(int[])
			}, (Type[])null);
		}

		private bool ApplyPatch(string owner, Type patchClass, MethodBase target, string description)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			try
			{
				if (target == null)
				{
					throw new MissingMethodException("Target method was not found: " + description);
				}
				Harmony val = new Harmony(owner);
				val.CreateClassProcessor(patchClass).Patch();
				Patches patchInfo = Harmony.GetPatchInfo(target);
				bool flag = patchInfo != null && patchInfo.Owners.Contains(owner);
				if (!flag)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)(description + " patch was not found in Harmony patch info"));
				}
				return flag;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to install " + description + " patch: " + ex));
				return false;
			}
		}

		private static bool IsUxAssistLoaded()
		{
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				if (value.Metadata.Name.IndexOf("UXAssist", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}
	}
}
namespace VeinProtector.Core
{
	internal static class VeinProtectionLogic
	{
		internal const int KeepVeinAmount = 1;

		internal static bool IsSupportedProtectedResourceItem(int itemId)
		{
			if (itemId <= 0 || (Object)(object)LDB.veins == (Object)null)
			{
				return false;
			}
			int num = 15;
			for (int i = 1; i < num; i++)
			{
				if (i != 7)
				{
					VeinProto val = ((ProtoSet<VeinProto>)(object)LDB.veins).Select(i);
					if (val != null && IsSupportedProtectedVeinPrototype(itemId, i, val.MiningItem))
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static bool IsSupportedProtectedVeinPrototype(int itemId, int veinTypeId, int miningItemId)
		{
			if (itemId > 0 && miningItemId == itemId && veinTypeId > 0 && veinTypeId < 15)
			{
				return veinTypeId != 7;
			}
			return false;
		}

		internal static bool ShouldShowReferenceExtension(bool protectionEnabled, int itemId, bool supportedVeinItem)
		{
			if (protectionEnabled && itemId > 0)
			{
				return supportedVeinItem;
			}
			return false;
		}

		internal static double CalculateReferenceSpeedLoss(double baseReferenceSpeed, int physicalCount, int effectiveCount)
		{
			if (baseReferenceSpeed <= 0.0 || physicalCount <= effectiveCount)
			{
				return 0.0;
			}
			return baseReferenceSpeed * (double)(physicalCount - effectiveCount);
		}

		internal static bool AddProtectedVeinId(HashSet<int> protectedVeinIds, int veinId)
		{
			if (protectedVeinIds != null && veinId > 0)
			{
				return protectedVeinIds.Add(veinId);
			}
			return false;
		}

		internal static float CalculateProtectedReferenceSpeed(bool protectionEnabled, float miningRate, float vanillaTotal, double protectionLoss)
		{
			if (!protectionEnabled || miningRate <= 0f || protectionLoss <= 0.0)
			{
				return vanillaTotal;
			}
			return Math.Max(0f, (float)((double)vanillaTotal - protectionLoss));
		}

		internal static IEnumerable<PlanetFactory> EnumerateFactoriesForAstroFilter(GameData data, int astroFilter)
		{
			if (data == null || data.factories == null || data.factoryCount <= 0)
			{
				yield break;
			}
			int factoryLimit = Math.Min(data.factoryCount, data.factories.Length);
			HashSet<int> yieldedFactoryIds = new HashSet<int>();
			switch (astroFilter)
			{
			case -1:
			{
				for (int i = 0; i < factoryLimit; i++)
				{
					PlanetFactory factory = data.factories[i];
					if (IsActualFactory(factory, factoryLimit) && yieldedFactoryIds.Add(factory.index))
					{
						yield return factory;
					}
				}
				break;
			}
			case 0:
			{
				PlanetData localPlanet = data.localPlanet;
				if (localPlanet != null)
				{
					PlanetFactory localFactory = localPlanet.factory;
					if (IsActualFactory(localFactory, factoryLimit) && yieldedFactoryIds.Add(localFactory.index))
					{
						yield return localFactory;
					}
					break;
				}
				goto default;
			}
			default:
				if (astroFilter % 100 == 0)
				{
					StarData star = ((astroFilter == 0) ? data.localStar : ((data.galaxy == null) ? null : data.galaxy.StarById(astroFilter / 100)));
					if (star == null || star.planets == null)
					{
						break;
					}
					int planetLimit = Math.Min(star.planetCount, star.planets.Length);
					for (int j = 0; j < planetLimit; j++)
					{
						PlanetFactory factory2 = star.planets[j]?.factory;
						if (IsActualFactory(factory2, factoryLimit) && yieldedFactoryIds.Add(factory2.index))
						{
							yield return factory2;
						}
					}
				}
				else
				{
					PlanetFactory selectedFactory = ((data.galaxy == null) ? null : data.galaxy.PlanetById(astroFilter))?.factory;
					if (IsActualFactory(selectedFactory, factoryLimit) && yieldedFactoryIds.Add(selectedFactory.index))
					{
						yield return selectedFactory;
					}
				}
				break;
			}
		}

		private static bool IsActualFactory(PlanetFactory factory, int factoryLimit)
		{
			if (factory != null && factory.index >= 0)
			{
				return factory.index < factoryLimit;
			}
			return false;
		}

		internal static int GetEffectiveVeinCount(MinerComponent miner, VeinData[] veinPool, float miningRate)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if (!Plugin.IsProtectionEnabled || (int)miner.type != 2 || miningRate <= 0f)
			{
				return miner.veinCount;
			}
			if (miner.minimumVeinAmount > 1)
			{
				return miner.veinCount;
			}
			if (veinPool == null || miner.veins == null)
			{
				return 0;
			}
			int num = 0;
			int num2 = Math.Min(miner.veinCount, miner.veins.Length);
			for (int i = 0; i < num2; i++)
			{
				int num3 = miner.veins[i];
				if (num3 > 0 && num3 < veinPool.Length && veinPool[num3].id != 0 && veinPool[num3].amount > 1)
				{
					num++;
				}
			}
			return num;
		}

		internal static int GetProductionTickCount(MinerComponent miner, VeinData[] veinPool, float miningRate)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			int effectiveVeinCount = GetEffectiveVeinCount(miner, veinPool, miningRate);
			if (effectiveVeinCount > 0 || (int)miner.type != 2 || miningRate <= 0f || veinPool == null || miner.veins == null)
			{
				return effectiveVeinCount;
			}
			int num = Math.Min(miner.veinCount, miner.veins.Length);
			for (int i = 0; i < num; i++)
			{
				int num2 = miner.veins[i];
				if (num2 <= 0 || num2 >= veinPool.Length || veinPool[num2].id == 0 || veinPool[num2].amount <= 0)
				{
					return miner.veinCount;
				}
			}
			return 0;
		}

		internal static int GetEffectiveVeinCount(MinerComponent miner, VeinData[] veinPool, PlanetFactory factory)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			float miningRate = 0f;
			if (factory != null && factory.gameData != null && factory.gameData.history != null)
			{
				miningRate = factory.gameData.history.miningCostRate;
			}
			return GetEffectiveVeinCount(miner, veinPool, miningRate);
		}

		internal static int GetEffectiveVeinCount(MinerComponent miner, PlanetFactory factory)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return GetEffectiveVeinCount(miner, factory?.veinPool, factory);
		}

		internal static bool IsProtectedVein(MinerComponent miner, VeinData[] veinPool, float miningRate, int veinId)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			return Plugin.IsProtectionEnabled && (int)miner.type == 2 && miningRate > 0f && veinPool != null && veinId > 0 && veinId < veinPool.Length && veinPool[veinId].id != 0 && veinPool[veinId].amount == 1;
		}

		internal static int GetMaxAllowedCost(int amount, MinerComponent miner, float miningRate)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if (!Plugin.IsProtectionEnabled || (int)miner.type != 2 || !(miningRate > 0f) || amount <= 1)
			{
				return amount;
			}
			return amount - 1;
		}

		internal static void AdvanceCurrentVein(ref MinerComponent miner)
		{
			int veinCount = miner.veinCount;
			if (veinCount <= 0)
			{
				miner.currentVeinIndex = 0;
				return;
			}
			int num = miner.currentVeinIndex + 1;
			miner.currentVeinIndex = ((num < veinCount) ? num : 0);
		}

		internal static bool AreAllBoundVeinsProtected(MinerComponent miner, VeinData[] veinPool, float miningRate)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if (!Plugin.IsProtectionEnabled || (int)miner.type != 2 || miningRate <= 0f || miner.veinCount <= 0 || veinPool == null || miner.veins == null)
			{
				return false;
			}
			int num = Math.Min(miner.veinCount, miner.veins.Length);
			if (num == 0)
			{
				return false;
			}
			for (int i = 0; i < num; i++)
			{
				int num2 = miner.veins[i];
				if (num2 <= 0 || num2 >= veinPool.Length || veinPool[num2].id == 0 || veinPool[num2].amount != 1)
				{
					return false;
				}
			}
			return num == miner.veinCount;
		}
	}
}
namespace VeinProtector.Patches
{
	[HarmonyPatch]
	internal static class MinerProtectionTranspilerPatch
	{
		private static readonly MethodInfo ProductionTickCountMethod = AccessTools.Method(typeof(VeinProtectionLogic), "GetProductionTickCount", new Type[3]
		{
			typeof(MinerComponent),
			typeof(VeinData[]),
			typeof(float)
		}, (Type[])null);

		private static readonly MethodInfo ProtectedVeinMethod = AccessTools.Method(typeof(VeinProtectionLogic), "IsProtectedVein", (Type[])null, (Type[])null);

		private static readonly MethodInfo AdvanceMethod = AccessTools.Method(typeof(VeinProtectionLogic), "AdvanceCurrentVein", (Type[])null, (Type[])null);

		private static readonly MethodInfo MaxAllowedCostMethod = AccessTools.Method(typeof(VeinProtectionLogic), "GetMaxAllowedCost", (Type[])null, (Type[])null);

		internal static bool TimeMultiplierPatched;

		internal static bool ProtectedVeinSkipPatched;

		internal static bool CostBoundaryPatched;

		[HarmonyTargetMethod]
		private static MethodInfo TargetMethod()
		{
			return AccessTools.Method(typeof(MinerComponent), "InternalUpdate", new Type[6]
			{
				typeof(PlanetFactory),
				typeof(VeinData[]),
				typeof(float),
				typeof(float),
				typeof(float),
				typeof(int[])
			}, (Type[])null);
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
		{
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0344: Expected O, but got Unknown
			//IL_0356: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Expected O, but got Unknown
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Expected O, but got Unknown
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Expected O, but got Unknown
			//IL_0388: Unknown result type (might be due to invalid IL or missing references)
			//IL_038e: Expected O, but got Unknown
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Expected O, but got Unknown
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b6: Expected O, but got Unknown
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c5: Expected O, but got Unknown
			//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Expected O, but got Unknown
			//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ee: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Expected O, but got Unknown
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Expected O, but got Unknown
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Expected O, but got Unknown
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Expected O, but got Unknown
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Expected O, but got Unknown
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			for (int i = 1; i + 2 < list.Count; i++)
			{
				FieldInfo fieldInfo = list[i].operand as FieldInfo;
				if (list[i].opcode == OpCodes.Ldfld && fieldInfo != null && fieldInfo.Name == "veinCount" && list[i + 1].opcode == OpCodes.Conv_R4 && list[i + 2].opcode == OpCodes.Mul && list[i - 1].opcode == OpCodes.Ldarg_0)
				{
					list.RemoveAt(i);
					list.RemoveAt(i - 1);
					list.InsertRange(i - 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[5]
					{
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Ldobj, (object)typeof(MinerComponent)),
						new CodeInstruction(OpCodes.Ldarg_2, (object)null),
						new CodeInstruction(OpCodes.Ldarg_S, (object)(byte)4),
						new CodeInstruction(OpCodes.Call, (object)ProductionTickCountMethod)
					});
					TimeMultiplierPatched = true;
					break;
				}
			}
			for (int j = 4; j + 1 < list.Count; j++)
			{
				FieldInfo fieldInfo2 = list[j].operand as FieldInfo;
				LocalBuilder localBuilder = list[j + 1].operand as LocalBuilder;
				if (list[j].opcode == OpCodes.Ldfld && fieldInfo2 != null && fieldInfo2.Name == "amount" && list[j + 1].opcode == OpCodes.Stloc_S && localBuilder != null && localBuilder.LocalIndex == 8 && list[j - 3].opcode == OpCodes.Ldarg_2 && list[j - 2].opcode == OpCodes.Ldloc_3 && list[j - 1].opcode == OpCodes.Ldelema)
				{
					list.InsertRange(j + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[6]
					{
						new CodeInstruction(OpCodes.Ldloc_S, (object)localBuilder),
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Ldobj, (object)typeof(MinerComponent)),
						new CodeInstruction(OpCodes.Ldarg_S, (object)(byte)4),
						new CodeInstruction(OpCodes.Call, (object)MaxAllowedCostMethod),
						new CodeInstruction(OpCodes.Stloc_S, (object)localBuilder)
					});
					CostBoundaryPatched = true;
					break;
				}
			}
			int num = FindVeinSelection(list);
			if (num >= 0)
			{
				Label label = generator.DefineLabel();
				list[num].labels.Add(label);
				Label label2 = generator.DefineLabel();
				list[num + 6].labels.Add(label2);
				list.InsertRange(num + 6, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[10]
				{
					new CodeInstruction(OpCodes.Ldarg_0, (object)null),
					new CodeInstruction(OpCodes.Ldobj, (object)typeof(MinerComponent)),
					new CodeInstruction(OpCodes.Ldarg_2, (object)null),
					new CodeInstruction(OpCodes.Ldarg_S, (object)(byte)4),
					new CodeInstruction(OpCodes.Ldloc_3, (object)null),
					new CodeInstruction(OpCodes.Call, (object)ProtectedVeinMethod),
					new CodeInstruction(OpCodes.Brfalse, (object)label2),
					new CodeInstruction(OpCodes.Ldarg_0, (object)null),
					new CodeInstruction(OpCodes.Call, (object)AdvanceMethod),
					new CodeInstruction(OpCodes.Br, (object)label)
				});
				ProtectedVeinSkipPatched = true;
			}
			return list;
		}

		private static int FindVeinSelection(List<CodeInstruction> codes)
		{
			for (int i = 0; i + 5 < codes.Count; i++)
			{
				if (codes[i].opcode == OpCodes.Ldarg_0 && codes[i + 1].opcode == OpCodes.Ldfld && codes[i + 1].operand is FieldInfo && ((FieldInfo)codes[i + 1].operand).Name == "veins" && codes[i + 2].opcode == OpCodes.Ldarg_0 && codes[i + 3].opcode == OpCodes.Ldfld && codes[i + 3].operand is FieldInfo && ((FieldInfo)codes[i + 3].operand).Name == "currentVeinIndex" && codes[i + 4].opcode == OpCodes.Ldelem_I4 && codes[i + 5].opcode == OpCodes.Stloc_3)
				{
					return i;
				}
			}
			return -1;
		}
	}
	[HarmonyPatch(typeof(UIMinerWindow), "_OnUpdate")]
	internal static class UIMinerWindowPatch
	{
		internal static bool SpeedMultiplierPatched;

		private static readonly MethodInfo EffectiveCountMethod = AccessTools.Method(typeof(VeinProtectionLogic), "GetEffectiveVeinCount", new Type[3]
		{
			typeof(MinerComponent),
			typeof(VeinData[]),
			typeof(PlanetFactory)
		}, (Type[])null);

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			for (int i = 1; i + 2 < list.Count; i++)
			{
				FieldInfo fieldInfo = list[i].operand as FieldInfo;
				if (list[i].opcode == OpCodes.Ldfld && fieldInfo != null && fieldInfo.Name == "veinCount" && list[i + 1].opcode == OpCodes.Conv_R4 && list[i + 2].opcode == OpCodes.Mul && list[i - 1].opcode == OpCodes.Ldloc_0)
				{
					list.RemoveAt(i);
					list.RemoveAt(i - 1);
					list.InsertRange(i - 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[5]
					{
						new CodeInstruction(OpCodes.Ldloc_0, (object)null),
						new CodeInstruction(OpCodes.Ldloc_2, (object)null),
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(UIMinerWindow), "factory")),
						new CodeInstruction(OpCodes.Call, (object)EffectiveCountMethod)
					});
					SpeedMultiplierPatched = true;
					break;
				}
			}
			return list;
		}
	}
	[HarmonyPatch]
	[HarmonyPatch]
	internal static class MinerAllProtectedPrefixPatch
	{
		[HarmonyTargetMethod]
		private static MethodInfo TargetMethod()
		{
			return AccessTools.Method(typeof(MinerComponent), "InternalUpdate", new Type[6]
			{
				typeof(PlanetFactory),
				typeof(VeinData[]),
				typeof(float),
				typeof(float),
				typeof(float),
				typeof(int[])
			}, (Type[])null);
		}

		[HarmonyPrefix]
		private static bool Prefix(ref MinerComponent __instance, VeinData[] __1, float __3, ref uint __result)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (!VeinProtectionLogic.AreAllBoundVeinsProtected(__instance, __1, __3))
			{
				return true;
			}
			__result = 0u;
			return false;
		}
	}
	[HarmonyPatch(typeof(UIOptionWindow), "_OnOpen")]
	internal static class OptionsProtectionTogglePatch
	{
		private const string RowName = "VeinProtector-ProtectionRow";

		private const string ToggleName = "VeinProtector-ProtectionToggle";

		private const int GameTabIndex = 2;

		private const float BottomMargin = 22f;

		private static int _loggedFailure;

		[HarmonyPostfix]
		private static void Postfix(UIOptionWindow __instance)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null)
				{
					throw new InvalidOperationException("UIOptionWindow is null");
				}
				RectTransform gameScrollContentRect = __instance.gameScrollContentRect;
				if ((Object)(object)gameScrollContentRect == (Object)null)
				{
					throw new InvalidOperationException("confirmed Game page gameScrollContentRect is missing");
				}
				EnsureProtectionRow(__instance, gameScrollContentRect);
			}
			catch (Exception ex)
			{
				if (Interlocked.Exchange(ref _loggedFailure, 1) == 0)
				{
					Plugin.LogError("Settings protection row creation failed: " + ex);
				}
			}
		}

		private static void EnsureProtectionRow(UIOptionWindow window, RectTransform content)
		{
			Transform val = FindNamedChild(((Component)window).transform, "VeinProtector-ProtectionRow");
			RectTransform val2 = (RectTransform)(object)((val is RectTransform) ? val : null);
			bool flag = (Object)(object)val2 == (Object)null;
			if (flag)
			{
				val2 = CreateProtectionRow(window, content);
			}
			bool flag2 = (Object)(object)((Transform)val2).parent != (Object)(object)content;
			if (flag2)
			{
				((Transform)val2).SetParent((Transform)(object)content, false);
			}
			((Component)val2).gameObject.SetActive(true);
			Toggle nativeToggle = GetNativeToggle((Transform)(object)val2);
			if ((Object)(object)nativeToggle == (Object)null)
			{
				throw new InvalidOperationException("the cloned settings row has no Unity Toggle");
			}
			UIToggle componentInChildren = ((Component)val2).GetComponentInChildren<UIToggle>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				throw new InvalidOperationException("the cloned settings row has no UIToggle wrapper");
			}
			Text val3 = FindLabel((Transform)(object)val2, ((Component)componentInChildren).transform);
			if ((Object)(object)val3 == (Object)null)
			{
				throw new InvalidOperationException("the cloned settings row has no label Text");
			}
			RemoveLocalizers((Transform)(object)val2);
			val3.text = GetProtectionLabel();
			((Behaviour)val3).enabled = true;
			((Component)val3).gameObject.SetActive(true);
			((Object)((Component)nativeToggle).gameObject).name = "VeinProtector-ProtectionToggle";
			ProtectionToggleBinding protectionToggleBinding = ((Component)val2).GetComponent<ProtectionToggleBinding>();
			if ((Object)(object)protectionToggleBinding == (Object)null)
			{
				protectionToggleBinding = ((Component)val2).gameObject.AddComponent<ProtectionToggleBinding>();
			}
			protectionToggleBinding.Bind(nativeToggle);
			protectionToggleBinding.Sync(Plugin.IsProtectionEnabled);
			if (flag || flag2)
			{
				PlaceNewRow(content, val2);
			}
		}

		private static RectTransform CreateProtectionRow(UIOptionWindow window, RectTransform targetContent)
		{
			if ((Object)(object)window.techQueueComp == (Object)null || (Object)(object)window.gameScrollContentRect == (Object)null)
			{
				throw new InvalidOperationException("confirmed Game tech-queue Toggle template or content is missing");
			}
			RectTransform gameScrollContentRect = window.gameScrollContentRect;
			Text val = FindLabel((Transform)(object)gameScrollContentRect, ((Component)window.techQueueComp).transform);
			if ((Object)(object)val == (Object)null)
			{
				throw new InvalidOperationException("the confirmed tech-queue row label could not be resolved");
			}
			Transform val2 = FindCommonRowRoot((Transform)(object)gameScrollContentRect, ((Component)window.techQueueComp).transform, ((Component)val).transform);
			if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)gameScrollContentRect)
			{
				throw new InvalidOperationException("tech-queue label and Toggle do not share a setting-row parent");
			}
			GameObject val3 = Object.Instantiate<GameObject>(((Component)val2).gameObject, (Transform)(object)targetContent, false);
			((Object)val3).name = "VeinProtector-ProtectionRow";
			val3.SetActive(true);
			Transform transform = val3.transform;
			RectTransform val4 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if ((Object)(object)val4 == (Object)null)
			{
				throw new InvalidOperationException("tech-queue row has no RectTransform");
			}
			UIToggle[] componentsInChildren = ((Component)val4).GetComponentsInChildren<UIToggle>(true);
			if (componentsInChildren.Length != 1)
			{
				throw new InvalidOperationException("tech-queue row must contain exactly one UIToggle; found " + componentsInChildren.Length);
			}
			Toggle nativeToggle = GetNativeToggle((Transform)(object)val4);
			if ((Object)(object)nativeToggle == (Object)null)
			{
				throw new InvalidOperationException("tech-queue row clone has no Unity Toggle");
			}
			ClearAllListeners(nativeToggle);
			return val4;
		}

		private static Transform FindCommonRowRoot(Transform content, Transform toggle, Transform label)
		{
			Transform val = toggle;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)content)
			{
				if ((Object)(object)label == (Object)(object)val || label.IsChildOf(val))
				{
					return val;
				}
				val = val.parent;
			}
			return null;
		}

		private static void PlaceNewRow(RectTransform content, RectTransform row)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: 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)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			LayoutGroup component = ((Component)content).GetComponent<LayoutGroup>();
			ContentSizeFitter component2 = ((Component)content).GetComponent<ContentSizeFitter>();
			Rect rect = content.rect;
			float previousHeight = Mathf.Max(((Rect)(ref rect)).height, content.sizeDelta.y);
			if ((Object)(object)component != (Object)null)
			{
				((Transform)row).SetAsLastSibling();
				LayoutRebuilder.ForceRebuildLayoutImmediate(content);
				EnsureContentHeight(content, row, previousHeight);
				return;
			}
			List<RectTransform> directSettingRows = GetDirectSettingRows(content, row);
			if (directSettingRows.Count == 0)
			{
				throw new InvalidOperationException("Game content has no active direct-child setting rows for manual spacing");
			}
			directSettingRows.Sort(delegate(RectTransform a, RectTransform b)
			{
				//IL_0007: 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_000f: 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_002b: Unknown result type (might be due to invalid IL or missing references)
				Bounds localBounds3 = GetLocalBounds(a, (Transform)(object)content);
				float y = ((Bounds)(ref localBounds3)).min.y;
				Bounds localBounds4 = GetLocalBounds(b, (Transform)(object)content);
				return y.CompareTo(((Bounds)(ref localBounds4)).min.y);
			});
			RectTransform rect2 = directSettingRows[0];
			Bounds localBounds = GetLocalBounds(rect2, (Transform)(object)content);
			float num = MeasureBottomGap(directSettingRows, content, ((Bounds)(ref localBounds)).size.y);
			float num2 = ((Bounds)(ref localBounds)).min.y - num;
			Bounds localBounds2 = GetLocalBounds(row, (Transform)(object)content);
			row.anchoredPosition = new Vector2(row.anchoredPosition.x, row.anchoredPosition.y + num2 - ((Bounds)(ref localBounds2)).max.y);
			((Transform)row).SetAsLastSibling();
			if ((Object)(object)component2 != (Object)null)
			{
				LayoutRebuilder.ForceRebuildLayoutImmediate(content);
			}
			EnsureContentHeight(content, row, previousHeight);
		}

		private static float GetRequiredHeight(RectTransform row)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Abs(row.anchoredPosition.y);
			Rect rect = row.rect;
			return num + ((Rect)(ref rect)).height + 22f;
		}

		private static void EnsureContentHeight(RectTransform content, RectTransform row, float previousHeight)
		{
			//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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			float requiredHeight = GetRequiredHeight(row);
			float preferredHeight = LayoutUtility.GetPreferredHeight(content);
			float[] array = new float[5];
			Rect rect = content.rect;
			array[0] = ((Rect)(ref rect)).height;
			array[1] = content.sizeDelta.y;
			array[2] = previousHeight;
			array[3] = requiredHeight;
			array[4] = preferredHeight;
			float num = Mathf.Max(array);
			if (num > content.sizeDelta.y)
			{
				content.sizeDelta = new Vector2(content.sizeDelta.x, num);
			}
		}

		private static float MeasureBottomGap(List<RectTransform> rows, RectTransform content, float fallbackRowHeight)
		{
			//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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			int num = Math.Min(3, rows.Count - 1);
			float num2 = 0f;
			int num3 = 0;
			for (int i = 0; i < num; i++)
			{
				Bounds localBounds = GetLocalBounds(rows[i], (Transform)(object)content);
				Bounds localBounds2 = GetLocalBounds(rows[i + 1], (Transform)(object)content);
				float num4 = ((Bounds)(ref localBounds2)).min.y - ((Bounds)(ref localBounds)).max.y;
				if (num4 >= 0f && num4 < 100f)
				{
					num2 += num4;
					num3++;
				}
			}
			if (num3 > 0)
			{
				return num2 / (float)num3;
			}
			return Mathf.Max(0f, Mathf.Max(fallbackRowHeight + 8f, 40f) - fallbackRowHeight);
		}

		private static List<RectTransform> GetDirectSettingRows(RectTransform content, RectTransform excluded)
		{
			List<RectTransform> list = new List<RectTransform>();
			for (int i = 0; i < ((Transform)content).childCount; i++)
			{
				Transform child = ((Transform)content).GetChild(i);
				RectTransform val = (RectTransform)(object)((child is RectTransform) ? child : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)excluded) && ((Component)val).gameObject.activeSelf && !IsDropdownListItem((Transform)(object)val) && HasSettingControl((Transform)(object)val))
				{
					list.Add(val);
				}
			}
			return list;
		}

		private static bool HasSettingControl(Transform row)
		{
			if (!((Object)(object)((Component)row).GetComponentInChildren<UIToggle>(true) != (Object)null) && !((Object)(object)((Component)row).GetComponentInChildren<Slider>(true) != (Object)null) && !((Object)(object)((Component)row).GetComponentInChildren<UIComboBox>(true) != (Object)null) && !((Object)(object)((Component)row).GetComponentInChildren<Dropdown>(true) != (Object)null) && !((Object)(object)((Component)row).GetComponentInChildren<UIButton>(true) != (Object)null))
			{
				return (Object)(object)((Component)row).GetComponentInChildren<Button>(true) != (Object)null;
			}
			return true;
		}

		private static bool IsDropdownListItem(Transform row)
		{
			string name = ((Object)row).name;
			if (name.IndexOf("Dropdown List", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Dropdown Item", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Scrollbar", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return (Object)(object)((Component)row).GetComponent<Scrollbar>() != (Object)null;
			}
			return true;
		}

		private static Bounds GetLocalBounds(RectTransform rect, Transform localTo)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			rect.GetWorldCorners(array);
			Vector3 val = localTo.InverseTransformPoint(array[0]);
			Bounds result = default(Bounds);
			((Bounds)(ref result))..ctor(val, Vector3.zero);
			for (int i = 1; i < array.Length; i++)
			{
				((Bounds)(ref result)).Encapsulate(localTo.InverseTransformPoint(array[i]));
			}
			return result;
		}

		private static Toggle GetNativeToggle(Transform root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			UIToggle componentInChildren = ((Component)root).GetComponentInChildren<UIToggle>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return null;
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(UIToggle), "toggle");
			if (!(fieldInfo == null))
			{
				object? value = fieldInfo.GetValue(componentInChildren);
				return (Toggle)((value is Toggle) ? value : null);
			}
			return null;
		}

		private static Text FindLabel(Transform root, Transform toggle)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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)
			if ((Object)(object)root == (Object)null || (Object)(object)toggle == (Object)null)
			{
				return null;
			}
			Text[] componentsInChildren = ((Component)root).GetComponentsInChildren<Text>(true);
			Text result = null;
			float num = float.PositiveInfinity;
			Vector3 val = root.InverseTransformPoint(toggle.position);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if (!((Object)(object)componentsInChildren[i] == (Object)null) && !((Object)(object)((Component)componentsInChildren[i]).transform == (Object)(object)toggle) && !((Component)componentsInChildren[i]).transform.IsChildOf(toggle) && !string.IsNullOrWhiteSpace(componentsInChildren[i].text))
				{
					Vector3 val2 = root.InverseTransformPoint(((Component)componentsInChildren[i]).transform.position);
					float num2 = Mathf.Abs(val2.y - val.y) + Mathf.Abs(val2.x - val.x) * 0.01f;
					if (num2 < num)
					{
						result = componentsInChildren[i];
						num = num2;
					}
				}
			}
			return result;
		}

		private static void RemoveLocalizers(Transform root)
		{
			Component[] componentsInChildren = ((Component)root).GetComponentsInChildren<Component>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if ((Object)(object)componentsInChildren[i] != (Object)null && ((object)componentsInChildren[i]).GetType().Name.IndexOf("Localizer", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
				}
			}
		}

		private static void ClearAllListeners(Toggle toggle)
		{
			((UnityEventBase)toggle.onValueChanged).RemoveAllListeners();
			FieldInfo fieldInfo = AccessTools.Field(typeof(UnityEventBase), "m_PersistentCalls");
			object obj = ((fieldInfo == null) ? null : fieldInfo.GetValue(toggle.onValueChanged));
			MethodInfo methodInfo = ((obj == null) ? null : AccessTools.Method(obj.GetType(), "Clear", (Type[])null, (Type[])null));
			if (methodInfo != null)
			{
				methodInfo.Invoke(obj, null);
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(UnityEventBase), "DirtyPersistentCalls", (Type[])null, (Type[])null);
			if (methodInfo2 != null)
			{
				methodInfo2.Invoke(toggle.onValueChanged, null);
			}
		}

		private static Transform FindNamedChild(Transform root, string name)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			if (((Object)root).name == name)
			{
				return root;
			}
			for (int i = 0; i < root.childCount; i++)
			{
				Transform val = FindNamedChild(root.GetChild(i), name);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static string GetProtectionLabel()
		{
			try
			{
				return (CultureInfo.GetCultureInfo(Localization.CurrentLanguageLCID).TwoLetterISOLanguageName == "en") ? "Vein Protection" : "矿脉保护";
			}
			catch
			{
				return "矿脉保护";
			}
		}
	}
	internal sealed class ProtectionToggleBinding : MonoBehaviour
	{
		private Toggle _toggle;

		private UnityAction<bool> _listener;

		internal void Bind(Toggle toggle)
		{
			if (!((Object)(object)_toggle == (Object)(object)toggle) || _listener == null)
			{
				if ((Object)(object)_toggle != (Object)null && _listener != null)
				{
					((UnityEvent<bool>)(object)_toggle.onValueChanged).RemoveListener(_listener);
				}
				_toggle = toggle;
				ClearListeners(_toggle);
				_listener = OnValueChanged;
				((UnityEvent<bool>)(object)_toggle.onValueChanged).AddListener(_listener);
			}
		}

		internal void Sync(bool value)
		{
			if ((Object)(object)_toggle != (Object)null && _toggle.isOn != value)
			{
				_toggle.isOn = value;
			}
		}

		private void OnValueChanged(bool value)
		{
			Plugin.SetProtectionEnabled(value);
		}

		private static void ClearListeners(Toggle toggle)
		{
			((UnityEventBase)toggle.onValueChanged).RemoveAllListeners();
			FieldInfo fieldInfo = AccessTools.Field(typeof(UnityEventBase), "m_PersistentCalls");
			object obj = ((fieldInfo == null) ? null : fieldInfo.GetValue(toggle.onValueChanged));
			MethodInfo methodInfo = ((obj == null) ? null : AccessTools.Method(obj.GetType(), "Clear", (Type[])null, (Type[])null));
			if (methodInfo != null)
			{
				methodInfo.Invoke(obj, null);
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(UnityEventBase), "DirtyPersistentCalls", (Type[])null, (Type[])null);
			if (methodInfo2 != null)
			{
				methodInfo2.Invoke(toggle.onValueChanged, null);
			}
		}
	}
	[HarmonyPatch(typeof(UIVeinCollectorPanel), "_OnUpdate")]
	internal static class UIVeinCollectorPanelPatch
	{
		internal static bool SpeedMultiplierPatched;

		private static readonly MethodInfo EffectiveCountMethod = AccessTools.Method(typeof(VeinProtectionLogic), "GetEffectiveVeinCount", new Type[2]
		{
			typeof(MinerComponent),
			typeof(PlanetFactory)
		}, (Type[])null);

		private static readonly FieldInfo FactoryField = AccessTools.Field(typeof(UIVeinCollectorPanel), "factory");

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			for (int i = 0; i + 3 < list.Count; i++)
			{
				FieldInfo fieldInfo = list[i + 1].operand as FieldInfo;
				if (list[i].opcode == OpCodes.Ldloc_0 && list[i + 1].opcode == OpCodes.Ldfld && fieldInfo != null && fieldInfo.Name == "veinCount" && list[i + 2].opcode == OpCodes.Conv_R4 && list[i + 3].opcode == OpCodes.Mul)
				{
					list.RemoveAt(i + 1);
					list.InsertRange(i + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[3]
					{
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Ldfld, (object)FactoryField),
						new CodeInstruction(OpCodes.Call, (object)EffectiveCountMethod)
					});
					SpeedMultiplierPatched = true;
					break;
				}
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(UIReferenceSpeedTip), "SetTip")]
	internal static class ReferenceSpeedDetailExtensionPatch
	{
		private const int ProductCycle = 1;

		private static readonly FieldInfo EntriesField = AccessTools.Field(typeof(UIReferenceSpeedTip), "entries");

		private static readonly FieldInfo EntryRectField = AccessTools.Field(typeof(UIReferenceSpeedTipEntry), "rectTrans");

		private static int _loggedFailure;

		[HarmonyPrefix]
		private static void Prefix(UIReferenceSpeedTip __instance)
		{
			try
			{
				if (!((Object)(object)__instance == (Object)null))
				{
					ReferenceSpeedTipExtensionState component = ((Component)__instance).GetComponent<ReferenceSpeedTipExtensionState>();
					if ((Object)(object)component != (Object)null)
					{
						component.BeforeSetTip();
					}
				}
			}
			catch (Exception ex)
			{
				if (Interlocked.Exchange(ref _loggedFailure, 1) == 0)
				{
					Plugin.LogError("Reference detail pre-refresh cleanup failed: " + ex);
				}
			}
		}

		[HarmonyPostfix]
		private static void Postfix(UIReferenceSpeedTip __instance, int _itemId, int _astroFilter, object _itemCycle, Transform _parent)
		{
			ReferenceSpeedTipExtensionState referenceSpeedTipExtensionState = null;
			try
			{
				if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.rectTrans == (Object)null)
				{
					return;
				}
				int num = Convert.ToInt32(_itemCycle, CultureInfo.InvariantCulture);
				referenceSpeedTipExtensionState = ((Component)__instance).GetComponent<ReferenceSpeedTipExtensionState>();
				if ((Object)(object)referenceSpeedTipExtensionState == (Object)null)
				{
					referenceSpeedTipExtensionState = ((Component)__instance).gameObject.AddComponent<ReferenceSpeedTipExtensionState>();
				}
				referenceSpeedTipExtensionState.CaptureVanillaBaseline(__instance);
				if (num != 1)
				{
					referenceSpeedTipExtensionState.Hide();
					return;
				}
				bool supportedVeinItem = VeinProtectionLogic.IsSupportedProtectedResourceItem(_itemId);
				if (!VeinProtectionLogic.ShouldShowReferenceExtension(Plugin.IsProtectionEnabled, _itemId, supportedVeinItem))
				{
					referenceSpeedTipExtensionState.Hide();
					return;
				}
				int statisticsAstroFilter = GetStatisticsAstroFilter(_parent, _astroFilter);
				GameData data = GameMain.data;
				float vanillaTotal = ReadVanillaReferenceTotal(_itemId, statisticsAstroFilter);
				CalculateForTooltip(_itemId, statisticsAstroFilter, out var protectedVeinCount, out var protectionLoss);
				float speed = VeinProtectionLogic.CalculateProtectedReferenceSpeed(Plugin.IsProtectionEnabled, (data == null || data.history == null) ? 0f : data.history.miningCostRate, vanillaTotal, protectionLoss);
				referenceSpeedTipExtensionState.Refresh(__instance, GetProtectedVeinLabel(protectedVeinCount), GetProtectedSpeedLabel(speed));
			}
			catch (Exception ex)
			{
				if ((Object)(object)referenceSpeedTipExtensionState != (Object)null)
				{
					try
					{
						referenceSpeedTipExtensionState.AbortExtension();
					}
					catch (Exception ex2)
					{
						ex = new AggregateException(ex, ex2);
					}
				}
				if (Interlocked.Exchange(ref _loggedFailure, 1) == 0)
				{
					Plugin.LogError("Reference detail extension failed: " + ex);
				}
			}
		}

		private static int GetStatisticsAstroFilter(Transform parent, int tooltipAstroFilter)
		{
			UIProductEntry val = (((Object)(object)parent == (Object)null) ? null : ((Component)parent).GetComponentInParent<UIProductEntry>());
			if ((Object)(object)val != (Object)null && (Object)(object)val.productionStatWindow != (Object)null)
			{
				return val.productionStatWindow.astroFilter;
			}
			if (tooltipAstroFilter != 0)
			{
				return tooltipAstroFilter;
			}
			return -1;
		}

		private static float ReadVanillaReferenceTotal(int itemId, int astroFilter)
		{
			GameData data = GameMain.data;
			if (data == null || data.statistics == null || data.statistics.production == null || data.statistics.production.factoryStatPool == null)
			{
				return 0f;
			}
			FactoryProductionStat[] factoryStatPool = data.statistics.production.factoryStatPool;
			float num = 0f;
			foreach (PlanetFactory item in VeinProtectionLogic.EnumerateFactoriesForAstroFilter(data, astroFilter))
			{
				if (item == null || item.index < 0 || item.index >= factoryStatPool.Length)
				{
					continue;
				}
				FactoryProductionStat val = factoryStatPool[item.index];
				if (val != null && val.productIndices != null && itemId >= 0 && itemId < val.productIndices.Length && val.productPool != null)
				{
					int num2 = val.productIndices[itemId];
					if (num2 > 0 && num2 < val.productPool.Length && val.productPool[num2] != null)
					{
						num += val.productPool[num2].refProductSpeed;
					}
				}
			}
			return num;
		}

		private static void CalculateForTooltip(int itemId, int astroFilter, out int protectedVeinCount, out double protectionLoss)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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_00ce: Invalid comparison between Unknown and I4
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: 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_0250: Unknown result type (might be due to invalid IL or missing references)
			protectedVeinCount = 0;
			protectionLoss = 0.0;
			GameData data = GameMain.data;
			if (!Plugin.IsProtectionEnabled || data == null || data.history == null)
			{
				return;
			}
			float miningCostRate = data.history.miningCostRate;
			if (miningCostRate <= 0f)
			{
				return;
			}
			foreach (PlanetFactory item in VeinProtectionLogic.EnumerateFactoriesForAstroFilter(data, astroFilter))
			{
				if (item == null || item.veinPool == null || item.factorySystem == null)
				{
					continue;
				}
				MinerComponent[] minerPool = item.factorySystem.minerPool;
				if (minerPool == null)
				{
					continue;
				}
				int num = Math.Min(item.factorySystem.minerCursor, minerPool.Length);
				HashSet<int> hashSet = new HashSet<int>();
				for (int i = 1; i < num; i++)
				{
					MinerComponent miner = minerPool[i];
					if (miner.id != i || (int)miner.type != 2 || miner.veinCount <= 0 || miner.minimumVeinAmount > 1 || miner.veins == null)
					{
						continue;
					}
					int num2 = Math.Min(miner.veinCount, miner.veins.Length);
					for (int j = 0; j < num2; j++)
					{
						int num3 = miner.veins[j];
						if (num3 > 0 && num3 < item.veinPool.Length)
						{
							VeinData val = item.veinPool[num3];
							if (val.id != 0 && val.productId == itemId && val.amount <= 1)
							{
								VeinProtectionLogic.AddProtectedVeinId(hashSet, num3);
							}
						}
					}
					if (miner.period <= 0 || miner.currentVeinIndex < 0 || miner.currentVeinIndex >= num2 || item.powerSystem == null || item.powerSystem.consumerPool == null || miner.pcId < 0 || miner.pcId >= item.powerSystem.consumerPool.Length)
					{
						continue;
					}
					int num4 = miner.veins[miner.currentVeinIndex];
					if (num4 > 0 && num4 < item.veinPool.Length)
					{
						VeinData val2 = item.veinPool[num4];
						if (val2.id != 0 && val2.productId == itemId && item.powerSystem.consumerPool[miner.pcId].networkId > 0)
						{
							int effectiveVeinCount = VeinProtectionLogic.GetEffectiveVeinCount(miner, item.veinPool, miningCostRate);
							double baseReferenceSpeed = 3600.0 / (double)miner.period * (double)data.history.miningSpeedScale * (double)miner.speed;
							protectionLoss += VeinProtectionLogic.CalculateReferenceSpeedLoss(baseReferenceSpeed, miner.veinCount, effectiveVeinCount);
						}
					}
				}
				protectedVeinCount += hashSet.Count;
			}
		}

		private static string GetProtectedVeinLabel(int count)
		{
			string text = count.ToString("#,##0", CultureInfo.CurrentCulture);
			if (!IsEnglishUi())
			{
				return "已保护矿点:" + text;
			}
			return "Protected veins: " + text;
		}

		private static string GetProtectedSpeedLabel(float speed)
		{
			string text = speed.ToString("#,##0.##", CultureInfo.CurrentCulture) + " / min";
			if (!IsEnglishUi())
			{
				return "保护状态参考速率:" + text;
			}
			return "Protected-State Reference Rate: " + text;
		}

		private static bool IsEnglishUi()
		{
			try
			{
				return CultureInfo.GetCultureInfo(Localization.CurrentLanguageLCID).TwoLetterISOLanguageName == "en";
			}
			catch
			{
				return false;
			}
		}
	}
	internal sealed class ReferenceSpeedTipExtensionState : MonoBehaviour
	{
		private sealed class RectState
		{
			internal readonly RectTransform Rect;

			internal readonly Vector2 BasePosition;

			internal readonly Vector2 BaseSize;

			internal bool Shift;

			internal RectState(RectTransform rect, Vector2 basePosition, Vector2 baseSize, bool shift)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				Rect = rect;
				BasePosition = basePosition;
				BaseSize = baseSize;
				Shift = shift;
			}
		}

		private const float AddedHeight = 44f;

		private const float GapAbove = 2f;

		private const float GapBelow = 3f;

		private const float RowHeight = 18f;

		private static int _loggedFailure;

		private static readonly FieldInfo EntriesField = AccessTools.Field(typeof(UIReferenceSpeedTip), "entries");

		private static readonly FieldInfo EntryRectField = AccessTools.Field(typeof(UIReferenceSpeedTipEntry), "rectTrans");

		private readonly List<RectState> _layout = new List<RectState>();

		private RectTransform _panel;

		private Vector2 _panelBaseSize;

		private Vector2 _panelBasePosition;

		private Bounds _baseTableHeaderBounds;

		private Vector2 _baseTableHeaderPosition;

		private float _layoutOffset;

		private RectTransform _extensionRoot;

		private Text _protectedVeinText;

		private Text _protectedSpeedText;

		private string _expectedVeinText;

		private string _expectedSpeedText;

		private Coroutine _detailLayoutCoroutine;

		private int _layoutGeneration;

		internal void BeforeSetTip()
		{
			CancelPendingLayout();
			RestoreBaseLayout();
			if ((Object)(object)_extensionRoot != (Object)null)
			{
				((Component)_extensionRoot).gameObject.SetActive(false);
			}
		}

		internal void Hide()
		{
			CancelPendingLayout();
			RestoreBaseLayout();
			if ((Object)(object)_extensionRoot != (Object)null)
			{
				((Component)_extensionRoot).gameObject.SetActive(false);
			}
		}

		internal void AbortExtension()
		{
			CancelPendingLayout();
			RestoreBaseLayout();
			if ((Object)(object)_extensionRoot != (Object)null)
			{
				((Component)_extensionRoot).gameObject.SetActive(false);
			}
		}

		internal void Refresh(UIReferenceSpeedTip tip, string protectedVeins, string protectedSpeed)
		{
			EnsureExtensionRoot(tip);
			_protectedVeinText.text = protectedVeins;
			_protectedSpeedText.text = protectedSpeed;
			_expectedVeinText = protectedVeins;
			_expectedSpeedText = protectedSpeed;
			CaptureBaseLayout(tip);
			RefreshCanvasLayout(tip);
			ValidateVisibleText();
			ScheduleNextFrame(tip);
		}

		internal void CaptureVanillaBaseline(UIReferenceSpeedTip tip)
		{
			CaptureBaseLayout(tip);
		}

		private void EnsureExtensionRoot(UIReferenceSpeedTip tip)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			if ((Object)(object)_extensionRoot != (Object)null && (Object)(object)_protectedVeinText != (Object)null && (Object)(object)_protectedSpeedText != (Object)null)
			{
				return;
			}
			Transform val = ((Transform)tip.rectTrans).Find("VeinProtector-ReferenceExtension");
			if ((Object)(object)val != (Object)null)
			{
				_extensionRoot = (RectTransform)(object)((val is RectTransform) ? val : null);
				_protectedVeinText = FindText(val, "VeinProtector-ProtectedVeinText");
				_protectedSpeedText = FindText(val, "VeinProtector-ProtectedRefSpeedText");
			}
			if ((Object)(object)_extensionRoot == (Object)null)
			{
				GameObject val2 = new GameObject("VeinProtector-ReferenceExtension", new Type[1] { typeof(RectTransform) });
				ref RectTransform extensionRoot = ref _extensionRoot;
				Transform transform = val2.transform;
				extensionRoot = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				((Transform)_extensionRoot).SetParent((Transform)(object)tip.rectTrans, false);
			}
			if ((Object)(object)_protectedVeinText == (Object)null || (Object)(object)_protectedSpeedText == (Object)null)
			{
				Text totalSpeedText = tip.totalSpeedText;
				if ((Object)(object)totalSpeedText == (Object)null || !((Behaviour)totalSpeedText).enabled || !((Component)totalSpeedText).gameObject.activeInHierarchy || string.IsNullOrEmpty(totalSpeedText.text))
				{
					throw new InvalidOperationException("visible vanilla Reference Rate Text template was not found");
				}
				if ((Object)(object)_protectedVeinText == (Object)null)
				{
					_protectedVeinText = CloneText(totalSpeedText, (Transform)(object)_extensionRoot, "VeinProtector-ProtectedVeinText");
					PlaceChildText(((Graphic)_protectedVeinText).rectTransform, 0f);
				}
				if ((Object)(object)_protectedSpeedText == (Object)null)
				{
					_protectedSpeedText = CloneText(totalSpeedText, (Transform)(object)_extensionRoot, "VeinProtector-ProtectedRefSpeedText");
					PlaceChildText(((Graphic)_protectedSpeedText).rectTransform, -21f);
				}
			}
			((Component)_extensionRoot).gameObject.SetActive(false);
		}

		private static Text FindText(Transform root, string name)
		{
			Transform val = root.Find(name);
			if (!((Object)(object)val == (Object)null))
			{
				return ((Component)val).GetComponent<Text>();
			}
			return null;
		}

		private static Text CloneText(Text source, Transform parent, string name)
		{
			GameObject val = Object.Instantiate<GameObject>(((Component)source).gameObject, parent, false);
			((Object)val).name = name;
			Text component = val.GetComponent<Text>();
			if ((Object)(object)component == (Object)null)
			{
				throw new InvalidOperationException("cloned reference detail row has no Unity UI Text component");
			}
			RemoveLocalizerComponents(val.transform);
			((Graphic)component).raycastTarget = false;
			component.resizeTextForBestFit = false;
			component.fontSize = 16;
			component.alignment = (TextAnchor)3;
			((Behaviour)component).enabled = true;
			val.SetActive(true);
			return component;
		}

		private static void PlaceChildText(RectTransform rect, float y)
		{
			//IL_000b: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = new Vector2(0f, 1f);
			rect.anchorMax = new Vector2(1f, 1f);
			rect.pivot = new Vector2(0f, 1f);
			rect.sizeDelta = new Vector2(0f, 18f);
			rect.anchoredPosition = new Vector2(0f, y);
		}

		private void CaptureBaseLayout(UIReferenceSpeedTip tip)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			_layout.Clear();
			_panel = tip.rectTrans;
			_panelBaseSize = _panel.sizeDelta;
			_panelBasePosition = _panel.anchoredPosition;
			if ((Object)(object)tip.tableHeaderTrans == (Object)null)
			{
				throw new InvalidOperationException("reference table header RectTransform is missing");
			}
			_baseTableHeaderBounds = GetLocalBounds(tip.tableHeaderTrans, (Transform)(object)tip.rectTrans);
			_baseTableHeaderPosition = tip.tableHeaderTrans.anchoredPosition;
			_layoutOffset = 49f;
			CaptureRect(tip.headerTrans, shift: false);
			CaptureRect(((Object)(object)tip.totalSpeedText == (Object)null) ? null : ((Graphic)tip.totalSpeedText).rectTransform, shift: false);
			CaptureRect(tip.emptyTipTrans, shift: false);
			CaptureRect(((Object)(object)tip.zeroCountTipText == (Object)null) ? null : ((Graphic)tip.zeroCountTipText).rectTransform, shift: false);
			CaptureRect(tip.tableHeaderTrans, shift: true);
			CaptureRect(tip.subTipRectTrans, shift: true);
			if ((Object)(object)tip.previousPageBtn != (Object)null)
			{
				Transform transform = ((Component)tip.previousPageBtn).transform;
				CaptureRect((RectTransform)(object)((transform is RectTransform) ? transform : null), shift: true);
			}
			if ((Object)(object)tip.nextPageBtn != (Object)null)
			{
				Transform transform2 = ((Component)tip.nextPageBtn).transform;
				CaptureRect((RectTransform)(object)((transform2 is RectTransform) ? transform2 : null), shift: true);
			}
			if (!(EntriesField != null) || !(EntriesField.GetValue(tip) is IEnumerable enumerable))
			{
				return;
			}
			foreach (object item in enumerable)
			{
				if (item != null && EntryRectField != null)
				{
					object? value = EntryRectField.GetValue(item);
					CaptureRect((RectTransform)((value is RectTransform) ? value : null), shift: true);
				}
			}
		}

		private void CaptureRect(RectTransform rect, bool shift)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rect == (Object)null || (Object)(object)rect == (Object)(object)_panel || (Object)(object)rect == (Object)(object)_extensionRoot)
			{
				return;
			}
			for (int i = 0; i < _layout.Count; i++)
			{
				if ((Object)(object)_layout[i].Rect == (Object)(object)rect)
				{
					_layout[i].Shift = _layout[i].Shift || shift;
					return;
				}
			}
			_layout.Add(new RectState(rect, rect.anchoredPosition, rect.sizeDelta, shift));
		}

		private static Bounds GetLocalBounds(RectTransform rect, Transform localTo)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			rect.GetWorldCorners(array);
			Vector3 val = localTo.InverseTransformPoint(array[0]);
			Bounds result = default(Bounds);
			((Bounds)(ref result))..ctor(val, Vector3.zero);
			for (int i = 1; i < array.Length; i++)
			{
				((Bounds)(ref result)).Encapsulate(localTo.InverseTransformPoint(array[i]));
			}
			return result;
		}

		private void ApplyExtensionLayout(UIReferenceSpeedTip tip)
		{
			//IL_0023: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: 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_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			_panel.sizeDelta = new Vector2(_panelBaseSize.x, _panelBaseSize.y + _layoutOffset);
			for (int i = 0; i < _layout.Count; i++)
			{
				RectState rectState = _layout[i];
				if ((Object)(object)rectState.Rect != (Object)null && rectState.Shift && !IsChildOfShiftedRect(rectState.Rect))
				{
					rectState.Rect.anchoredPosition = new Vector2(rectState.BasePosition.x, rectState.BasePosition.y - _layoutOffset);
				}
			}
			_extensionRoot.anchorMin = new Vector2(0f, 1f);
			_extensionRoot.anchorMax = new Vector2(0f, 1f);
			_extensionRoot.pivot = new Vector2(0f, 1f);
			_extensionRoot.sizeDelta = new Vector2(((Bounds)(ref _baseTableHeaderBounds)).size.x, 44f);
			RectTransform extensionRoot = _extensionRoot;
			float x = ((Bounds)(ref _baseTableHeaderBounds)).min.x;
			Rect rect = tip.rectTrans.rect;
			extensionRoot.anchoredPosition = new Vector2(x - ((Rect)(ref rect)).xMin, _baseTableHeaderPosition.y - 2f);
			if ((Object)(object)((Transform)_extensionRoot).parent != (Object)(object)tip.rectTrans)
			{
				((Transform)_extensionRoot).SetParent((Transform)(object)tip.rectTrans, false);
			}
			if ((Object)(object)((Transform)tip.tableHeaderTrans).parent == (Object)(object)((Transform)_extensionRoot).parent)
			{
				((Transform)_extensionRoot).SetSiblingIndex(((Transform)tip.tableHeaderTrans).GetSiblingIndex());
			}
			((Component)_extensionRoot).gameObject.SetActive(true);
			_protectedVeinText.text = _expectedVeinText;
			_protectedSpeedText.text = _expectedSpeedText;
			((Behaviour)_protectedVeinText).enabled = true;
			((Behaviour)_protectedSpeedText).enabled = true;
			((Component)_protectedVeinText).gameObject.SetActive(true);
			((Component)_protectedSpeedText).gameObject.SetActive(true);
			PlaceChildText(((Graphic)_protectedVeinText).rectTransform, 0f);
			PlaceChildText(((Graphic)_protectedSpeedText).rectTransform, -21f);
		}

		private void RefreshCanvasLayout(UIReferenceSpeedTip tip)
		{
			((Component)_extensionRoot).gameObject.SetActive(true);
			Canvas.ForceUpdateCanvases();
			LayoutRebuilder.ForceRebuildLayoutImmediate(tip.rectTrans);
			ApplyExtensionLayout(tip);
			Canvas.ForceUpdateCanvases();
		}

		private static void RemoveLocalizerComponents(Transform root)
		{
			Component[] componentsInChildren = ((Component)root).GetComponentsInChildren<Component>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if ((Object)(object)componentsInChildren[i] != (Object)null && ((object)componentsInChildren[i]).GetType().Name.IndexOf("Localizer", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					Object.Destroy((Object)(object)componentsInChildren[i]);
				}
			}
		}

		private void ScheduleNextFrame(UIReferenceSpeedTip tip)
		{
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				throw new InvalidOperationException("Plugin coroutine runner is unavailable");
			}
			CancelPendingLayout();
			_detailLayoutCoroutine = ((MonoBehaviour)instance).StartCoroutine(ApplyReferenceDetailLayoutNextFrame(tip, ++_layoutGeneration));
		}

		private IEnumerator ApplyReferenceDetailLayoutNextFrame(UIReferenceSpeedTip tip, int generation)
		{
			yield return null;
			if (generation != _layoutGeneration)
			{
				yield break;
			}
			_detailLayoutCoroutine = null;
			if ((Object)(object)tip == (Object)null)
			{
				yield break;
			}
			if ((Object)(object)_extensionRoot == (Object)null || !Object.op_Implicit((Object)(object)_extensionRoot))
			{
				AbortExtension();
				yield break;
			}
			if (!((Component)tip).gameObject.activeInHierarchy)
			{
				AbortExtension();
				yield break;
			}
			try
			{
				_protectedVeinText.text = _expectedVeinText;
				_protectedSpeedText.text = _expectedSpeedText;
				RefreshCanvasLayout(tip);
				ValidateVisibleText();
			}
			catch (Exception ex)
			{
				try
				{
					AbortExtension();
				}
				catch (Exception ex2)
				{
					ex = new AggregateException(ex, ex2);
				}
				if (Interlocked.Exchange(ref _loggedFailure, 1) == 0)
				{
					Plugin.LogWarning("Reference detail next-frame layout failed; vanilla tooltip layout restored: " + ex.Message);
				}
			}
		}

		private void CancelPendingLayout()
		{
			_layoutGeneration++;
			Plugin instance = Plugin.Instance;
			if (_detailLayoutCoroutine != null && (Object)(object)instance != (Object)null)
			{
				((MonoBehaviour)instance).StopCoroutine(_detailLayoutCoroutine);
				_detailLayoutCoroutine = null;
			}
		}

		private void ValidateVisibleText()
		{
			ValidateText(_protectedVeinText);
			ValidateText(_protectedSpeedText);
			if ((Object)(object)_extensionRoot == (Object)null || !((Component)_extensionRoot).gameObject.activeInHierarchy)
			{
				throw new InvalidOperationException("reference extension root is not active");
			}
		}

		private static void ValidateText(Text text)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)text == (Object)null || !((Behaviour)text).enabled || !((Component)text).gameObject.activeInHierarchy || string.IsNullOrEmpty(text.text) || ((Graphic)text).color.a <= 0.01f)
			{
				throw new InvalidOperationException("VeinProtector detail Text is empty, inactive, or transparent");
			}
			CanvasRenderer component = ((Component)text).GetComponent<CanvasRenderer>();
			if ((Object)(object)component != (Object)null && component.GetAlpha() <= 0.01f)
			{
				throw new InvalidOperationException("VeinProtector detail CanvasRenderer alpha is zero");
			}
			Rect rect = ((Graphic)text).rectTransform.rect;
			if (((Rect)(ref rect)).width <= 0.5f || ((Rect)(ref rect)).height <= 0.5f)
			{
				throw new InvalidOperationException("VeinProtector detail Text has an empty RectTransform");
			}
		}

		private void RestoreBaseLayout()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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)
			for (int i = 0; i < _layout.Count; i++)
			{
				RectState rectState = _layout[i];
				if ((Object)(object)rectState.Rect != (Object)null)
				{
					rectState.Rect.anchoredPosition = rectState.BasePosition;
					rectState.Rect.sizeDelta = rectState.BaseSize;
				}
			}
			if ((Object)(object)_panel != (Object)null)
			{
				_panel.sizeDelta = _panelBaseSize;
				_panel.anchoredPosition = _panelBasePosition;
			}
			_layoutOffset = 0f;
			_layout.Clear();
			_panel = null;
		}

		private bool IsChildOfShiftedRect(RectTransform rect)
		{
			for (int i = 0; i < _layout.Count; i++)
			{
				if (_layout[i].Shift && (Object)(object)_layout[i].Rect != (Object)null && (Object)(object)rect != (Object)(object)_layout[i].Rect && ((Transform)rect).IsChildOf((Transform)(object)_layout[i].Rect))
				{
					return true;
				}
			}
			return false;
		}
	}
}