Decompiled source of ChallengeHub BlutEid v2.12.50

BepInEx\plugins\ChallengeHubValheim.BlutEid\ChallengeHubValheim.BlutEid.dll

Decompiled 4 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.WebSockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
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: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ChallengeHubValheim
{
	internal static class AdvancedQoLFeature
	{
		private static Plugin _plugin;

		private static AdvancedQoLBehaviour _behaviour;

		private static ConfigEntry<float> _harvestRadius;

		private static ConfigEntry<int> _harvestMaxTargets;

		private static ConfigEntry<int> _maxGridSize;

		private static ConfigEntry<float> _doorCloseDelay;

		private static ConfigEntry<float> _doorSafetyRadius;

		private static ConfigEntry<float> _fuelContainerRadius;

		private static ConfigEntry<int> _storageMaxContainers;

		private static ConfigEntry<int> _signMaxFontSize;

		private static readonly float[] RotationSteps = new float[6] { 1f, 5f, 15f, 22.5f, 45f, 90f };

		private const string ContainerLockOwnerKey = "ChallengeHub.QoL.LockOwner";

		private const string ContainerLockUntilKey = "ChallengeHub.QoL.LockUntil";

		internal static float HarvestRadius
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || !(qoLWebConfig.harvestRadius > 0f))
				{
					return Mathf.Clamp(_harvestRadius?.Value ?? 7f, 2f, 20f);
				}
				return Mathf.Clamp(RemoteQol().harvestRadius, 2f, 20f);
			}
		}

		internal static int HarvestMaxTargets
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || qoLWebConfig.harvestMaxTargets <= 0)
				{
					return Mathf.Clamp(_harvestMaxTargets?.Value ?? 24, 2, 80);
				}
				return Mathf.Clamp(RemoteQol().harvestMaxTargets, 2, 80);
			}
		}

		internal static int MaximumGridSize
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || qoLWebConfig.maxPlantGridSize <= 0)
				{
					return Mathf.Clamp(_maxGridSize?.Value ?? 5, 2, 5);
				}
				return Mathf.Clamp(RemoteQol().maxPlantGridSize, 2, 5);
			}
		}

		internal static float DoorCloseDelay
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || !(qoLWebConfig.doorCloseDelaySeconds > 0f))
				{
					return Mathf.Clamp(_doorCloseDelay?.Value ?? 5f, 1f, 60f);
				}
				return Mathf.Clamp(RemoteQol().doorCloseDelaySeconds, 1f, 60f);
			}
		}

		internal static float DoorSafetyRadius
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || !(qoLWebConfig.doorSafetyRadius > 0f))
				{
					return Mathf.Clamp(_doorSafetyRadius?.Value ?? 1.8f, 0.8f, 5f);
				}
				return Mathf.Clamp(RemoteQol().doorSafetyRadius, 0.8f, 5f);
			}
		}

		internal static float FuelContainerRadius
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || !(qoLWebConfig.fuelContainerRadius > 0f))
				{
					return Mathf.Clamp(_fuelContainerRadius?.Value ?? 12f, 2f, 30f);
				}
				return Mathf.Clamp(RemoteQol().fuelContainerRadius, 2f, 30f);
			}
		}

		internal static int StorageMaxContainers
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || qoLWebConfig.storageMaxContainers <= 0)
				{
					return Mathf.Clamp(_storageMaxContainers?.Value ?? 20, 1, 40);
				}
				return Mathf.Clamp(RemoteQol().storageMaxContainers, 1, 40);
			}
		}

		internal static int SignMaxFontSize
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || qoLWebConfig.signMaxFontSize <= 0)
				{
					return Mathf.Clamp(_signMaxFontSize?.Value ?? 48, 18, 64);
				}
				return Mathf.Clamp(RemoteQol().signMaxFontSize, 18, 64);
			}
		}

		internal static float SwimCriticalStaminaPercent
		{
			get
			{
				QoLWebConfig qoLWebConfig = RemoteQol();
				if (qoLWebConfig == null || !(qoLWebConfig.swimCriticalStaminaPercent > 0f))
				{
					return 0.25f;
				}
				return Mathf.Clamp(RemoteQol().swimCriticalStaminaPercent, 0.05f, 0.9f);
			}
		}

		private static QoLWebConfig RemoteQol()
		{
			return _plugin?.RemoteConfig?.qol;
		}

		internal static void Initialize(Plugin plugin)
		{
			if (!((Object)(object)plugin == (Object)null))
			{
				_plugin = plugin;
				_harvestRadius = ((BaseUnityPlugin)plugin).Config.Bind<float>("QoLSkilltree.Advanced", "AreaHarvestRadius", 7f, "Radius der bewusst mit Alt+Benutzen ausgeloesten Flaechenernte.");
				_harvestMaxTargets = ((BaseUnityPlugin)plugin).Config.Bind<int>("QoLSkilltree.Advanced", "AreaHarvestMaxTargets", 24, "Maximale Anzahl zusaetzlicher Pickables pro Flaechenernte.");
				_maxGridSize = ((BaseUnityPlugin)plugin).Config.Bind<int>("QoLSkilltree.Advanced", "MaximumPlantGridSize", 5, "Maximal erlaubte Rastergroesse. Unterstuetzt werden 2, 3 und 5.");
				_doorCloseDelay = ((BaseUnityPlugin)plugin).Config.Bind<float>("QoLSkilltree.Advanced", "DoorCloseDelaySeconds", 5f, "Verzoegerung des Tuerwaechters.");
				_doorSafetyRadius = ((BaseUnityPlugin)plugin).Config.Bind<float>("QoLSkilltree.Advanced", "DoorSafetyRadius", 1.8f, "Tuer wird nur geschlossen, wenn kein Spieler in diesem Radius steht.");
				_fuelContainerRadius = ((BaseUnityPlugin)plugin).Config.Bind<float>("QoLSkilltree.Advanced", "FuelContainerRadius", 12f, "Reichweite berechtigter Lager fuer den Brennstoffhelfer.");
				_storageMaxContainers = ((BaseUnityPlugin)plugin).Config.Bind<int>("QoLSkilltree.Advanced", "StorageMaxContainers", 20, "Maximale Anzahl Kisten pro bewusst ausgeloestem Lager-Routing.");
				_signMaxFontSize = ((BaseUnityPlugin)plugin).Config.Bind<int>("QoLSkilltree.Advanced", "SignMaximumFontSize", 48, "Sicheres Schriftgroessenlimit der Schildwerkstatt.");
				_behaviour = ((Component)plugin).gameObject.GetComponent<AdvancedQoLBehaviour>() ?? ((Component)plugin).gameObject.AddComponent<AdvancedQoLBehaviour>();
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Erweiterte QoL-Talente bereit: Lager-Routing, Landwirtschaft, Tueren, Brennstoff, Rotation, Schilder und Schwimm-HUD.");
				}
			}
		}

		internal static void OnTalentDataChanged()
		{
			_behaviour?.OnTalentDataChanged();
		}

		internal static IEnumerator IntelligentStoreNearby()
		{
			Player player = Player.m_localPlayer;
			TalentData data = TalentStore.GetLocalData();
			if ((Object)(object)player == (Object)null || data == null || !data.HasSkill("storage.quick_store"))
			{
				yield break;
			}
			Inventory playerInventory = ((Humanoid)player).GetInventory();
			if (playerInventory == null)
			{
				yield break;
			}
			List<Container> containers = (from container in QoLSkillRuntimeFeature.FindNearbyContainers(((Component)player).transform.position, QoLSkillRuntimeFeature.ContainerRadius)
				where IsContainerEligible(container, player.GetPlayerID())
				select container).Take((!data.HasSkill("storage.stack_nearby")) ? 1 : StorageMaxContainers).ToList();
			if (containers.Count == 0)
			{
				QoLSkillRuntimeFeature.ShowCenter("Keine berechtigte freie Kiste in Reichweite.");
				yield break;
			}
			int movedItems = 0;
			HashSet<Container> touched = new HashSet<Container>();
			List<ItemData> list = (from item in playerInventory.GetAllItems()
				where item != null && item.m_stack > 0 && !item.m_equipped && item.m_gridPos.y != 0
				where !QoLSkillRuntimeFeature.IsReservedItem(data, playerInventory, item)
				select item).ToList();
			foreach (ItemData source in list)
			{
				if (source == null || source.m_stack <= 0)
				{
					continue;
				}
				string itemKey = QoLSkillRuntimeFeature.ItemKey(source);
				string group = MaterialGroup(source);
				List<Container> list2 = (from container in containers
					select new
					{
						Container = container,
						Score = StorageTargetScore(container.GetInventory(), source, itemKey, @group, data.AllowEmptyStorageTargets)
					} into entry
					where entry.Score > 0
					orderby entry.Score descending, Vector3.Distance(((Component)player).transform.position, ((Component)entry.Container).transform.position)
					select entry.Container).ToList();
				foreach (Container item in list2)
				{
					if (source.m_stack <= 0)
					{
						break;
					}
					Inventory inventory = item.GetInventory();
					if (inventory != null)
					{
						inventory.CanAddItem(source, source.m_stack);
					}
					if (!TryAcquireContainerLock(item, player.GetPlayerID()))
					{
						continue;
					}
					try
					{
						ClaimContainer(item);
						int num = CountCompatible(inventory, source);
						ItemData val = source.Clone();
						val.m_stack = source.m_stack;
						try
						{
							inventory.AddItem(val);
						}
						catch (Exception ex)
						{
							ManualLogSource log = Plugin.Log;
							if (log != null)
							{
								log.LogDebug((object)("Lager-Routing Ziel uebersprungen: " + ex.Message));
							}
							goto end_IL_03a6;
						}
						int num2 = Mathf.Clamp(CountCompatible(inventory, source) - num, 0, source.m_stack);
						if (num2 <= 0)
						{
							continue;
						}
						playerInventory.RemoveItem(source, num2);
						movedItems += num2;
						touched.Add(item);
						InventoryReflection.NotifyChanged(inventory);
						InventoryReflection.NotifyChanged(playerInventory);
						SaveContainer(item);
						goto IL_04d7;
						end_IL_03a6:;
					}
					finally
					{
						ReleaseContainerLock(item, player.GetPlayerID());
					}
					continue;
					IL_04d7:
					yield return null;
				}
			}
			int count = touched.Count;
			if (movedItems > 0)
			{
				MeadowsSettlementFeature.TouchLocal(((Component)player).transform.position, "qol_storage");
			}
			QoLSkillRuntimeFeature.ShowCenter((movedItems > 0) ? (movedItems + " Gegenstaende auf " + count + " Lager verteilt.") : "Keine passende Lagergruppe oder kein freier Stapel gefunden.");
		}

		private static bool IsContainerEligible(Container container, long playerId)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)container == (Object)null || container.GetInventory() == null || container.IsInUse())
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && !MeadowsSettlementFeature.CanModify(localPlayer, ((Component)container).transform.position, showMessage: false))
			{
				return false;
			}
			try
			{
				Vagon wagon = container.m_wagon;
				if ((Object)(object)wagon != (Object)null && wagon.InUse())
				{
					return false;
				}
			}
			catch
			{
			}
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					object obj2 = methodInfo.Invoke(container, new object[1] { playerId });
					if (obj2 is bool && !(bool)obj2)
					{
						return false;
					}
				}
			}
			catch
			{
				return false;
			}
			try
			{
				if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false))
				{
					return false;
				}
			}
			catch
			{
			}
			return true;
		}

		private static bool TryAcquireContainerLock(Container container, long playerId)
		{
			try
			{
				ZNetView val = (((Object)(object)container.m_rootObjectOverride != (Object)null) ? container.m_rootObjectOverride : ((Component)container).GetComponent<ZNetView>());
				ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
				if (val2 == null || (Object)(object)ZNet.instance == (Object)null)
				{
					return false;
				}
				if (!val.IsOwner())
				{
					val.ClaimOwnership();
				}
				long ticks = ZNet.instance.GetTime().Ticks;
				long num = val2.GetLong("ChallengeHub.QoL.LockUntil", 0L);
				long num2 = val2.GetLong("ChallengeHub.QoL.LockOwner", 0L);
				if (num > ticks && num2 != 0L && num2 != playerId)
				{
					return false;
				}
				val2.Set("ChallengeHub.QoL.LockOwner", playerId);
				val2.Set("ChallengeHub.QoL.LockUntil", ticks + TimeSpan.FromSeconds(12.0).Ticks);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void ReleaseContainerLock(Container container, long playerId)
		{
			try
			{
				ZNetView val = (((Object)(object)container.m_rootObjectOverride != (Object)null) ? container.m_rootObjectOverride : ((Component)container).GetComponent<ZNetView>());
				ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
				if (val2 != null && val2.GetLong("ChallengeHub.QoL.LockOwner", 0L) == playerId)
				{
					val2.Set("ChallengeHub.QoL.LockOwner", 0L);
					val2.Set("ChallengeHub.QoL.LockUntil", 0L);
				}
			}
			catch
			{
			}
		}

		private static int StorageTargetScore(Inventory inventory, ItemData source, string itemKey, string group, bool allowEmpty)
		{
			if (inventory == null || source == null)
			{
				return 0;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems.Any((ItemData item) => item != null && string.Equals(QoLSkillRuntimeFeature.ItemKey(item), itemKey, StringComparison.Ordinal)))
			{
				return 300;
			}
			if (!string.IsNullOrEmpty(group) && allItems.Any((ItemData item) => item != null && MaterialGroup(item) == group))
			{
				return 200;
			}
			if (allowEmpty && allItems.Count == 0)
			{
				return 100;
			}
			return 0;
		}

		private static int CountCompatible(Inventory inventory, ItemData source)
		{
			if (inventory == null || source == null)
			{
				return 0;
			}
			string key = QoLSkillRuntimeFeature.ItemKey(source);
			return (from item in inventory.GetAllItems()
				where item != null && string.Equals(QoLSkillRuntimeFeature.ItemKey(item), key, StringComparison.Ordinal) && item.m_quality == source.m_quality && item.m_variant == source.m_variant && item.m_worldLevel == source.m_worldLevel
				select item).Sum((ItemData item) => Math.Max(0, item.m_stack));
		}

		private static string MaterialGroup(ItemData item)
		{
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Invalid comparison between Unknown and I4
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Invalid comparison between Unknown and I4
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Invalid comparison between Unknown and I4
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Expected I4, but got Unknown
			if (item?.m_shared == null)
			{
				return string.Empty;
			}
			string text = (QoLSkillRuntimeFeature.ItemKey(item) + " " + item.m_shared.m_name).ToLowerInvariant();
			if (ContainsAny(text, "ore", "copper", "tin", "iron", "silver", "blackmetal", "flametal"))
			{
				return "ore-metal";
			}
			if (ContainsAny(text, "wood", "finewood", "roundlog", "yggdrasil"))
			{
				return "wood";
			}
			if (ContainsAny(text, "seed", "seeds", "carrot", "turnip", "onion", "barley", "flax", "sapling"))
			{
				return "seed-crop";
			}
			if (ContainsAny(text, "trophy"))
			{
				return "trophy";
			}
			if (ContainsAny(text, "hide", "leather", "pelt", "wolf", "deer"))
			{
				return "hide";
			}
			if (ContainsAny(text, "stone", "marble", "coal", "resin", "tar"))
			{
				return "construction";
			}
			if ((int)item.m_shared.m_itemType == 2)
			{
				return "food";
			}
			if ((int)item.m_shared.m_itemType == 9 || (int)item.m_shared.m_itemType == 23)
			{
				return "ammo";
			}
			return "type-" + ((int)item.m_shared.m_itemType).ToString(CultureInfo.InvariantCulture);
		}

		private static bool ContainsAny(string text, params string[] values)
		{
			return values.Any((string value) => text.Contains(value));
		}

		private static void ClaimContainer(Container container)
		{
			try
			{
				ZNetView val = (((Object)(object)container.m_rootObjectOverride != (Object)null) ? container.m_rootObjectOverride : ((Component)container).GetComponent<ZNetView>());
				if ((Object)(object)val != (Object)null && val.IsValid() && !val.IsOwner())
				{
					val.ClaimOwnership();
				}
			}
			catch
			{
			}
		}

		private static void SaveContainer(Container container)
		{
			try
			{
				AccessTools.Method(typeof(Container), "Save", (Type[])null, (Type[])null)?.Invoke(container, Array.Empty<object>());
			}
			catch
			{
			}
		}

		internal static string BuildPlantProgress(Plant plant)
		{
			if ((Object)(object)plant == (Object)null || !TalentStore.HasSkill("comfort.growth_sight"))
			{
				return string.Empty;
			}
			try
			{
				ZNetView component = ((Component)plant).GetComponent<ZNetView>();
				ZDO val = ((component != null) ? component.GetZDO() : null);
				if (val == null || (Object)(object)ZNet.instance == (Object)null)
				{
					return string.Empty;
				}
				long ticks = val.GetLong(ZDOVars.s_plantTime, ZNet.instance.GetTime().Ticks);
				double num = Math.Max(0.0, (ZNet.instance.GetTime() - new DateTime(ticks)).TotalSeconds);
				MethodInfo methodInfo = AccessTools.Method(typeof(Plant), "GetGrowTime", (Type[])null, (Type[])null);
				float val2 = ((methodInfo != null) ? Convert.ToSingle(methodInfo.Invoke(plant, Array.Empty<object>()), CultureInfo.InvariantCulture) : plant.m_growTimeMax);
				val2 = Math.Max(1f, val2);
				float num2 = Mathf.Clamp01((float)(num / (double)val2));
				TimeSpan remaining = TimeSpan.FromSeconds(Math.Max(0.0, (double)val2 - num));
				return ProgressLine(num2, remaining, (num2 >= 1f) ? "erntereif" : "waechst");
			}
			catch
			{
				return string.Empty;
			}
		}

		internal static string BuildPickableProgress(Pickable pickable)
		{
			if ((Object)(object)pickable == (Object)null || !TalentStore.HasSkill("comfort.growth_sight"))
			{
				return string.Empty;
			}
			try
			{
				ZNetView component = ((Component)pickable).GetComponent<ZNetView>();
				ZDO val = ((component != null) ? component.GetZDO() : null);
				if (val == null)
				{
					return string.Empty;
				}
				if (!val.GetBool(ZDOVars.s_picked, false))
				{
					return ProgressLine(1f, TimeSpan.Zero, "erntereif");
				}
				if (pickable.m_respawnTimeMinutes <= 0f)
				{
					return "\n<color=#8fa0aa>Nicht nachwachsend</color>";
				}
				long num = val.GetLong(ZDOVars.s_pickedTime, 0L);
				if (num <= 1 || (Object)(object)ZNet.instance == (Object)null)
				{
					return string.Empty;
				}
				double num2 = Math.Max(0.0, (ZNet.instance.GetTime() - new DateTime(num)).TotalMinutes);
				float percent = Mathf.Clamp01((float)(num2 / (double)pickable.m_respawnTimeMinutes));
				TimeSpan remaining = TimeSpan.FromMinutes(Math.Max(0.0, (double)pickable.m_respawnTimeMinutes - num2));
				return ProgressLine(percent, remaining, "Regeneration");
			}
			catch
			{
				return string.Empty;
			}
		}

		private static string ProgressLine(float percent, TimeSpan remaining, string label)
		{
			string text = ((percent >= 0.99f) ? "#8CFF8C" : ((percent >= 0.66f) ? "#E7E47A" : ((percent >= 0.33f) ? "#FFC266" : "#FF8B78")));
			string text2 = ((remaining.TotalSeconds <= 1.0) ? "bereit" : HumanDuration(remaining));
			return "\n<color=" + text + ">" + label + ": " + Mathf.RoundToInt(percent * 100f) + "% · " + text2 + "</color>";
		}

		private static string HumanDuration(TimeSpan time)
		{
			if (time.TotalHours >= 1.0)
			{
				return Math.Floor(time.TotalHours) + " Std. " + time.Minutes + " Min.";
			}
			if (time.TotalMinutes >= 1.0)
			{
				return Math.Floor(time.TotalMinutes) + " Min. " + time.Seconds + " Sek.";
			}
			return Math.Max(1, time.Seconds) + " Sek.";
		}

		internal static IEnumerator HarvestArea(Pickable origin, Player player)
		{
			if ((Object)(object)origin == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !TalentStore.HasSkill("comfort.area_harvest"))
			{
				yield break;
			}
			string prefab = Utils.GetPrefabName(((Component)origin).gameObject);
			if (string.IsNullOrWhiteSpace(prefab))
			{
				yield break;
			}
			List<Pickable> list = (from target in (from hit in Physics.OverlapSphere(((Component)origin).transform.position, HarvestRadius)
					select (!((Object)(object)hit != (Object)null)) ? null : ((Component)hit).GetComponentInParent<Pickable>() into target
					where (Object)(object)target != (Object)null && (Object)(object)target != (Object)(object)origin && Utils.GetPrefabName(((Component)target).gameObject) == prefab
					select target).Distinct()
				orderby Vector3.Distance(((Component)origin).transform.position, ((Component)target).transform.position)
				select target).Take(HarvestMaxTargets).ToList();
			int harvested = 0;
			foreach (Pickable item in list)
			{
				if (CanHarvest(item) && MeadowsSettlementFeature.CanModify(player, ((Component)item).transform.position, showMessage: false) && PrivateArea.CheckAccess(((Component)item).transform.position, 0f, false, false) && HasLineOfSight(player, ((Component)item).transform.position))
				{
					try
					{
						item.Interact((Humanoid)(object)player, false, false);
						harvested++;
					}
					catch
					{
					}
					if (harvested % 5 == 0)
					{
						yield return null;
					}
				}
			}
			if (harvested > 0)
			{
				MeadowsSettlementFeature.TouchLocal(((Component)origin).transform.position, "qol_harvest");
				QoLSkillRuntimeFeature.ShowCenter("Flächenernte: " + harvested + " zusätzliche Ziele.");
			}
		}

		private static bool CanHarvest(Pickable target)
		{
			try
			{
				ZNetView component = ((Component)target).GetComponent<ZNetView>();
				ZDO val = ((component != null) ? component.GetZDO() : null);
				return (Object)(object)component != (Object)null && component.IsValid() && val != null && !val.GetBool(ZDOVars.s_picked, false) && val.GetBool(ZDOVars.s_enabled, true) && target.GetEnabled != 0;
			}
			catch
			{
				return false;
			}
		}

		private static bool HasLineOfSight(Player player, Vector3 target)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_001c: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 eyePoint = ((Character)player).GetEyePoint();
			Vector3 val = target + Vector3.up * 0.5f - eyePoint;
			RaycastHit val2 = default(RaycastHit);
			if (!Physics.Raycast(eyePoint, ((Vector3)(ref val)).normalized, ref val2, ((Vector3)(ref val)).magnitude, LayerMask.GetMask(new string[4] { "Default", "static_solid", "piece", "terrain" })))
			{
				return true;
			}
			return Vector3.Distance(((RaycastHit)(ref val2)).point, target) < 1.25f;
		}

		internal static IEnumerator PlantGridExtras(Player player, Piece piecePrefab, Vector3 center, Quaternion rotation, int requestedGridSize)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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)
			if ((Object)(object)player == (Object)null || (Object)(object)piecePrefab == (Object)null || !TalentStore.HasSkill("building.grid_planting"))
			{
				yield break;
			}
			Plant plantPrefab = ((Component)piecePrefab).GetComponentInChildren<Plant>(true);
			if ((Object)(object)plantPrefab == (Object)null)
			{
				yield break;
			}
			int size = ((requestedGridSize <= 2) ? 2 : ((requestedGridSize >= 5 && MaximumGridSize >= 5) ? 5 : 3));
			float spacing = Mathf.Max(1.25f, plantPrefab.m_growRadius * 2.15f);
			List<Vector3> list = GridPositions(center, ((Component)player).transform.rotation, size, spacing);
			int placed = 0;
			foreach (Vector3 item in list)
			{
				if (Vector3.Distance(item, center) < 0.2f || !TryGroundPosition(item, out var position) || !CanPlantAt(player, plantPrefab, position))
				{
					continue;
				}
				if (!player.HaveRequirements(piecePrefab, (RequirementMode)0))
				{
					break;
				}
				try
				{
					TerrainModifier.SetTriggerOnPlaced(true);
					GameObject val = Object.Instantiate<GameObject>(((Component)piecePrefab).gameObject, position, rotation);
					TerrainModifier.SetTriggerOnPlaced(false);
					Piece component = val.GetComponent<Piece>();
					if ((Object)(object)component != (Object)null)
					{
						component.SetCreator(player.GetPlayerID());
					}
					WearNTear component2 = val.GetComponent<WearNTear>();
					if (component2 != null)
					{
						component2.OnPlaced();
					}
					piecePrefab.m_placeEffect.Create(position, rotation, val.transform, 1f, -1);
					player.ConsumeResources(piecePrefab.m_resources, 0, -1, 1);
					ConsumePlantingCost(player);
					placed++;
				}
				catch (Exception ex)
				{
					TerrainModifier.SetTriggerOnPlaced(false);
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogDebug((object)("Rasterpflanzung Position uebersprungen: " + ex.Message));
					}
				}
				if (placed % 3 == 0)
				{
					yield return null;
				}
			}
			if (placed > 0)
			{
				MeadowsSettlementFeature.TouchLocal(center, "qol_planting");
				QoLSkillRuntimeFeature.ShowCenter("Rasterpflanzung: " + placed + " zusätzliche Pflanzen gesetzt.");
			}
		}

		internal static List<Vector3> GridPositions(Vector3 center, Quaternion playerRotation, int size, float spacing)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_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_005a: 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)
			//IL_0063: 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_006d: 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)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			float num = (float)(size - 1) * 0.5f;
			Vector3 val = playerRotation * Vector3.right;
			Vector3 val2 = playerRotation * Vector3.forward;
			val.y = 0f;
			val2.y = 0f;
			((Vector3)(ref val)).Normalize();
			((Vector3)(ref val2)).Normalize();
			for (int i = 0; i < size; i++)
			{
				for (int j = 0; j < size; j++)
				{
					list.Add(center + val * (((float)j - num) * spacing) + val2 * (((float)i - num) * spacing));
				}
			}
			return list;
		}

		private static bool TryGroundPosition(Vector3 raw, out Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			position = raw;
			float y = default(float);
			if (Heightmap.GetHeight(raw, ref y))
			{
				position.y = y;
				return true;
			}
			RaycastHit val = default(RaycastHit);
			if (Physics.Raycast(raw + Vector3.up * 8f, Vector3.down, ref val, 20f, LayerMask.GetMask(new string[3] { "terrain", "piece", "static_solid" })))
			{
				position = ((RaycastHit)(ref val)).point;
				return true;
			}
			return false;
		}

		private static bool CanPlantAt(Player player, Plant plant, Vector3 position)
		{
			//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_001e: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (!MeadowsSettlementFeature.CanModify(player, position, showMessage: false))
			{
				return false;
			}
			if (!PrivateArea.CheckAccess(position, 0f, false, false))
			{
				return false;
			}
			if (!HasLineOfSight(player, position))
			{
				return false;
			}
			Heightmap val = Heightmap.FindHeightmap(position);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Biome biome = val.GetBiome(position, 0.02f, false);
			if ((plant.m_biome & biome) == 0)
			{
				return false;
			}
			if (plant.m_needCultivatedGround && !val.IsCultivated(position))
			{
				return false;
			}
			return Physics.OverlapSphere(position + Vector3.up * 0.5f, Mathf.Max(0.5f, plant.m_growRadius * 0.85f), LayerMask.GetMask(new string[3] { "piece", "piece_nonsolid", "Default_small" })).All((Collider collider) => (Object)(object)collider == (Object)null || collider.isTrigger);
		}

		private static void ConsumePlantingCost(Player player)
		{
			try
			{
				((Character)player).UseStamina(1f);
			}
			catch
			{
			}
			try
			{
				ItemData representativeEquippedItem = PlayerItemReflection.GetRepresentativeEquippedItem(player);
				((representativeEquippedItem != null) ? AccessTools.Method(((object)representativeEquippedItem).GetType(), "UseDurability", (Type[])null, (Type[])null) : null)?.Invoke(representativeEquippedItem, new object[1] { 1f });
			}
			catch
			{
			}
		}

		internal static bool TryFuelHoveredDevice(Player player, out string result)
		{
			result = string.Empty;
			TalentData localData = TalentStore.GetLocalData();
			if ((Object)(object)player == (Object)null || localData == null || !localData.HasSkill("storage.fuel_helper"))
			{
				return false;
			}
			Fireplace val = LookTarget.FindComponent<Fireplace>(7f);
			if ((Object)(object)val != (Object)null)
			{
				return FillFireplace(player, localData, val, out result);
			}
			Smelter val2 = LookTarget.FindComponent<Smelter>(7f);
			if ((Object)(object)val2 != (Object)null)
			{
				return FillSmelter(player, localData, val2, out result);
			}
			result = "Keine Feuerstelle oder Produktionsanlage anvisiert.";
			return false;
		}

		private static bool FillFireplace(Player player, TalentData data, Fireplace fireplace, out string result)
		{
			//IL_0040: 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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			result = string.Empty;
			if (fireplace.m_infiniteFuel || (Object)(object)fireplace.m_fuelItem == (Object)null || !MeadowsSettlementFeature.CanModify(player, ((Component)fireplace).transform.position, showMessage: false) || !PrivateArea.CheckAccess(((Component)fireplace).transform.position, 0f, false, false))
			{
				return false;
			}
			ZNetView component = ((Component)fireplace).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (val == null)
			{
				return false;
			}
			float num = val.GetFloat(ZDOVars.s_fuel, 0f);
			int num2 = Mathf.Clamp(Mathf.CeilToInt(fireplace.m_maxFuel * (float)data.FuelTargetPercent / 100f), 1, Mathf.CeilToInt(fireplace.m_maxFuel));
			int num3 = Math.Max(0, num2 - Mathf.CeilToInt(num));
			if (num3 == 0)
			{
				result = "Brennstoffziel bereits erreicht.";
				return true;
			}
			string name = fireplace.m_fuelItem.m_itemData.m_shared.m_name;
			int num4 = ConsumeFuel(player, data, name, num3, ((Component)fireplace).transform.position, delegate
			{
				fireplace.AddFuel(1f);
			});
			if (num4 > 0)
			{
				MeadowsSettlementFeature.TouchLocal(((Component)fireplace).transform.position, "qol_fuel");
			}
			result = ((num4 > 0) ? ("Brennstoffhelfer: " + num4 + " × " + QoLSkillRuntimeFeature.Localize(name) + " nachgefüllt.") : "Kein berechtigter Brennstoff vorhanden.");
			return num4 > 0;
		}

		private static bool FillSmelter(Player player, TalentData data, Smelter smelter, out string result)
		{
			//IL_001c: 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_0106: 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)
			result = string.Empty;
			if ((Object)(object)smelter.m_fuelItem == (Object)null || !MeadowsSettlementFeature.CanModify(player, ((Component)smelter).transform.position, showMessage: false) || !PrivateArea.CheckAccess(((Component)smelter).transform.position, 0f, false, false))
			{
				return false;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Smelter), "SetFuel", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				return false;
			}
			float num = Convert.ToSingle(methodInfo.Invoke(smelter, Array.Empty<object>()), CultureInfo.InvariantCulture);
			int num2 = Mathf.Clamp(Mathf.CeilToInt((float)(smelter.m_maxFuel * data.FuelTargetPercent) / 100f), 1, smelter.m_maxFuel);
			int num3 = Math.Max(0, num2 - Mathf.CeilToInt(num));
			if (num3 == 0)
			{
				result = "Brennstoffziel bereits erreicht.";
				return true;
			}
			string name = smelter.m_fuelItem.m_itemData.m_shared.m_name;
			int num4 = 0;
			int num5 = ConsumeFuel(player, data, name, num3, ((Component)smelter).transform.position, delegate
			{
			});
			if (num5 > 0)
			{
				ZNetView val = ((Component)smelter).GetComponent<ZNetView>() ?? ((Component)smelter).GetComponentInParent<ZNetView>();
				if ((Object)(object)val != (Object)null && !val.IsOwner())
				{
					val.ClaimOwnership();
				}
				methodInfo2.Invoke(smelter, new object[1] { num + (float)num5 });
				num4 = num5;
			}
			if (num4 > 0)
			{
				MeadowsSettlementFeature.TouchLocal(((Component)smelter).transform.position, "qol_fuel");
			}
			result = ((num4 > 0) ? ("Brennstoffhelfer: " + num4 + " × " + QoLSkillRuntimeFeature.Localize(name) + " nachgefüllt.") : "Kein berechtigter Brennstoff vorhanden.");
			return num4 > 0;
		}

		private static int ConsumeFuel(Player player, TalentData data, string itemName, int need, Vector3 devicePosition, Action addOne)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			int i = 0;
			Inventory inventory = ((Humanoid)player).GetInventory();
			for (; i < need; i++)
			{
				if (!inventory.HaveItem(itemName, true))
				{
					break;
				}
				inventory.RemoveItem(itemName, 1, -1, true);
				addOne();
			}
			if (i >= need || !data.HasSkill("storage.fuel_helper_storage"))
			{
				return i;
			}
			foreach (Container item in from c in QoLSkillRuntimeFeature.FindNearbyContainers(devicePosition, FuelContainerRadius)
				where IsContainerEligible(c, player.GetPlayerID())
				select c)
			{
				Inventory inventory2 = item.GetInventory();
				if (inventory2 == null || !TryAcquireContainerLock(item, player.GetPlayerID()))
				{
					continue;
				}
				try
				{
					ClaimContainer(item);
					for (; i < need; i++)
					{
						if (!inventory2.HaveItem(itemName, true))
						{
							break;
						}
						inventory2.RemoveItem(itemName, 1, -1, true);
						addOne();
					}
					InventoryReflection.NotifyChanged(inventory2);
					SaveContainer(item);
				}
				finally
				{
					ReleaseContainerLock(item, player.GetPlayerID());
				}
				if (i >= need)
				{
					break;
				}
			}
			return i;
		}

		internal static string BuildSignRichText(string raw, TalentData data)
		{
			string text = Regex.Replace(raw ?? string.Empty, "<[^>]+>", string.Empty).Replace("\r", " ").Replace("\0", string.Empty);
			if (text.Length > 120)
			{
				text = text.Substring(0, 120);
			}
			string text2 = ((data.SignAlignment == 1) ? "center" : ((data.SignAlignment == 2) ? "right" : "left"));
			string text3 = text;
			if (data.SignBold)
			{
				text3 = "<b>" + text3 + "</b>";
			}
			if (data.SignItalic)
			{
				text3 = "<i>" + text3 + "</i>";
			}
			string text4 = (data.SignColor = NormalizeSignColor(data.SignColor));
			return "<align=\"" + text2 + "\"><size=" + Mathf.Clamp(data.SignFontSize, 12, SignMaxFontSize) + "><color=" + text4 + ">" + text3 + "</color></size></align>";
		}

		private static string NormalizeSignColor(string value)
		{
			string text = (value ?? string.Empty).Trim();
			if (!Regex.IsMatch(text, "^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$"))
			{
				return "#FFFFFF";
			}
			return text.ToUpperInvariant();
		}

		internal static void CycleFineRotationStep(float direction)
		{
			TalentData localData = TalentStore.GetLocalData();
			if (localData == null || !localData.HasSkill("building.fine_rotation"))
			{
				return;
			}
			int num = 0;
			float num2 = float.MaxValue;
			for (int i = 0; i < RotationSteps.Length; i++)
			{
				float num3 = Mathf.Abs(RotationSteps[i] - localData.FineRotationStep);
				if (num3 < num2)
				{
					num2 = num3;
					num = i;
				}
			}
			num = ((direction >= 0f) ? ((num + 1) % RotationSteps.Length) : ((num - 1 + RotationSteps.Length) % RotationSteps.Length));
			localData.FineRotationStep = RotationSteps[num];
			TalentStore.SaveLocalSettings();
			QoLSkillRuntimeFeature.ShowCenter("Drehschritt: " + localData.FineRotationStep.ToString("0.#", CultureInfo.InvariantCulture) + "°");
		}

		internal static string RotationHudText()
		{
			//IL_0071: 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_007a: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			TalentData localData = TalentStore.GetLocalData();
			if (localData == null || !localData.HasSkill("building.fine_rotation"))
			{
				return string.Empty;
			}
			string text = ((localData.FineRotationAxis == 1) ? "X" : ((localData.FineRotationAxis == 2) ? "Z" : "Y"));
			Player localPlayer = Player.m_localPlayer;
			GameObject val = (((Object)(object)localPlayer != (Object)null) ? PlayerBuildReflection.GetPlacementGhost(localPlayer) : null);
			Vector3 val2;
			if (!((Object)(object)val != (Object)null))
			{
				val2 = Vector3.zero;
			}
			else
			{
				Quaternion rotation = val.transform.rotation;
				val2 = ((Quaternion)(ref rotation)).eulerAngles;
			}
			Vector3 val3 = val2;
			string text2 = "Welt X " + FormatWorldAngle(val3.x) + " | Y " + FormatWorldAngle(val3.y) + " | Z " + FormatWorldAngle(val3.z);
			return text2 + "  ·  Achse " + text + "  ·  Schritt " + localData.FineRotationStep.ToString("0.#", CultureInfo.InvariantCulture) + "°  ·  " + (localData.FineRotationLocalFrame ? "Eingabe lokal" : "Eingabe Welt") + "  [Alt+V Achse | Alt+Shift+V Rahmen | Alt+Shift+Rad Schritt | Alt+Z Reset]";
		}

		private static string FormatWorldAngle(float angle)
		{
			float num = Mathf.Repeat(angle, 360f);
			if (num >= 359.95f || num < 0.05f)
			{
				num = 0f;
			}
			return num.ToString("0.0", CultureInfo.InvariantCulture) + "°";
		}

		internal static void ApplyFineRotation(Player player)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_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)
			TalentData localData = TalentStore.GetLocalData();
			if (!((Object)(object)player == (Object)null) && localData != null && localData.HasSkill("building.fine_rotation"))
			{
				GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(player);
				Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(player);
				if (!((Object)(object)placementGhost == (Object)null) && !((Object)(object)selectedPiece == (Object)null) && !IsRotationExcluded(selectedPiece))
				{
					Quaternion val = Quaternion.Euler(localData.FineRotationX, localData.FineRotationY, localData.FineRotationZ);
					placementGhost.transform.rotation = (localData.FineRotationLocalFrame ? (placementGhost.transform.rotation * val) : (val * placementGhost.transform.rotation));
				}
			}
		}

		private static bool IsRotationExcluded(Piece piece)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return true;
			}
			if ((Object)(object)((Component)piece).GetComponentInChildren<Plant>(true) != (Object)null)
			{
				return true;
			}
			if ((Object)(object)((Component)piece).GetComponentInChildren<TerrainModifier>(true) != (Object)null)
			{
				return true;
			}
			return ContainsAny(Utils.GetPrefabName(((Component)piece).gameObject).ToLowerInvariant(), "sapling", "seed", "terrain", "raise", "cultivate", "path");
		}

		internal static void AdjustFineRotation(float delta)
		{
			TalentData localData = TalentStore.GetLocalData();
			if (localData != null && localData.HasSkill("building.fine_rotation"))
			{
				float num = Mathf.Sign(delta) * localData.FineRotationStep;
				if (localData.FineRotationAxis == 1)
				{
					localData.FineRotationX = Mathf.Repeat(localData.FineRotationX + num, 360f);
				}
				else if (localData.FineRotationAxis == 2)
				{
					localData.FineRotationZ = Mathf.Repeat(localData.FineRotationZ + num, 360f);
				}
				else
				{
					localData.FineRotationY = Mathf.Repeat(localData.FineRotationY + num, 360f);
				}
				TalentStore.SaveLocalSettings();
			}
		}

		internal static string SwimOverview(Player player)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || !TalentStore.HasSkill("orientation.swim_overview"))
			{
				return string.Empty;
			}
			try
			{
				if (!((Character)player).IsSwimming())
				{
					return string.Empty;
				}
			}
			catch
			{
				return string.Empty;
			}
			float num = 0f;
			float num2 = 0f;
			try
			{
				num = player.GetStamina();
				num2 = ((Character)player).GetMaxStamina();
			}
			catch
			{
			}
			float num3 = 0f;
			try
			{
				num3 = ((Character)player).GetSkills().GetSkillLevel((SkillType)103);
			}
			catch
			{
			}
			float num4 = ReadFloatField(player, "m_swimStaminaDrainMinSkill", 5f);
			float num5 = ReadFloatField(player, "m_swimStaminaDrainMaxSkill", 2f);
			float num6 = Mathf.Lerp(num4, num5, Mathf.Clamp01(num3 / 100f));
			float num7 = ((num6 > 0.01f) ? (num / num6) : 999f);
			float num8 = EstimateGroundDistance(((Component)player).transform.position);
			string text = ((num2 > 0f && num / num2 < SwimCriticalStaminaPercent) ? " · KRITISCH" : string.Empty);
			return "Schwimmen: " + num.ToString("0", CultureInfo.InvariantCulture) + "/" + num2.ToString("0", CultureInfo.InvariantCulture) + " · ca. " + num7.ToString("0", CultureInfo.InvariantCulture) + " s · Effizienz " + Mathf.RoundToInt((1f - num6 / Math.Max(0.01f, num4)) * 100f) + "% · fester Grund ~" + ((num8 < 999f) ? (Mathf.RoundToInt(num8) + " m") : ">100 m") + text;
		}

		private static float ReadFloatField(object instance, string name, float fallback)
		{
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(instance.GetType(), name);
				return (fieldInfo != null) ? Convert.ToSingle(fieldInfo.GetValue(instance), CultureInfo.InvariantCulture) : fallback;
			}
			catch
			{
				return fallback;
			}
		}

		private static float EstimateGroundDistance(Vector3 position)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			int mask = LayerMask.GetMask(new string[3] { "terrain", "static_solid", "piece" });
			float num = 999f;
			Vector3 val = default(Vector3);
			RaycastHit val2 = default(RaycastHit);
			for (int i = 0; i < 16; i++)
			{
				float num2 = (float)i * 22.5f * ((float)Math.PI / 180f);
				((Vector3)(ref val))..ctor(Mathf.Cos(num2), 0f, Mathf.Sin(num2));
				if (Physics.Raycast(position + Vector3.up, val, ref val2, 100f, mask))
				{
					num = Mathf.Min(num, ((RaycastHit)(ref val2)).distance);
				}
			}
			float num3 = default(float);
			if (Heightmap.GetHeight(position, ref num3))
			{
				num = Mathf.Min(num, Mathf.Max(0f, position.y - num3));
			}
			return num;
		}
	}
	internal sealed class AdvancedQoLBehaviour : MonoBehaviour
	{
		private readonly Dictionary<Door, float> _scheduledDoors = new Dictionary<Door, float>();

		private readonly List<GameObject> _gridPreview = new List<GameObject>();

		private Material _gridPreviewMaterial;

		private bool _cursorStateCaptured;

		private bool _previousCursorVisible;

		private CursorLockMode _previousCursorLock;

		private float _nextDoorCheck;

		private float _nextPreviewUpdate;

		private bool _showSignEditor;

		private Sign _editingSign;

		private string _signText = string.Empty;

		private Rect _signWindow = new Rect(80f, 80f, 520f, 430f);

		private static readonly int SignWindowId = "ChallengeHub_SignWorkshop_v280".GetHashCode();

		private GUIStyle _hudStyle;

		private GUIStyle _signPreviewStyle;

		internal void ScheduleDoor(Door door)
		{
			if (!((Object)(object)door == (Object)null) && TalentStore.HasSkill("comfort.door_guardian"))
			{
				_scheduledDoors[door] = Time.realtimeSinceStartup + AdvancedQoLFeature.DoorCloseDelay;
			}
		}

		internal void OnTalentDataChanged()
		{
			if (!TalentStore.HasSkill("building.sign_workshop"))
			{
				CloseSignEditor();
			}
			if (!TalentStore.HasSkill("building.grid_planting"))
			{
				ClearGridPreview();
			}
		}

		private void Update()
		{
			//IL_0342: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				ClearGridPreview();
				return;
			}
			bool flag = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307);
			bool flag2 = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			if (flag && !TalentMenuBehaviour.IsVisible)
			{
				if (Input.GetKeyDown((KeyCode)117) && TalentStore.HasSkill("storage.stack_nearby"))
				{
					TalentData localData = TalentStore.GetLocalData();
					localData.AllowEmptyStorageTargets = !localData.AllowEmptyStorageTargets;
					TalentStore.SaveLocalSettings();
					QoLSkillRuntimeFeature.ShowCenter("Leere Lagerziele: " + (localData.AllowEmptyStorageTargets ? "erlaubt" : "nur vorhandene Typen/Gruppen"));
				}
				else if (Input.GetKeyDown((KeyCode)106) && TalentStore.HasSkill("building.grid_planting"))
				{
					TalentData localData2 = TalentStore.GetLocalData();
					int num = (localData2.FarmingGridSize = ((localData2.FarmingGridSize == 2) ? 3 : ((localData2.FarmingGridSize == 3 && AdvancedQoLFeature.MaximumGridSize >= 5) ? 5 : 2)));
					TalentStore.SaveLocalSettings();
					QoLSkillRuntimeFeature.ShowCenter("Pflanzraster: " + num + "×" + num);
				}
				else if (Input.GetKeyDown((KeyCode)104) && TalentStore.HasSkill("storage.fuel_helper"))
				{
					TalentData localData3 = TalentStore.GetLocalData();
					localData3.FuelTargetPercent = ((localData3.FuelTargetPercent >= 100) ? 25 : (localData3.FuelTargetPercent + 25));
					TalentStore.SaveLocalSettings();
					QoLSkillRuntimeFeature.ShowCenter("Brennstoffziel: " + localData3.FuelTargetPercent + "%");
				}
				else if (Input.GetKeyDown((KeyCode)121) && TalentStore.HasSkill("storage.fuel_helper"))
				{
					AdvancedQoLFeature.TryFuelHoveredDevice(localPlayer, out var result);
					QoLSkillRuntimeFeature.ShowCenter(result);
				}
				else if (Input.GetKeyDown((KeyCode)100) && TalentStore.HasSkill("comfort.door_guardian"))
				{
					TalentData localData4 = TalentStore.GetLocalData();
					localData4.AutoCloseDoorsInDungeons = !localData4.AutoCloseDoorsInDungeons;
					TalentStore.SaveLocalSettings();
					QoLSkillRuntimeFeature.ShowCenter("Türwächter in Dungeons: " + (localData4.AutoCloseDoorsInDungeons ? "an" : "aus"));
				}
				else if (Input.GetKeyDown((KeyCode)105) && TalentStore.HasSkill("building.sign_workshop"))
				{
					Sign val = LookTarget.FindComponent<Sign>(7f);
					if ((Object)(object)val != (Object)null)
					{
						OpenSignEditor(val);
					}
				}
				else if (Input.GetKeyDown((KeyCode)118) && TalentStore.HasSkill("building.fine_rotation"))
				{
					TalentData localData5 = TalentStore.GetLocalData();
					if (flag2)
					{
						localData5.FineRotationLocalFrame = !localData5.FineRotationLocalFrame;
					}
					else
					{
						localData5.FineRotationAxis = (localData5.FineRotationAxis + 1) % 3;
					}
					TalentStore.SaveLocalSettings();
					QoLSkillRuntimeFeature.ShowCenter(AdvancedQoLFeature.RotationHudText());
				}
				else if (Input.GetKeyDown((KeyCode)122) && TalentStore.HasSkill("building.fine_rotation"))
				{
					TalentData localData6 = TalentStore.GetLocalData();
					if (flag2)
					{
						localData6.FineRotationX = (localData6.FineRotationY = (localData6.FineRotationZ = 0f));
					}
					else if (localData6.FineRotationAxis == 1)
					{
						localData6.FineRotationX = 0f;
					}
					else if (localData6.FineRotationAxis == 2)
					{
						localData6.FineRotationZ = 0f;
					}
					else
					{
						localData6.FineRotationY = 0f;
					}
					TalentStore.SaveLocalSettings();
					QoLSkillRuntimeFeature.ShowCenter("Drehung zurückgesetzt.");
				}
				float y = Input.mouseScrollDelta.y;
				if (Mathf.Abs(y) > 0.01f && TalentStore.HasSkill("building.fine_rotation"))
				{
					if (flag2)
					{
						AdvancedQoLFeature.CycleFineRotationStep(y);
					}
					else
					{
						AdvancedQoLFeature.AdjustFineRotation(y);
					}
				}
			}
			if (Time.realtimeSinceStartup >= _nextDoorCheck)
			{
				_nextDoorCheck = Time.realtimeSinceStartup + 0.25f;
				ProcessDoors(localPlayer);
			}
			if (Time.realtimeSinceStartup >= _nextPreviewUpdate)
			{
				_nextPreviewUpdate = Time.realtimeSinceStartup + 0.15f;
				UpdateGridPreview(localPlayer, flag);
			}
		}

		private void ProcessDoors(Player player)
		{
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			KeyValuePair<Door, float>[] array = _scheduledDoors.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<Door, float> keyValuePair = array[i];
				Door door = keyValuePair.Key;
				if ((Object)(object)door == (Object)null)
				{
					_scheduledDoors.Remove(keyValuePair.Key);
				}
				else
				{
					if (Time.realtimeSinceStartup < keyValuePair.Value)
					{
						continue;
					}
					_scheduledDoors.Remove(door);
					TalentData localData = TalentStore.GetLocalData();
					if (localData == null || !localData.HasSkill("comfort.door_guardian") || (((Character)player).InInterior() && !localData.AutoCloseDoorsInDungeons))
					{
						continue;
					}
					ZNetView component = ((Component)door).GetComponent<ZNetView>();
					ZDO val = ((component != null) ? component.GetZDO() : null);
					if (val == null || val.GetInt(ZDOVars.s_state, 0) == 0 || door.m_canNotBeClosed)
					{
						continue;
					}
					if (Player.GetAllPlayers().Any((Player other) => (Object)(object)other != (Object)null && Vector3.Distance(((Component)other).transform.position, ((Component)door).transform.position) <= AdvancedQoLFeature.DoorSafetyRadius))
					{
						_scheduledDoors[door] = Time.realtimeSinceStartup + 1f;
					}
					else if (MeadowsSettlementFeature.CanModify(player, ((Component)door).transform.position, showMessage: false) && (!door.m_checkGuardStone || PrivateArea.CheckAccess(((Component)door).transform.position, 0f, false, false)))
					{
						try
						{
							door.Interact((Humanoid)(object)player, false, false);
						}
						catch
						{
						}
					}
				}
			}
		}

		private void UpdateGridPreview(Player player, bool alt)
		{
			//IL_0083: 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_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: 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)
			if (!alt || !TalentStore.HasSkill("building.grid_planting"))
			{
				ClearGridPreview();
				return;
			}
			Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(player);
			GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(player);
			Plant val = (((Object)(object)selectedPiece != (Object)null) ? ((Component)selectedPiece).GetComponentInChildren<Plant>(true) : null);
			if ((Object)(object)selectedPiece == (Object)null || (Object)(object)placementGhost == (Object)null || (Object)(object)val == (Object)null)
			{
				ClearGridPreview();
				return;
			}
			int farmingGridSize = TalentStore.GetLocalData().FarmingGridSize;
			float spacing = Mathf.Max(1.25f, val.m_growRadius * 2.15f);
			List<Vector3> list = AdvancedQoLFeature.GridPositions(placementGhost.transform.position, ((Component)player).transform.rotation, farmingGridSize, spacing);
			if ((Object)(object)_gridPreviewMaterial == (Object)null)
			{
				Shader val2 = Shader.Find("Unlit/Color") ?? Shader.Find("Standard");
				if ((Object)(object)val2 != (Object)null)
				{
					_gridPreviewMaterial = new Material(val2)
					{
						color = new Color(0.25f, 1f, 0.35f, 0.55f)
					};
				}
			}
			while (_gridPreview.Count < list.Count)
			{
				GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)2);
				((Object)val3).name = "ChallengeHub_PlantGridPreview";
				Object.Destroy((Object)(object)val3.GetComponent<Collider>());
				val3.transform.localScale = new Vector3(0.25f, 0.02f, 0.25f);
				Renderer component = val3.GetComponent<Renderer>();
				if ((Object)(object)component != (Object)null && (Object)(object)_gridPreviewMaterial != (Object)null)
				{
					component.sharedMaterial = _gridPreviewMaterial;
				}
				_gridPreview.Add(val3);
			}
			float num = default(float);
			for (int i = 0; i < _gridPreview.Count; i++)
			{
				bool flag = i < list.Count;
				_gridPreview[i].SetActive(flag);
				if (flag)
				{
					Vector3 val4 = list[i];
					if (Heightmap.GetHeight(val4, ref num))
					{
						val4.y = num + 0.03f;
					}
					_gridPreview[i].transform.position = val4;
				}
			}
		}

		private void ClearGridPreview()
		{
			foreach (GameObject item in _gridPreview)
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.Destroy((Object)(object)item);
				}
			}
			_gridPreview.Clear();
		}

		private void OpenSignEditor(Sign sign)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)sign == (Object)null) && MeadowsSettlementFeature.CanModify(Player.m_localPlayer, ((Component)sign).transform.position, showMessage: false) && PrivateArea.CheckAccess(((Component)sign).transform.position, 0f, true, false))
			{
				_editingSign = sign;
				string text = string.Empty;
				try
				{
					text = sign.GetText();
				}
				catch
				{
				}
				_signText = Regex.Replace(text ?? string.Empty, "<[^>]+>", string.Empty);
				if (!_cursorStateCaptured)
				{
					_previousCursorVisible = Cursor.visible;
					_previousCursorLock = Cursor.lockState;
					_cursorStateCaptured = true;
				}
				_showSignEditor = true;
				Cursor.visible = true;
				Cursor.lockState = (CursorLockMode)0;
			}
		}

		private void CloseSignEditor()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			_showSignEditor = false;
			_editingSign = null;
			if (_cursorStateCaptured)
			{
				Cursor.visible = _previousCursorVisible;
				Cursor.lockState = _previousCursorLock;
				_cursorStateCaptured = false;
			}
		}

		private void OnGUI()
		{
			//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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Expected O, but got Unknown
			//IL_014a: 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_0106: 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)
			if (_hudStyle == null)
			{
				_hudStyle = new GUIStyle(GUI.skin.box)
				{
					alignment = (TextAnchor)4,
					fontSize = 15,
					wordWrap = true
				};
				_signPreviewStyle = new GUIStyle(GUI.skin.box)
				{
					alignment = (TextAnchor)4,
					fontSize = 18,
					wordWrap = true,
					richText = true
				};
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && !TalentMenuBehaviour.IsVisible)
			{
				string text = AdvancedQoLFeature.RotationHudText();
				if (!string.IsNullOrEmpty(text) && (Object)(object)PlayerBuildReflection.GetPlacementGhost(localPlayer) != (Object)null)
				{
					GUI.Box(new Rect((float)Screen.width * 0.5f - 330f, (float)Screen.height - 178f, 660f, 30f), text, _hudStyle);
				}
				string text2 = AdvancedQoLFeature.SwimOverview(localPlayer);
				if (!string.IsNullOrEmpty(text2))
				{
					GUI.Box(new Rect((float)Screen.width * 0.5f - 330f, 86f, 660f, 34f), text2, _hudStyle);
				}
			}
			if (_showSignEditor && (Object)(object)_editingSign != (Object)null)
			{
				_signWindow = GUI.Window(SignWindowId, _signWindow, new WindowFunction(DrawSignWindow), "ChallengeHub Schildwerkstatt");
			}
		}

		private void DrawSignWindow(int id)
		{
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			TalentData localData = TalentStore.GetLocalData();
			if (localData == null)
			{
				CloseSignEditor();
				return;
			}
			GUILayout.Label("Text (max. 120 Zeichen)", Array.Empty<GUILayoutOption>());
			_signText = GUILayout.TextArea(_signText ?? string.Empty, 120, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(80f) });
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Farbe", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) });
			localData.SignColor = GUILayout.TextField(localData.SignColor ?? "#FFFFFF", 9, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) });
			GUILayout.Label("Größe", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) });
			if (int.TryParse(GUILayout.TextField(localData.SignFontSize.ToString(CultureInfo.InvariantCulture), 3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }), out var result))
			{
				localData.SignFontSize = Mathf.Clamp(result, 12, AdvancedQoLFeature.SignMaxFontSize);
			}
			localData.SignBold = GUILayout.Toggle(localData.SignBold, "Fett", Array.Empty<GUILayoutOption>());
			localData.SignItalic = GUILayout.Toggle(localData.SignItalic, "Kursiv", Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Links", Array.Empty<GUILayoutOption>()))
			{
				localData.SignAlignment = 0;
			}
			if (GUILayout.Button("Mitte", Array.Empty<GUILayoutOption>()))
			{
				localData.SignAlignment = 1;
			}
			if (GUILayout.Button("Rechts", Array.Empty<GUILayoutOption>()))
			{
				localData.SignAlignment = 2;
			}
			GUILayout.EndHorizontal();
			GUILayout.Label("Vorschau:", Array.Empty<GUILayoutOption>());
			GUILayout.Box(AdvancedQoLFeature.BuildSignRichText(_signText, localData), _signPreviewStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(70f) });
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			for (int i = 0; i < 3; i++)
			{
				int index = i;
				if (GUILayout.Button("Vorlage " + (i + 1), Array.Empty<GUILayoutOption>()))
				{
					ApplyOrSavePreset(localData, index, Event.current.shift);
				}
			}
			GUILayout.EndHorizontal();
			GUILayout.Label("Shift+Klick speichert den aktuellen Stil in die Vorlage.", Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Speichern", Array.Empty<GUILayoutOption>()))
			{
				try
				{
					_editingSign.SetText(AdvancedQoLFeature.BuildSignRichText(_signText, localData));
					MeadowsSettlementFeature.TouchLocal(((Component)_editingSign).transform.position, "qol_sign");
				}
				catch (Exception ex)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("Schildtext konnte nicht gespeichert werden: " + ex.Message));
					}
				}
				TalentStore.SaveLocalSettings();
				CloseSignEditor();
			}
			if (GUILayout.Button("Abbrechen", Array.Empty<GUILayoutOption>()))
			{
				CloseSignEditor();
			}
			GUILayout.EndHorizontal();
			GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _signWindow)).width, 22f));
		}

		private static void ApplyOrSavePreset(TalentData data, int index, bool save)
		{
			while (data.SignStylePresets.Count <= index)
			{
				data.SignStylePresets.Add(new SignStylePresetData
				{
					Name = "Vorlage " + (data.SignStylePresets.Count + 1)
				});
			}
			SignStylePresetData signStylePresetData = data.SignStylePresets[index];
			if (save)
			{
				signStylePresetData.Color = data.SignColor;
				signStylePresetData.FontSize = data.SignFontSize;
				signStylePresetData.Bold = data.SignBold;
				signStylePresetData.Italic = data.SignItalic;
				signStylePresetData.Alignment = data.SignAlignment;
				signStylePresetData.Normalize();
				TalentStore.SaveLocalSettings();
			}
			else
			{
				data.SignColor = signStylePresetData.Color;
				data.SignFontSize = signStylePresetData.FontSize;
				data.SignBold = signStylePresetData.Bold;
				data.SignItalic = signStylePresetData.Italic;
				data.SignAlignment = signStylePresetData.Alignment;
			}
		}

		private void OnDestroy()
		{
			CloseSignEditor();
			ClearGridPreview();
			if ((Object)(object)_gridPreviewMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_gridPreviewMaterial);
			}
			_gridPreviewMaterial = null;
		}
	}
	[HarmonyPatch(typeof(Plant), "GetHoverText")]
	internal static class QoLPlantProgressPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Plant __instance, ref string __result)
		{
			__result += AdvancedQoLFeature.BuildPlantProgress(__instance);
		}
	}
	[HarmonyPatch(typeof(Pickable), "GetHoverText")]
	internal static class QoLPickableProgressPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Pickable __instance, ref string __result)
		{
			__result += AdvancedQoLFeature.BuildPickableProgress(__instance);
		}
	}
	[HarmonyPatch(typeof(Pickable), "Interact")]
	internal static class QoLAreaHarvestPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Pickable __instance, Humanoid character, bool repeat, bool alt)
		{
			Player val = (Player)(object)((character is Player) ? character : null);
			if (!(!alt || repeat) && !((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && TalentStore.HasSkill("comfort.area_harvest"))
			{
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					((MonoBehaviour)instance).StartCoroutine(AdvancedQoLFeature.HarvestArea(__instance, val));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Door), "Interact")]
	internal static class QoLDoorGuardianPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Door __instance, Humanoid character, bool hold, ref bool __result)
		{
			Player val = (Player)(object)((character is Player) ? character : null);
			if (!__result || hold || (Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !TalentStore.HasSkill("comfort.door_guardian"))
			{
				return;
			}
			object obj;
			if (__instance == null)
			{
				obj = null;
			}
			else
			{
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				obj = ((component != null) ? component.GetZDO() : null);
			}
			ZDO val2 = (ZDO)obj;
			if (val2 != null && val2.GetInt(ZDOVars.s_state, 0) != 0)
			{
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					((Component)instance).GetComponent<AdvancedQoLBehaviour>()?.ScheduleDoor(__instance);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
	internal static class QoLFineRotationGhostPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				AdvancedQoLFeature.ApplyFineRotation(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "PlacePiece", new Type[]
	{
		typeof(Piece),
		typeof(Vector3),
		typeof(Quaternion),
		typeof(bool)
	})]
	internal static class QoLGridPlantingPatch
	{
		internal sealed class State
		{
			internal Piece Piece;

			internal Vector3 Position;

			internal Quaternion Rotation;

			internal bool Requested;

			internal int Size;
		}

		[HarmonyPrefix]
		private static void Prefix(Player __instance, out State __state)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && TalentStore.HasSkill("building.grid_planting") && (Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307)))
			{
				Piece selectedPiece = PlayerBuildReflection.GetSelectedPiece(__instance);
				GameObject placementGhost = PlayerBuildReflection.GetPlacementGhost(__instance);
				if (!((Object)(object)selectedPiece == (Object)null) && !((Object)(object)placementGhost == (Object)null) && !((Object)(object)((Component)selectedPiece).GetComponentInChildren<Plant>(true) == (Object)null))
				{
					TalentData localData = TalentStore.GetLocalData();
					__state = new State
					{
						Piece = selectedPiece,
						Position = placementGhost.transform.position,
						Rotation = placementGhost.transform.rotation,
						Requested = true,
						Size = (localData?.FarmingGridSize ?? 3)
					};
				}
			}
		}

		[HarmonyPostfix]
		private static void Postfix(Player __instance, State __state)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (__state != null && __state.Requested && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				Plugin instance = Plugin.Instance;
				if (instance != null)
				{
					((MonoBehaviour)instance).StartCoroutine(AdvancedQoLFeature.PlantGridExtras(__instance, __state.Piece, __state.Position, __state.Rotation, __state.Size));
				}
			}
		}
	}
	internal static class StartupWelcomeFeature
	{
		internal static void Initialize(Plugin plugin)
		{
			if (!((Object)(object)plugin == (Object)null) && !Application.isBatchMode && (Object)(object)((Component)plugin).GetComponent<BlutEidStartupBehaviour>() == (Object)null)
			{
				((Component)plugin).gameObject.AddComponent<BlutEidStartupBehaviour>();
			}
		}
	}
	internal sealed class BlutEidStartupBehaviour : MonoBehaviour
	{
		[Serializable]
		private sealed class MainMenuCharacterRegistration
		{
			public string action;

			public string clientVersion;

			public string challengeShortCode;

			public string linkCode;

			public string characterId;

			public string characterName;
		}

		[Serializable]
		private sealed class DeathRunEnvelope
		{
			public bool ok;

			public bool worldReady;

			public DeathRunWeb run;
		}

		[Serializable]
		private sealed class DeathRunWeb
		{
			public string id;

			public string worldCode;

			public string worldPackageStatus;
		}

		internal static bool CharacterSelected;

		private Texture2D _image;

		private Texture2D _panel;

		private GUIStyle _button;

		private GUIStyle _title;

		private GUIStyle _status;

		private GUIStyle _version;

		private string _message = "Vorzeichen des Nordens · Charakter auswählen und Challenge-Welt starten";

		private bool _resumeRunning;

		private string _installedWorldCode = string.Empty;

		private void OnGUI()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			if (IsMainMenuHome())
			{
				EnsureAssets();
				float num = Mathf.Clamp((float)Screen.width * 0.34f, 390f, 610f);
				float num2 = num / 1.777f;
				Rect val = default(Rect);
				((Rect)(ref val))..ctor((float)Screen.width - num - 24f, 24f, num, num2 + 128f);
				GUI.DrawTexture(val, (Texture)(object)_panel, (ScaleMode)0);
				Rect val2 = default(Rect);
				((Rect)(ref val2))..ctor(((Rect)(ref val)).x + 6f, ((Rect)(ref val)).y + 6f, num - 12f, num2 - 2f);
				if ((Object)(object)_image != (Object)null)
				{
					GUI.DrawTexture(val2, (Texture)(object)_image, (ScaleMode)2, true);
				}
				if (GUI.Button(val2, GUIContent.none, _button))
				{
					HandleCardClick();
				}
				GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val2)).yMax + 8f, num - 36f, 34f), "Der Blut-Eid: Vorzeichen des Nordens", _title);
				GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val2)).yMax + 45f, num - 36f, 42f), _message, _status);
				GUI.Label(new Rect(((Rect)(ref val)).x + 18f, ((Rect)(ref val2)).yMax + 91f, num - 36f, 24f), "ChallengeHub Blut-Eid v2.12.50  ·  Modpaket aktuell", _version);
			}
		}

		private static bool IsMainMenuHome()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			if (string.Equals(((Scene)(ref activeScene)).name, "start", StringComparison.OrdinalIgnoreCase))
			{
				return (Object)(object)Player.m_localPlayer == (Object)null;
			}
			return false;
		}

		private void HandleCardClick()
		{
			FejdStartup startup = Object.FindFirstObjectByType<FejdStartup>();
			if ((Object)(object)startup == (Object)null)
			{
				_message = "Valheim-Hauptmenü ist noch nicht bereit";
				return;
			}
			if (!string.IsNullOrWhiteSpace(_installedWorldCode))
			{
				RefreshAndSelectWorld(startup, _installedWorldCode);
				return;
			}
			if (!CharacterSelected)
			{
				_message = "Schritt 1 von 2 · Charakter auswählen";
				AccessTools.Method(typeof(FejdStartup), "ShowCharacterSelection", (Type[])null, (Type[])null)?.Invoke(startup, null);
				return;
			}
			PlayerLinkSetupFeature playerLinkSetupFeature = (((Object)(object)Plugin.Instance != (Object)null) ? ((Component)Plugin.Instance).GetComponent<PlayerLinkSetupFeature>() : null);
			_message = "Schritt 2 von 2 · Blut-Eid-Welt wird eingerichtet ...";
			if ((Object)(object)playerLinkSetupFeature == (Object)null)
			{
				_message = "ChallengeHub-Verbindung ist nicht bereit";
				return;
			}
			playerLinkSetupFeature.SelectDeathRunFromMainMenu(delegate(string message)
			{
				if (message.IndexOf("eingerichtet", StringComparison.OrdinalIgnoreCase) < 0)
				{
					_message = message;
				}
				else
				{
					((MonoBehaviour)this).StartCoroutine(RegisterSelectedCharacterAndOpenWebApp(startup));
				}
			});
		}

		private IEnumerator RegisterSelectedCharacterAndOpenWebApp(FejdStartup startup)
		{
			PlayerProfile val = SelectedProfile(startup);
			if (val == null)
			{
				CharacterSelected = false;
				_message = "Bitte zuerst einen Charakter auswählen";
				yield break;
			}
			string text = ((Plugin.PlayerLinkCode != null) ? (Plugin.PlayerLinkCode.Value ?? string.Empty).Trim() : string.Empty);
			if (string.IsNullOrWhiteSpace(text))
			{
				_message = "Player-Link-Code fehlt · bitte im ChallengeHub-Dialog eintragen";
				yield break;
			}
			string characterId = val.GetPlayerID().ToString();
			string characterName = val.GetName() ?? "Valheim-Charakter";
			MainMenuCharacterRegistration value = new MainMenuCharacterRegistration
			{
				action = "register",
				challengeShortCode = "BLUTEID",
				linkCode = text,
				characterId = characterId,
				characterName = characterName,
				clientVersion = "2.12.50"
			};
			string text2 = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/admission";
			byte[] bytes = Encoding.UTF8.GetBytes(ChallengeHubJson.Serialize(value));
			UnityWebRequest request = new UnityWebRequest(text2, "POST");
			try
			{
				request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes);
				request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
				request.SetRequestHeader("Content-Type", "application/json");
				request.SetRequestHeader("x-challengehub-key", Plugin.ApiKey.Value ?? string.Empty);
				request.timeout = 15;
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					_message = ((request.responseCode == 401) ? "Player-Link-Code oder Challenge-Zugang ungültig" : "Charakter konnte nicht übertragen werden");
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("Blut-Eid Charakterregistrierung fehlgeschlagen: HTTP " + request.responseCode + " " + request.error));
					}
					yield break;
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
			_message = characterName + " übertragen · Schwierigkeit in der Webapp wählen";
			Application.OpenURL(Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/deathrun?characterId=" + UnityWebRequest.EscapeURL(characterId) + "&source=valheim");
			yield return WaitForPreparedWorld(startup, characterId);
		}

		private IEnumerator WaitForPreparedWorld(FejdStartup startup, string characterId)
		{
			float deadline = Time.realtimeSinceStartup + 1800f;
			while (Time.realtimeSinceStartup < deadline)
			{
				string text = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/deathrun?characterId=" + UnityWebRequest.EscapeURL(characterId);
				UnityWebRequest request = UnityWebRequest.Get(text);
				try
				{
					ApplyMainMenuAuthorization(request);
					request.timeout = 12;
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)"Blut-Eid: Run-Status wird direkt bei ChallengeHub abgefragt.");
					}
					yield return request.SendWebRequest();
					if ((int)request.result == 1)
					{
						DeathRunEnvelope deathRunEnvelope = ChallengeHubJson.Deserialize<DeathRunEnvelope>(request.downloadHandler.text);
						if (deathRunEnvelope != null && deathRunEnvelope.ok && deathRunEnvelope.run != null)
						{
							if (deathRunEnvelope.worldReady || string.Equals(deathRunEnvelope.run.worldPackageStatus, "ready", StringComparison.OrdinalIgnoreCase))
							{
								yield return DownloadAndInstallWorld(startup, deathRunEnvelope.run);
								yield break;
							}
							_message = "Run " + deathRunEnvelope.run.worldCode + " · Welt wird vorbereitet ...";
						}
					}
				}
				finally
				{
					((IDisposable)request)?.Dispose();
				}
				yield return (object)new WaitForSecondsRealtime(4f);
			}
			_message = "Weltvorbereitung dauert länger · Valheim kann geöffnet bleiben";
		}

		internal void ResumeSelectedRun(FejdStartup startup, PlayerProfile selectedProfile)
		{
			if (!_resumeRunning && (Object)(object)startup != (Object)null && selectedProfile != null)
			{
				((MonoBehaviour)this).StartCoroutine(ResumeSelectedRunRoutine(startup, selectedProfile));
			}
		}

		private IEnumerator ResumeSelectedRunRoutine(FejdStartup startup, PlayerProfile selectedProfile)
		{
			_resumeRunning = true;
			yield return (object)new WaitForSecondsRealtime(0.35f);
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("Blut-Eid: aktiver Run wird für Charakter " + (selectedProfile.GetName() ?? "unbekannt") + " gesucht."));
			}
			string characterId = selectedProfile.GetPlayerID().ToString();
			string text = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/deathrun?characterId=" + UnityWebRequest.EscapeURL(characterId);
			UnityWebRequest request = UnityWebRequest.Get(text);
			try
			{
				ApplyMainMenuAuthorization(request);
				request.timeout = 12;
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"Blut-Eid: vorhandener Run wird direkt bei ChallengeHub abgefragt.");
				}
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogWarning((object)("Blut-Eid Run-Wiederaufnahme fehlgeschlagen: HTTP " + request.responseCode + " " + request.error));
					}
					_resumeRunning = false;
					yield break;
				}
				DeathRunEnvelope deathRunEnvelope = ChallengeHubJson.Deserialize<DeathRunEnvelope>(request.downloadHandler.text);
				if (deathRunEnvelope == null || !deathRunEnvelope.ok || deathRunEnvelope.run == null)
				{
					_resumeRunning = false;
					yield break;
				}
				_message = "Aktiver Run " + deathRunEnvelope.run.worldCode + " wird fortgesetzt ...";
				bool flag = deathRunEnvelope.worldReady || string.Equals(deathRunEnvelope.run.worldPackageStatus, "ready", StringComparison.OrdinalIgnoreCase);
				ManualLogSource log4 = Plugin.Log;
				if (log4 != null)
				{
					log4.LogInfo((object)("Blut-Eid: Run-Antwort empfangen; ready=" + flag + "; worldReady=" + deathRunEnvelope.worldReady + "; packageStatus=" + (deathRunEnvelope.run.worldPackageStatus ?? "<leer>")));
				}
				if (flag)
				{
					string path = Path.Combine(Application.persistentDataPath, "worlds_local");
					string path2 = Path.Combine(path, deathRunEnvelope.run.worldCode + ".db");
					string path3 = Path.Combine(path, deathRunEnvelope.run.worldCode + ".fwl");
					if (File.Exists(path2) && File.Exists(path3))
					{
						_installedWorldCode = deathRunEnvelope.run.worldCode;
						RefreshAndSelectWorld(startup, deathRunEnvelope.run.worldCode);
					}
					else
					{
						_message = deathRunEnvelope.run.worldCode + " fehlt lokal · wird erneut heruntergeladen ...";
						yield return DownloadAndInstallWorld(startup, deathRunEnvelope.run);
					}
				}
				else
				{
					yield return WaitForPreparedWorld(startup, characterId);
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
			_resumeRunning = false;
		}

		private static void ApplyMainMenuAuthorization(UnityWebRequest request)
		{
			string text = ((Plugin.PlayerLinkCode != null) ? (Plugin.PlayerLinkCode.Value ?? string.Empty).Trim() : string.Empty);
			request.SetRequestHeader("x-challengehub-key", Plugin.ApiKey.Value ?? string.Empty);
			request.SetRequestHeader("x-player-link-code", text);
		}

		private IEnumerator DownloadAndInstallWorld(FejdStartup startup, DeathRunWeb run)
		{
			_message = run.worldCode + " · Welt wird heruntergeladen ...";
			string text = Plugin.ApiBaseUrl.Value.TrimEnd(new char[1] { '/' }) + "/api/valheim/deathrun/world?runId=" + UnityWebRequest.EscapeURL(run.id);
			UnityWebRequest request = UnityWebRequest.Get(text);
			try
			{
				ApplyMainMenuAuthorization(request);
				request.timeout = 60;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Blut-Eid: fertiges Weltpaket wird direkt heruntergeladen.");
				}
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					_message = "Welt-Download fehlgeschlagen · erneut auf die Kachel klicken";
					yield break;
				}
				try
				{
					string text2 = Path.Combine(Application.persistentDataPath, "worlds_local");
					Directory.CreateDirectory(text2);
					using (MemoryStream stream = new MemoryStream(request.downloadHandler.data, writable: false))
					{
						using ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: false);
						int num = 0;
						foreach (ZipArchiveEntry entry in zipArchive.Entries)
						{
							string fileName = Path.GetFileName(entry.FullName);
							string extension = Path.GetExtension(fileName);
							if (string.IsNullOrWhiteSpace(fileName) || (!extension.Equals(".db", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".fwl", StringComparison.OrdinalIgnoreCase)))
							{
								continue;
							}
							string path = Path.Combine(text2, fileName);
							using (Stream stream2 = entry.Open())
							{
								using FileStream destination = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
								stream2.CopyTo(destination);
							}
							num++;
						}
						if (num < 2)
						{
							throw new InvalidDataException("Weltpaket enthält keine vollständige .db/.fwl-Welt.");
						}
					}
					_installedWorldCode = run.worldCode;
					RefreshAndSelectWorld(startup, run.worldCode);
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)("Blut-Eid-Welt automatisch installiert: " + run.worldCode));
					}
				}
				catch (Exception ex)
				{
					_message = "Welt konnte lokal nicht installiert werden";
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogWarning((object)("Automatische Blut-Eid-Weltinstallation fehlgeschlagen: " + ex));
					}
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private void RefreshAndSelectWorld(FejdStartup startup, string worldCode)
		{
			try
			{
				SaveSystem.InvalidateCache();
				SaveSystem.ClearWorldListCache(false);
				AccessTools.Method(typeof(FejdStartup), "ShowStartGame", (Type[])null, (Type[])null)?.Invoke(startup, null);
				AccessTools.Method(typeof(FejdStartup), "UpdateWorldList", new Type[1] { typeof(bool) }, (Type[])null)?.Invoke(startup, new object[1] { false });
				FieldInfo fieldInfo = AccessTools.Field(typeof(FejdStartup), "m_worlds");
				IList<World> list = ((fieldInfo != null) ? (fieldInfo.GetValue(startup) as IList<World>) : null);
				FieldInfo fieldInfo2 = AccessTools.Field(typeof(World), "m_fileName");
				FieldInfo fieldInfo3 = AccessTools.Field(typeof(World), "m_name");
				int num = -1;
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						World obj = list[i];
						string a = ((fieldInfo2 != null) ? (fieldInfo2.GetValue(obj) as string) : string.Empty);
						string a2 = ((fieldInfo3 != null) ? (fieldInfo3.GetValue(obj) as string) : string.Empty);
						if (string.Equals(a, worldCode, StringComparison.OrdinalIgnoreCase) || string.Equals(a2, worldCode, StringComparison.OrdinalIgnoreCase))
						{
							num = i;
							break;
						}
					}
				}
				if (num < 0)
				{
					_message = worldCode + " installiert · erneut auf die Kachel klicken";
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("Blut-Eid: installierte Welt wurde nach UpdateWorldList nicht gefunden: " + worldCode));
					}
					return;
				}
				AccessTools.Method(typeof(FejdStartup), "SetSelectedWorld", new Type[2]
				{
					typeof(int),
					typeof(bool)
				}, (Type[])null)?.Invoke(startup, new object[2] { num, true });
				AccessTools.Method(typeof(FejdStartup), "RefreshWorldSelection", (Type[])null, (Type[])null)?.Invoke(startup, null);
				_message = worldCode + " ausgewählt · Multiplayer und Passwort festlegen, dann Start drücken";
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("Blut-Eid: Weltliste aktualisiert und Run-Welt ausgewählt: " + worldCode));
				}
			}
			catch (Exception ex)
			{
				_message = "Weltliste konnte nicht aktualisiert werden · erneut auf die Kachel klicken";
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogWarning((object)("Blut-Eid Weltlisten-Aktualisierung fehlgeschlagen: " + ex));
				}
			}
		}

		internal static PlayerProfile SelectedProfile(FejdStartup startup)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(FejdStartup), "m_profiles");
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(FejdStartup), "m_profileIndex");
			IList<PlayerProfile> list = ((fieldInfo != null) ? (fieldInfo.GetValue(startup) as IList<PlayerProfile>) : null);
			int num = ((fieldInfo2 != null) ? Convert.ToInt32(fieldInfo2.GetValue(startup)) : (-1));
			if (list == null || num < 0 || num >= list.Count)
			{
				return null;
			}
			return list[num];
		}

		private void EnsureAssets()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_0091: 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_00b6: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: 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_012c: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			if (_button != null)
			{
				return;
			}
			_button = new GUIStyle(GUI.skin.button);
			GUIStyleState normal = _button.normal;
			GUIStyleState hover = _button.hover;
			Texture2D val = (_button.active.background = null);
			Texture2D background = (hover.background = val);
			normal.background = background;
			_button.focused.background = null;
			_panel = new Texture2D(1, 1, (TextureFormat)4, false);
			_panel.SetPixel(0, 0, new Color(0.035f, 0.045f, 0.04f, 0.94f));
			_panel.Apply();
			_title = new GUIStyle(GUI.skin.label)
			{
				alignment = (TextAnchor)4,
				fontSize = 27,
				fontStyle = (FontStyle)1
			};
			_title.normal.textColor = new Color(0.96f, 0.77f, 0.35f, 1f);
			_status = new GUIStyle(GUI.skin.label)
			{
				alignment = (TextAnchor)4,
				fontSize = 17,
				fontStyle = (FontStyle)1,
				wordWrap = true
			};
			_status.normal.textColor = new Color(0.55f, 0.86f, 0.62f, 1f);
			_version = new GUIStyle(GUI.skin.label)
			{
				alignment = (TextAnchor)4,
				fontSize = 14,
				fontStyle = (FontStyle)1
			};
			_version.normal.textColor = new Color(0.9f, 0.91f, 0.86f, 1f);
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ChallengeHubValheim.Assets.BlutEid.png");
				using MemoryStream memoryStream = new MemoryStream();
				stream?.CopyTo(memoryStream);
				_image = new Texture2D(2, 2, (TextureFormat)4, false);
				if (stream == null || !LoadImageCompat(_image, memoryStream.ToArray()))
				{
					Object.Destroy((Object)(object)_image);
					_image = null;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Blut-Eid-Titelbild konnte nicht geladen werden: " + ex.Message));
				}
			}
		}

		private static bool LoadImageCompat(Texture2D texture, byte[] bytes)
		{
			Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule", throwOnError: false);
			MethodInfo methodInfo = ((type != null) ? type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
			{
				typeof(Texture2D),
				typeof(byte[]),
				typeof(bool)
			}, null) : null);
			if (methodInfo == null)
			{
				return false;
			}
			object obj = methodInfo.Invoke(null, new object[3] { texture, bytes, true });
			if (obj is bool)
			{
				return (bool)obj;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "OnSelelectCharacterBack")]
	internal static class BlutEidShowCardOnMainMenuReturnPatch
	{
		private static void Postfix()
		{
			BlutEidStartupBehaviour.CharacterSelected = false;
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "OnCharacterStart")]
	internal static class BlutEidCharacterStartFallbackPatch
	{
		private static void Prefix(FejdStartup __instance, ref PlayerProfile __state)
		{
			__state = BlutEidStartupBehaviour.SelectedProfile(__instance);
		}

		private static void Postfix(FejdStartup __instance, PlayerProfile __state)
		{
			BlutEidStartupBehaviour.CharacterSelected = true;
			BlutEidStartupBehaviour blutEidStartupBehaviour = (((Object)(object)Plugin.Instance != (Object)null) ? ((Component)Plugin.Instance).GetComponent<BlutEidStartupBehaviour>() : null);
			if ((Object)(object)blutEidStartupBehaviour != (Object)null && __state != null)
			{
				blutEidStartupBehaviour.ResumeSelectedRun(__instance, __state);
				return;
			}
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)"Blut-Eid: ausgewähltes Charakterprofil konnte beim Start nicht übernommen werden.");
			}
		}

		private static IEnumerator OpenWorldSelectionIfStillClosed(FejdStartup startup)
		{
			yield return (object)new WaitForSecondsRealtime(0.35f);
			if ((Object)(object)startup == (Object)null)
			{
				yield break;
			}
			Scene activeScene = SceneManager.GetActiveScene();
			if (!string.Equals(((Scene)(ref activeScene)).name, "start", StringComparison.OrdinalIgnoreCase))
			{
				yield break;
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(FejdStartup), "m_startGamePanel");
			GameObject val = (GameObject)((fieldInfo != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val != (Object)null && val.activeInHierarchy)
			{
				yield break;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(FejdStartup), "ShowStartGame", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"Blut-Eid: FejdStartup.ShowStartGame wurde nicht gefunden.");
				}
				yield break;
			}
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)"Blut-Eid: Weltwahl-Fallback nach Charakterauswahl aktiviert.");
			}
			methodInfo.Invoke(startup, null);
		}
	}
	[Serializable]
	internal sealed class BossArenaResetRecord
	{
		public string id = string.Empty;

		public string boss = string.Empty;

		public float x;

		public float y;

		public float z;

		public string queuedAtUtc = string.Empty;
	}
	[Serializable]
	internal sealed class BossArenaResetStore
	{
		public BossArenaResetRecord[] pending = new BossArenaResetRecord[0];
	}
	internal static class BossArenaResetFeature
	{
		private static Plugin _plugin;

		private static ConfigEntry<bool> _enabled;

		private static ConfigEntry<float> _radius;

		private static ConfigEntry<float> _clearSeconds;

		private static readonly List<BossArenaResetRecord> Pending = new List<BossArenaResetRecord>();

		private static readonly Dictionary<string, float> ClearSince = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private static bool _loaded;

		private static bool _processing;

		private static string StorePath => Path.Combine(Paths.ConfigPath, "ChallengeHubValheim", "boss-arena-resets.json");

		private static float Radius => Mathf.Clamp(_radius?.Value ?? 100f, 25f, 200f);

		internal static void Initialize(Plugin plugin)
		{
			_plugin = plugin;
			_enabled = ((BaseUnityPlugin)plugin).Config.Bind<bool>("BossArenaReset", "Enabled", true, "Setzt den Bossbereich nach dem Bosskill zurueck, sobald kein Spieler mehr im Radius ist.");
			_radius = ((BaseUnityPlugin)plugin).Config.Bind<float>("BossArenaReset", "Radius", 100f, "Radius des Bossbereichs in Metern.");
			_clearSeconds = ((BaseUnityPlugin)plugin).Config.Bind<float>("BossArenaReset", "PlayerFreeConfirmSeconds", 5f, "Durchgehend spielerfreie Zeit vor dem Reset.");
			((MonoBehaviour)plugin).StartCoroutine(Loop());
		}

		internal static void Queue(Vector3 center, string boss, string completionId)
		{
			//IL_0058: 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_00d0: 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)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_plugin == (Object)null || _enabled == null || !_enabled.Value || !ChallengeHubWorldState.IsServer)
			{
				return;
			}
			EnsureLoaded();
			string id = (string.IsNullOrWhiteSpace(completionId) ? (Plugin.NormalizeKey(boss) + ":" + Mathf.RoundToInt(center.x) + ":" + Mathf.RoundToInt(center.z)) : completionId);
			if (!Pending.Any((BossArenaResetRecord record) => string.Equals(record.id, id, StringComparison.OrdinalIgnoreCase)))
			{
				Pending.Add(new BossArenaResetRecord
				{
					id = id,
					boss = Plugin.CanonicalBossKey(boss),
					x = center.x,
					y = center.y,
					z = center.z,
					queuedAtUtc = DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)
				});
				Save();
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("Bossbereich-Reset vorgemerkt: " + boss + "; Radius=" + Radius.ToString("0", CultureInfo.InvariantCulture) + "m."));
				}
			}
		}

		private static IEnumerator Loop()
		{
			Vector3 center = default(Vector3);
			while (true)
			{
				yield return (object)new WaitForSeconds(2f);
				if (_processing || _enabled == null || !_enabled.Value || !ChallengeHubWorldState.IsServer || !ChallengeHubServerGateFeature.GameplayAllowed || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null)
				{
					continue;
				}
				EnsureLoaded();
				BossArenaResetRecord bossArenaResetRecord = Pending.FirstOrDefault();
				if (bossArenaResetRecord != null)
				{
					((Vector3)(ref center))..ctor(bossArenaResetRecord.x, bossAren