Decompiled source of RunicBuildCamera v1.0.9

RunicBuildCamera.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Runic.Localization;
using RunicBuildCamera.Core;
using RunicBuildCamera.Integration;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("RunicBuildCamera")]
[assembly: AssemblyDescription("A standalone detached build camera for Valheim.")]
[assembly: AssemblyCompany("Chazman Mods")]
[assembly: AssemblyProduct("Runic Build Camera")]
[assembly: AssemblyCopyright("Copyright © 2026 Chazman")]
[assembly: ComVisible(false)]
[assembly: Guid("9e457d0a-f7b7-475f-91f0-16f97680663c")]
[assembly: AssemblyFileVersion("1.0.8.0")]
[assembly: AssemblyInformationalVersion("1.0.8")]
[assembly: InternalsVisibleTo("RunicBuildCamera.Tests")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.8.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 Runic.Localization
{
	internal sealed class LanguageCatalog
	{
		private readonly Dictionary<string, string> _english;

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

		private readonly Func<string, string> _read;

		private string _language;

		internal LanguageCatalog(string english, Func<string, string> read)
		{
			_english = Parse(english);
			_read = read;
		}

		internal static Dictionary<string, string> Parse(string json)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				return new Dictionary<string, string>();
			}
			if (json.Length > 2097152)
			{
				throw new InvalidDataException("Language file exceeds 2 MiB.");
			}
			DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(Dictionary<string, string>), new DataContractJsonSerializerSettings
			{
				UseSimpleDictionaryFormat = true
			});
			using MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json.TrimStart('\ufeff')));
			return ((Dictionary<string, string>)dataContractJsonSerializer.ReadObject(stream)) ?? new Dictionary<string, string>();
		}

		internal void Select(string language)
		{
			language = (string.IsNullOrWhiteSpace(language) ? "English" : language);
			if (string.Equals(language, _language, StringComparison.Ordinal))
			{
				return;
			}
			_language = language;
			_selected = new Dictionary<string, string>();
			if (language.IndexOfAny(new char[4] { '/', '\\', ':', '\0' }) >= 0 || language == "." || language == "..")
			{
				return;
			}
			try
			{
				_selected = Parse(_read(language));
			}
			catch (Exception)
			{
			}
		}

		internal string Get(string key)
		{
			_english.TryGetValue(key, out var value);
			if (_selected.TryGetValue(key, out var value2) && !string.IsNullOrWhiteSpace(value2) && (value == null || SameFields(value, value2)))
			{
				return value2;
			}
			return value ?? key;
		}

		internal string English(string key)
		{
			if (!_english.TryGetValue(key, out var value))
			{
				return key;
			}
			return value;
		}

		internal string TranslateEnglish(string text)
		{
			foreach (KeyValuePair<string, string> item in _english)
			{
				if (item.Value == text)
				{
					return Get(item.Key);
				}
			}
			return text;
		}

		internal string Format(string key, params object[] args)
		{
			_english.TryGetValue(key, out var value);
			string text = Get(key);
			if (value != null && !SameFields(value, text))
			{
				text = value;
			}
			try
			{
				return string.Format(CultureInfo.CurrentCulture, text, args);
			}
			catch (FormatException)
			{
				try
				{
					return string.Format(CultureInfo.CurrentCulture, value ?? key, args);
				}
				catch (FormatException)
				{
					return value ?? key;
				}
			}
		}

		private static bool SameFields(string a, string b)
		{
			try
			{
				return Fields(a).SetEquals(Fields(b));
			}
			catch (FormatException)
			{
				return false;
			}
		}

		private static HashSet<int> Fields(string text)
		{
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < text.Length; i++)
			{
				if (text[i] == '}')
				{
					if (i + 1 >= text.Length || text[i + 1] != '}')
					{
						throw new FormatException();
					}
					i++;
				}
				else
				{
					if (text[i] != '{')
					{
						continue;
					}
					if (i + 1 < text.Length && text[i + 1] == '{')
					{
						i++;
						continue;
					}
					int num = ++i;
					for (; i < text.Length && char.IsDigit(text[i]); i++)
					{
					}
					if (num == i || !int.TryParse(text.Substring(num, i - num), out var result))
					{
						throw new FormatException();
					}
					hashSet.Add(result);
					for (; i < text.Length && text[i] != '}'; i++)
					{
						if (text[i] == '{')
						{
							throw new FormatException();
						}
					}
					if (i == text.Length)
					{
						throw new FormatException();
					}
				}
			}
			return hashSet;
		}
	}
	internal static class RunicText
	{
		private static readonly Assembly Owner = typeof(RunicText).Assembly;

		private static readonly string Module = Owner.GetName().Name;

		private static readonly LanguageCatalog Catalog = Create();

		private static LanguageCatalog Create()
		{
			using Stream stream = Owner.GetManifestResourceStream("Runic.Localization.English.json");
			using StreamReader streamReader = ((stream == null) ? null : new StreamReader(stream));
			return new LanguageCatalog(streamReader?.ReadToEnd() ?? "{}", ReadLanguage);
		}

		private static string ReadLanguage(string language)
		{
			string path = Path.Combine(Path.Combine(Path.GetDirectoryName(Owner.Location), "Translations", Module), language + ".json");
			if (!File.Exists(path))
			{
				return null;
			}
			return File.ReadAllText(path);
		}

		private static void SelectLanguage()
		{
			string language = "English";
			try
			{
				Localization instance = Localization.instance;
				language = ((instance != null) ? instance.GetSelectedLanguage() : null) ?? "English";
			}
			catch
			{
			}
			Catalog.Select(language);
		}

		internal static string Get(string key)
		{
			SelectLanguage();
			return Catalog.Get(key);
		}

		internal static string English(string key)
		{
			return Catalog.English(key);
		}

		internal static string TranslateEnglish(string text)
		{
			SelectLanguage();
			return Catalog.TranslateEnglish(text);
		}

		internal static string Format(string key, params object[] args)
		{
			SelectLanguage();
			return Catalog.Format(key, args);
		}
	}
}
namespace RunicBuildCamera
{
	internal static class CompatibilityGuard
	{
		internal const string BuildCameraCheGuid = "Azumatt.BuildCameraCHE";

		internal static bool TryFindHardConflict(out string reason)
		{
			reason = null;
			foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
			{
				PluginInfo value = pluginInfo.Value;
				object a;
				if (value == null)
				{
					a = null;
				}
				else
				{
					BepInPlugin metadata = value.Metadata;
					a = ((metadata != null) ? metadata.GUID : null);
				}
				PluginInfo value2 = pluginInfo.Value;
				object obj;
				if (value2 == null)
				{
					obj = null;
				}
				else
				{
					BepInPlugin metadata2 = value2.Metadata;
					obj = ((metadata2 != null) ? metadata2.Name : null);
				}
				string text = (string)obj;
				if (string.Equals((string?)a, "Azumatt.BuildCameraCHE", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "BuildCameraCHE", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "Build Camera Custom Hammers Edition", StringComparison.OrdinalIgnoreCase))
				{
					reason = RunicText.Format("text_acac6a5e4a61", text ?? "Azumatt.BuildCameraCHE") + RunicText.Get("text_ba927d542b6f");
					return true;
				}
			}
			bool flag = false;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					if (assembly.GetType("Valheim_Build_Camera.Valheim_Build_CameraPlugin", throwOnError: false, ignoreCase: false) == null)
					{
						continue;
					}
					flag = true;
					break;
				}
				catch (Exception)
				{
				}
			}
			if (flag)
			{
				reason = RunicText.Get("text_a34f6de98a9a") + RunicText.Get("text_f57f78aa3dfa");
				return true;
			}
			return false;
		}
	}
	internal static class BuildCameraConfig
	{
		internal const float DefaultCameraRange = 60f;

		internal const float CameraRangeHardMaximum = 100f;

		internal const float DefaultRemoteActionDistance = 100f;

		internal const float RemoteActionDistanceHardMaximum = 100f;

		private const float MinimumDistance = 1f;

		private static readonly List<Action> UnsubscribeActions = new List<Action>();

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> ToggleShortcut { get; private set; }

		internal static ConfigEntry<float> CameraRange { get; private set; }

		internal static ConfigEntry<float> MoveSpeed { get; private set; }

		internal static ConfigEntry<float> FastMoveMultiplier { get; private set; }

		internal static ConfigEntry<bool> WorldRelativeMovement { get; private set; }

		internal static ConfigEntry<float> RemoteActionDistance { get; private set; }

		internal static ConfigEntry<bool> PickupEnabled { get; private set; }

		internal static ConfigEntry<float> PickupRange { get; private set; }

		internal static ConfigEntry<float> PickupIntervalSeconds { get; private set; }

		internal static ConfigEntry<bool> DemisterFollowCamera { get; private set; }

		internal static ConfigEntry<float> DemisterRangeMultiplier { get; private set; }

		internal static ConfigEntry<bool> InvertMouseHorizontal { get; private set; }

		internal static ConfigEntry<bool> InvertMouseVertical { get; private set; }

		internal static ConfigEntry<bool> InvertControllerHorizontal { get; private set; }

		internal static ConfigEntry<bool> InvertControllerVertical { get; private set; }

		internal static ConfigEntry<bool> VerboseLogging { get; private set; }

		internal static event Action Changed;

		internal static void Bind(ConfigFile config)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (config == null)
			{
				throw new ArgumentNullException("config");
			}
			UnhookChanges();
			Enabled = BindEntry(config, "General", "Enabled", defaultValue: true, RunicText.Get("text_0a6ae8b067ca"));
			ToggleShortcut = BindEntry<KeyboardShortcut>(config, "Controls", "ToggleShortcut", new KeyboardShortcut((KeyCode)98, Array.Empty<KeyCode>()), RunicText.Get("text_79be68bc27a1"));
			CameraRange = BindRange(config, "Camera", "CameraRange", 60f, 1f, 100f, RunicText.Get("text_1c9f371d5f2c"));
			MoveSpeed = BindRange(config, "Camera", "MoveSpeed", 10f, 0.5f, 50f, RunicText.Get("text_09f482d754a0"));
			FastMoveMultiplier = BindRange(config, "Camera", "FastMoveMultiplier", 3f, 1f, 10f, RunicText.Get("text_9abaa67ab9eb"));
			WorldRelativeMovement = BindEntry(config, "Camera", "WorldRelativeMovement", defaultValue: false, RunicText.Get("text_fdca2a2cae5e"));
			RemoteActionDistance = BindRange(config, "Remote Actions", "RemoteActionDistance", 100f, 1f, 100f, RunicText.Get("text_5cdd426faf6d") + RunicText.Get("text_d77fc99e1978") + RunicText.Get("text_015eb096e1ec"));
			PickupEnabled = BindEntry(config, "Pickup", "PickupEnabled", defaultValue: true, RunicText.Get("text_7e340344c138"));
			PickupRange = BindRange(config, "Pickup", "PickupRange", 10f, 1f, 50f, RunicText.Get("text_1503c4d0efb2"));
			PickupIntervalSeconds = BindRange(config, "Pickup", "PickupIntervalSeconds", 0.25f, 0.05f, 2f, RunicText.Get("text_6ac387048534"));
			DemisterFollowCamera = BindEntry(config, "Mist", "DemisterFollowCamera", defaultValue: true, RunicText.Get("text_2fbfdd579ce5"));
			DemisterRangeMultiplier = BindRange(config, "Mist", "DemisterRangeMultiplier", 2f, 0.25f, 5f, RunicText.Get("text_32c6dc7938c7"));
			InvertMouseHorizontal = BindEntry(config, "Controls", "InvertMouseHorizontal", defaultValue: false, RunicText.Get("text_c3b035822ef5"));
			InvertMouseVertical = BindEntry(config, "Controls", "InvertMouseVertical", defaultValue: false, RunicText.Get("text_41f4b367f85b"));
			InvertControllerHorizontal = BindEntry(config, "Controls", "InvertControllerHorizontal", defaultValue: false, RunicText.Get("text_f3622ea95b67"));
			InvertControllerVertical = BindEntry(config, "Controls", "InvertControllerVertical", defaultValue: false, RunicText.Get("text_908b56428063"));
			VerboseLogging = BindEntry(config, "Diagnostics", "VerboseLogging", defaultValue: false, RunicText.Get("text_2b4b917a0f25"));
		}

		private static ConfigEntry<T> BindEntry<T>(ConfigFile config, string section, string key, T defaultValue, string description)
		{
			ConfigEntry<T> obj = config.Bind<T>(section, key, defaultValue, description);
			HookChange<T>(obj);
			return obj;
		}

		private static ConfigEntry<float> BindRange(ConfigFile config, string section, string key, float defaultValue, float minimum, float maximum, string description)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			ConfigEntry<float> obj = config.Bind<float>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(minimum, maximum), Array.Empty<object>()));
			HookChange<float>(obj);
			return obj;
		}

		private static void HookChange<T>(ConfigEntry<T> entry)
		{
			entry.SettingChanged += OnSettingChanged;
			UnsubscribeActions.Add(delegate
			{
				entry.SettingChanged -= OnSettingChanged;
			});
		}

		private static void UnhookChanges()
		{
			foreach (Action unsubscribeAction in UnsubscribeActions)
			{
				unsubscribeAction();
			}
			UnsubscribeActions.Clear();
		}

		private static void OnSettingChanged(object sender, EventArgs args)
		{
			BuildCameraConfig.Changed?.Invoke();
		}
	}
	internal static class Diagnostics
	{
		private static ManualLogSource _log;

		internal static bool IsInitialized => _log != null;

		internal static void Initialize(ManualLogSource log)
		{
			_log = log ?? throw new ArgumentNullException("log");
		}

		internal static void Verbose(string message)
		{
			if (BuildCameraConfig.VerboseLogging != null && BuildCameraConfig.VerboseLogging.Value)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}

		internal static void Debug(string message)
		{
			if (BuildCameraConfig.VerboseLogging != null && BuildCameraConfig.VerboseLogging.Value)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)message);
				}
			}
		}

		internal static void Info(string message)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogInfo((object)message);
			}
		}

		internal static void Warn(string message)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogWarning((object)message);
			}
		}

		internal static void Error(string message)
		{
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogError((object)message);
			}
		}

		internal static void Error(Exception exception, string context)
		{
			if (exception == null)
			{
				Error(context);
				return;
			}
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogError((object)(context + Environment.NewLine + exception));
			}
		}
	}
	[BepInPlugin("chazman.RunicBuildCamera", "Runic Build Camera", "1.0.8")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "chazman.RunicBuildCamera";

		public const string Name = "Runic Build Camera";

		public const string Version = "1.0.8";

		private Harmony _harmony;

		private bool _runtimeReady;

		private bool _runtimeFailureReported;

		private void Awake()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			Diagnostics.Initialize(((BaseUnityPlugin)this).Logger);
			BuildCameraConfig.Bind(((BaseUnityPlugin)this).Config);
			BuildCameraConfig.Changed += OnConfigurationChanged;
			try
			{
				if (CompatibilityGuard.TryFindHardConflict(out var reason))
				{
					Diagnostics.Warn(reason);
					Diagnostics.Info("Runic Build Camera v1.0.8 loaded with its camera disabled.");
					return;
				}
				if (!ValheimAdapter.Initialize(out var error))
				{
					Diagnostics.Error(error);
					Diagnostics.Info("Runic Build Camera v1.0.8 loaded with its camera disabled.");
					return;
				}
				_harmony = new Harmony("chazman.RunicBuildCamera");
				_harmony.PatchAll(typeof(Plugin).Assembly);
				BuildCameraRuntime.Initialize();
				_runtimeReady = true;
				Diagnostics.Info("Runic Build Camera v1.0.8 ready. Precision Build Tool remains an independent plugin and is not required.");
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, RunicText.Get("text_8ea2ea6caed3"));
				DisableRuntime();
			}
		}

		private void Update()
		{
			if (!_runtimeReady)
			{
				return;
			}
			try
			{
				BuildCameraRuntime.Tick();
			}
			catch (Exception exception)
			{
				BuildCameraRuntime.ForceStop();
				RemotePickupRuntime.Reset();
				DemisterRuntime.OnCameraExit();
				if (!_runtimeFailureReported)
				{
					_runtimeFailureReported = true;
					Diagnostics.Error(exception, RunicText.Get("text_2f17ce857460"));
				}
			}
		}

		private void OnApplicationFocus(bool focused)
		{
			if (_runtimeReady)
			{
				BuildCameraRuntime.OnApplicationFocus(focused);
				if (!focused)
				{
					RemotePickupRuntime.Reset();
					DemisterRuntime.OnCameraExit();
				}
			}
		}

		private void OnConfigurationChanged()
		{
			if (!_runtimeReady)
			{
				return;
			}
			try
			{
				BuildCameraRuntime.OnConfigurationChanged();
			}
			catch (Exception exception)
			{
				BuildCameraRuntime.ForceStop();
				Diagnostics.Error(exception, RunicText.Get("text_888408df9d8b"));
			}
		}

		private void OnDestroy()
		{
			BuildCameraConfig.Changed -= OnConfigurationChanged;
			DisableRuntime();
		}

		private void DisableRuntime()
		{
			_runtimeReady = false;
			TryCleanup(RemotePickupRuntime.Shutdown, RunicText.Get("text_d53b70e83194"));
			TryCleanup(DemisterRuntime.Shutdown, RunicText.Get("text_480ad81fe2bf"));
			TryCleanup(BuildCameraRuntime.Shutdown, RunicText.Get("text_e7e1616b5b33"));
			TryCleanup(ValheimAdapter.Shutdown, RunicText.Get("text_3dad311a77e5"));
			if (_harmony == null)
			{
				return;
			}
			try
			{
				_harmony.UnpatchSelf();
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, RunicText.Get("text_a907974ac55d"));
			}
			finally
			{
				_harmony = null;
			}
		}

		private static void TryCleanup(Action cleanup, string label)
		{
			try
			{
				cleanup();
			}
			catch (Exception exception)
			{
				Diagnostics.Error(exception, RunicText.Format("text_35b8f9a59ccc", label));
			}
		}
	}
}
namespace RunicBuildCamera.Integration
{
	internal static class BuildCameraRuntime
	{
		private static readonly BuildCameraSession Session = new BuildCameraSession();

		private static bool _initialized;

		private static bool _focused = true;

		internal static bool IsActive
		{
			get
			{
				if (_initialized)
				{
					return Session.IsActive;
				}
				return false;
			}
		}

		internal static void Initialize()
		{
			Session.End();
			_focused = Application.isFocused;
			_initialized = true;
		}

		internal static void Shutdown()
		{
			Stop();
			_initialized = false;
		}

		internal unsafe static void Tick()
		{
			//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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized)
			{
				return;
			}
			_focused = Application.isFocused;
			Player localPlayer = Player.m_localPlayer;
			KeyboardShortcut value = BuildCameraConfig.ToggleShortcut.Value;
			bool num = (int)((KeyboardShortcut)(ref value)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey);
			bool flag = (int)((KeyboardShortcut)(ref value)).MainKey != 0 && ((KeyboardShortcut)(ref value)).IsDown();
			if (num && !flag)
			{
				string text = RunicText.Get("text_868e36f8c510");
				KeyboardShortcut val = value;
				ActivationFeedback(localPlayer, text + ((object)(*(KeyboardShortcut*)(&val))/*cast due to .constrained prefix*/).ToString());
			}
			if (!ConfigEnabled() || !_focused)
			{
				if (flag)
				{
					ActivationFeedback(localPlayer, (!ConfigEnabled()) ? RunicText.Get("text_e1ab24965475") : RunicText.Get("text_4ea5638c368a"));
				}
				Stop();
			}
			else if (Session.IsActive && (!SessionBelongsToUsablePlayer(localPlayer) || (Object)(object)GameCamera.instance == (Object)null))
			{
				Stop();
			}
			else if (flag)
			{
				if (Session.IsActive)
				{
					Stop();
					ActivationFeedback(localPlayer, "Off.");
				}
				else if (ValheimAdapter.CanTakeInput(localPlayer))
				{
					TryStart(localPlayer);
				}
				else
				{
					ActivationFeedback(localPlayer, RunicText.Get("text_f46af67108b2"));
				}
			}
			else if (Session.IsActive)
			{
				if (!ValheimAdapter.IsBuildToolEquipped(localPlayer))
				{
					Stop();
					return;
				}
				Session.Reanchor(((Component)localPlayer).transform.position);
				Session.UpdateRange(Positive(BuildCameraConfig.CameraRange.Value, 1f));
			}
		}

		internal static void OnApplicationFocus(bool focused)
		{
			_focused = focused;
			if (!focused)
			{
				Stop();
			}
		}

		internal static void OnConfigurationChanged()
		{
			if (_initialized)
			{
				if (!ConfigEnabled())
				{
					Stop();
				}
				else if (Session.IsActive)
				{
					Session.UpdateRange(Positive(BuildCameraConfig.CameraRange.Value, 1f));
				}
			}
		}

		internal static void OnLocalPlayerAssigned(Player player)
		{
			if (Session.IsActive && ((Object)(object)player == (Object)null || !Session.BelongsTo(((Object)player).GetInstanceID())))
			{
				Stop();
			}
		}

		internal static bool ShouldFreezePlayer(Player player)
		{
			if (IsActive && (Object)(object)player != (Object)null && Session.BelongsTo(((Object)player).GetInstanceID()))
			{
				return ValheimAdapter.IsLocalPlayer(player);
			}
			return false;
		}

		internal static ValheimAdapter.RangeLease EnterRemoteActionRange(Player player)
		{
			if (!ShouldFreezePlayer(player))
			{
				return null;
			}
			float num = Positive(BuildCameraConfig.RemoteActionDistance.Value, 1f);
			return ValheimAdapter.EnterRemoteActionRange(player, num, num);
		}

		internal static bool IsWithinRemoteActionLimit(Player player, Vector3 target)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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)
			if (!ShouldFreezePlayer(player) || (Object)(object)((Character)player).m_eye == (Object)null || !IsFinite(target))
			{
				return false;
			}
			float num = Positive(BuildCameraConfig.RemoteActionDistance.Value, 1f);
			Vector3 value = target - ((Character)player).m_eye.position;
			if (IsFinite(value))
			{
				return (double)((Vector3)(ref value)).sqrMagnitude <= (double)num * (double)num;
			}
			return false;
		}

		internal static bool TryUpdateCamera(GameCamera camera, float deltaTime)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (!IsActive || (Object)(object)camera == (Object)null)
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!SessionBelongsToUsablePlayer(localPlayer))
			{
				Stop();
				return false;
			}
			Session.Reanchor(((Component)localPlayer).transform.position);
			if (_focused && ValheimAdapter.CanTakeInput(localPlayer) && !Console.IsVisible() && ((Object)(object)Hud.instance == (Object)null || !Hud.IsPieceSelectionVisible()))
			{
				Session.SetPose(StepCamera(Session, deltaTime));
			}
			((Component)camera).transform.SetPositionAndRotation(Session.Position, Session.Rotation);
			return true;
		}

		internal static bool TryGetActiveContext(out Player player, out Vector3 cameraPosition, out Quaternion cameraRotation)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			player = Player.m_localPlayer;
			if (!IsActive || !SessionBelongsToUsablePlayer(player))
			{
				cameraPosition = Vector3.zero;
				cameraRotation = Quaternion.identity;
				return false;
			}
			cameraPosition = Session.Position;
			cameraRotation = Session.Rotation;
			return true;
		}

		internal static void ForceStop()
		{
			Stop();
		}

		internal static bool ExitInputRequested()
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (CameraExitInput.HotbarRequested((Func<string, bool>)ZInput.GetButtonDown))
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			bool shortControllerRelease = !Hud.InRadial() && ZInput.GetButtonUp("JoyHide") && ZInput.GetButtonLastPressedTimer("JoyHide") < 0.33f;
			return CameraExitInput.HideRequested(ZInput.GetButtonDown("Hide"), (int)ZInput.InputLayout != 0 && ZInput.IsGamepadActive(), shortControllerRelease, ((Character)localPlayer).InPlaceMode(), ZInput.GetButton("JoyAltKeys"));
		}

		private static void TryStart(Player player)
		{
			//IL_005f: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			if (!ValheimAdapter.IsBuildToolEquipped(player))
			{
				ActivationFeedback(player, RunicText.Get("text_aad211031f8b"));
				return;
			}
			GameCamera instance = GameCamera.instance;
			if ((Object)(object)instance == (Object)null)
			{
				ActivationFeedback(player, RunicText.Get("text_2e15e203e13d"));
				return;
			}
			float range = Positive(BuildCameraConfig.CameraRange.Value, 1f);
			Session.Begin(((Object)player).GetInstanceID(), ((Component)player).transform.position, ((Component)instance).transform.position, ((Component)instance).transform.rotation, range);
			ActivationFeedback(player, RunicText.Get("text_3691699c3e79"));
		}

		private static void ActivationFeedback(Player player, string message)
		{
			Diagnostics.Info("Build camera: " + message);
			if ((Object)(object)player != (Object)null)
			{
				((Character)player).Message((MessageType)1, RunicText.Get("text_5a7920bb73fd") + message, 0, (Sprite)null, false);
			}
		}

		private static CameraPose StepCamera(BuildCameraSession session, float deltaTime)
		{
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			float right = ButtonAxis("Right", "Left") + ZInput.GetJoyLeftStickX();
			float forward = ButtonAxis("Forward", "Backward") - ZInput.GetJoyLeftStickY();
			float up = ((ZInput.GetButton("Jump") || ZInput.GetButton("JoyJump")) ? 1f : 0f) - ((ZInput.GetButton("Crouch") || ZInput.GetButton("JoyCrouch")) ? 1f : 0f);
			float num = Input.GetAxis("Mouse X") * PlayerController.m_mouseSens;
			float num2 = Input.GetAxis("Mouse Y") * PlayerController.m_mouseSens;
			float num3 = ZInput.GetJoyRightStickX() * 110f * deltaTime;
			float num4 = ZInput.GetJoyRightStickY() * 110f * deltaTime;
			if (BuildCameraConfig.InvertMouseHorizontal.Value)
			{
				num = 0f - num;
			}
			if (PlayerController.m_invertMouse)
			{
				num2 = 0f - num2;
			}
			if (BuildCameraConfig.InvertMouseVertical.Value)
			{
				num2 = 0f - num2;
			}
			if (BuildCameraConfig.InvertControllerHorizontal.Value)
			{
				num3 = 0f - num3;
			}
			if (BuildCameraConfig.InvertControllerVertical.Value)
			{
				num4 = 0f - num4;
			}
			CameraMotionInput input = new CameraMotionInput(right, up, forward, num + num3, 0f - num2 + num4, ZInput.GetButton("Run") || ZInput.GetButton("JoyRun"));
			CameraPose result = CameraMotion.Step(session.Position, session.Yaw, session.Pitch, session.Anchor, session.Range, in input, deltaTime, Positive(BuildCameraConfig.MoveSpeed.Value, 0f), Positive(BuildCameraConfig.FastMoveMultiplier.Value, 1f), BuildCameraConfig.WorldRelativeMovement.Value);
			float num5 = default(float);
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(result.Position, ref num5) && result.Position.y < num5)
			{
				Vector3 position = result.Position;
				position.y = num5;
				position = CameraMotion.ClampToAnchor(position, session.Anchor, session.Range);
				result = new CameraPose(position, result.Yaw, result.Pitch);
			}
			return result;
		}

		private static float ButtonAxis(string positive, string negative)
		{
			return (ZInput.GetButton(positive) ? 1f : 0f) - (ZInput.GetButton(negative) ? 1f : 0f);
		}

		private static bool SessionBelongsToUsablePlayer(Player player)
		{
			if ((Object)(object)player != (Object)null && Session.BelongsTo(((Object)player).GetInstanceID()))
			{
				return ValheimAdapter.IsUsableLocalPlayer(player);
			}
			return false;
		}

		private static bool ConfigEnabled()
		{
			if (BuildCameraConfig.Enabled != null && BuildCameraConfig.ToggleShortcut != null && BuildCameraConfig.CameraRange != null && BuildCameraConfig.MoveSpeed != null && BuildCameraConfig.FastMoveMultiplier != null && BuildCameraConfig.WorldRelativeMovement != null && BuildCameraConfig.RemoteActionDistance != null && BuildCameraConfig.InvertMouseHorizontal != null && BuildCameraConfig.InvertMouseVertical != null && BuildCameraConfig.InvertControllerHorizontal != null && BuildCameraConfig.InvertControllerVertical != null)
			{
				return BuildCameraConfig.Enabled.Value;
			}
			return false;
		}

		private static float Positive(float value, float fallback)
		{
			if (float.IsNaN(value) || float.IsInfinity(value) || !(value > 0f))
			{
				return fallback;
			}
			return value;
		}

		private static bool IsFinite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z))
			{
				return !float.IsInfinity(value.z);
			}
			return false;
		}

		private static void Stop()
		{
			if (Session.IsActive)
			{
				Session.End();
			}
			ValheimAdapter.ForceRestoreRanges();
		}
	}
	internal static class PlayerUpdateInputIsolation
	{
		[ThreadStatic]
		private static int _depth;

		[ThreadStatic]
		private static Player _player;

		[ThreadStatic]
		private static bool _captured;

		[ThreadStatic]
		private static bool _originalTakeInput;

		internal static bool Enter(Player player)
		{
			if (!BuildCameraRuntime.ShouldFreezePlayer(player))
			{
				return false;
			}
			if (_depth == 0)
			{
				_player = player;
				_captured = false;
				_originalTakeInput = false;
			}
			_depth++;
			return true;
		}

		internal static void CaptureTakeInput(Player player, bool original)
		{
			if (_depth > 0 && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)_player))
			{
				_captured = true;
				_originalTakeInput = original;
			}
		}

		internal static bool ShouldSuppress(Player player)
		{
			if (_depth > 0 && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)_player)
			{
				return BuildCameraRuntime.ShouldFreezePlayer(player);
			}
			return false;
		}

		internal static bool PlacementInput(Player player, bool current)
		{
			if (!ShouldSuppress(player) || !_captured)
			{
				return current;
			}
			return _originalTakeInput;
		}

		internal static void Exit()
		{
			if (_depth > 0)
			{
				_depth--;
				if (_depth == 0)
				{
					_player = null;
					_captured = false;
					_originalTakeInput = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class PlayerUpdateInputScopePatch
	{
		private static void Prefix(Player __instance, ref bool __state)
		{
			try
			{
				__state = PlayerUpdateInputIsolation.Enter(__instance);
			}
			catch
			{
				__state = false;
				BuildCameraRuntime.ForceStop();
			}
		}

		private static Exception Finalizer(Exception __exception, bool __state)
		{
			if (__state)
			{
				PlayerUpdateInputIsolation.Exit();
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Player), "TakeInput")]
	internal static class PlayerTakeInputIsolationPatch
	{
		private static void Postfix(Player __instance, ref bool __result)
		{
			if (PlayerUpdateInputIsolation.ShouldSuppress(__instance))
			{
				PlayerUpdateInputIsolation.CaptureTakeInput(__instance, __result);
				if (!CameraExitInput.ReleaseIfRequested(__result, BuildCameraRuntime.ExitInputRequested, BuildCameraRuntime.ForceStop))
				{
					__result = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UseHotbarItem", new Type[] { typeof(int) })]
	internal static class PlayerHotbarCameraExitPatch
	{
		private static void Prefix(Player __instance)
		{
			if (BuildCameraRuntime.ShouldFreezePlayer(__instance))
			{
				BuildCameraRuntime.ForceStop();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetMouseLook", new Type[] { typeof(Vector2) })]
	internal static class PlayerSetMouseLookIsolationPatch
	{
		private static void Prefix(Player __instance, ref Vector2 mouseLook)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (BuildCameraRuntime.ShouldFreezePlayer(__instance))
			{
				mouseLook = Vector2.zero;
			}
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "GetStationBuildRange")]
	internal static class CraftingStationScopedBuildRangePatch
	{
		private static void Postfix(ref float __result)
		{
			if (ValheimAdapter.TryGetScopedStationRange(out var range) && __result < range)
			{
				__result = range;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "PieceRayTest")]
	internal static class PlayerPieceRayRemoteLimitPatch
	{
		private static void Postfix(Player __instance, [HarmonyArgument("point")] ref Vector3 point, ref bool __result)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (__result && BuildCameraRuntime.ShouldFreezePlayer(__instance) && !BuildCameraRuntime.IsWithinRemoteActionLimit(__instance, point))
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch]
	internal static class GameCameraUpdateCameraPatch
	{
		private static MethodBase TargetMethod()
		{
			return ValheimAdapter.UpdateCameraMethod;
		}

		private static bool Prefix(GameCamera __instance, float dt)
		{
			try
			{
				return !BuildCameraRuntime.TryUpdateCamera(__instance, dt);
			}
			catch (Exception exception)
			{
				BuildCameraRuntime.ForceStop();
				Diagnostics.Error(exception, RunicText.Get("text_1d7135258182"));
				return true;
			}
		}
	}
	[HarmonyPatch]
	internal static class PlayerSetControlsPatch
	{
		private static MethodBase TargetMethod()
		{
			return ValheimAdapter.SetControlsMethod;
		}

		private static void Prefix(Player __instance, ref Vector3 movedir, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch, ref bool run, ref bool autoRun, ref bool dodge)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (BuildCameraRuntime.ShouldFreezePlayer(__instance))
			{
				movedir = Vector3.zero;
				attack = false;
				attackHold = false;
				secondaryAttack = false;
				secondaryAttackHold = false;
				block = false;
				blockHold = false;
				jump = false;
				crouch = false;
				run = false;
				autoRun = false;
				dodge = false;
			}
		}
	}
	[HarmonyPatch]
	internal static class PlayerUpdatePlacementRangePatch
	{
		private static MethodBase TargetMethod()
		{
			return ValheimAdapter.UpdatePlacementMethod;
		}

		[HarmonyPriority(800)]
		[HarmonyBefore(new string[] { "chazman.RunicPrecisionBuildTool" })]
		private static void Prefix(Player __instance, ref bool takeInput, ref ValheimAdapter.RangeLease __state)
		{
			try
			{
				takeInput = PlayerUpdateInputIsolation.PlacementInput(__instance, takeInput);
				__state = BuildCameraRuntime.EnterRemoteActionRange(__instance);
			}
			catch
			{
				__state = null;
				BuildCameraRuntime.ForceStop();
			}
		}

		private static Exception Finalizer(Exception __exception, ValheimAdapter.RangeLease __state)
		{
			try
			{
				__state?.Dispose();
			}
			catch
			{
				BuildCameraRuntime.ForceStop();
			}
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class PlayerUpdatePlacementGhostRangePatch
	{
		private static MethodBase TargetMethod()
		{
			return ValheimAdapter.UpdatePlacementGhostMethod;
		}

		private static void Prefix(Player __instance, ref ValheimAdapter.RangeLease __state)
		{
			try
			{
				__state = BuildCameraRuntime.EnterRemoteActionRange(__instance);
			}
			catch
			{
				__state = null;
				BuildCameraRuntime.ForceStop();
			}
		}

		private static Exception Finalizer(Exception __exception, ValheimAdapter.RangeLease __state)
		{
			try
			{
				__state?.Dispose();
			}
			catch
			{
				BuildCameraRuntime.ForceStop();
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Player), "SetLocalPlayer")]
	internal static class PlayerSetLocalPlayerCameraCleanupPatch
	{
		private static void Postfix(Player __instance)
		{
			try
			{
				BuildCameraRuntime.OnLocalPlayerAssigned(__instance);
			}
			catch
			{
				BuildCameraRuntime.ForceStop();
			}
		}
	}
	internal static class DemisterRuntime
	{
		private readonly struct ForceFieldBaseline
		{
			internal ParticleSystemForceField Field { get; }

			internal float EndRange { get; }

			internal ForceFieldBaseline(ParticleSystemForceField field, float endRange)
			{
				Field = field;
				EndRange = endRange;
			}
		}

		private sealed class BallState
		{
			internal GameObject Ball { get; }

			internal ForceFieldBaseline[] ForceFields { get; }

			internal BallState(GameObject ball, ForceFieldBaseline[] forceFields)
			{
				Ball = ball;
				ForceFields = forceFields ?? Array.Empty<ForceFieldBaseline>();
			}
		}

		private static readonly FieldInfo BallInstanceField = AccessTools.Field(typeof(SE_Demister), "m_ballInstance");

		private static readonly Dictionary<int, BallState> BallStates = new Dictionary<int, BallState>();

		private static readonly Dictionary<int, int> EffectBallIds = new Dictionary<int, int>();

		private static bool _subscribed;

		internal static void AfterStatusEffectUpdate(SE_Demister effect)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			EnsureSubscribed();
			if ((Object)(object)effect == (Object)null)
			{
				RefreshActiveState();
				return;
			}
			GameObject ball = GetBall(effect);
			if (!Object.op_Implicit((Object)(object)ball))
			{
				RestoreEffect(effect);
				PruneDestroyedBalls();
				return;
			}
			int instanceID = ((Object)effect).GetInstanceID();
			int instanceID2 = ((Object)ball).GetInstanceID();
			if (EffectBallIds.TryGetValue(instanceID, out var value) && value != instanceID2)
			{
				RestoreBall(value);
			}
			EffectBallIds[instanceID] = instanceID2;
			bool num = BuildCameraConfig.DemisterFollowCamera != null && BuildCameraConfig.DemisterFollowCamera.Value;
			Player player;
			Vector3 cameraPosition;
			Quaternion cameraRotation;
			bool flag = BuildCameraRuntime.TryGetActiveContext(out player, out cameraPosition, out cameraRotation);
			if (!num || !flag || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)((StatusEffect)effect).m_character != (Object)(object)player || !IsFinite(cameraPosition))
			{
				RestoreEffect(effect);
				return;
			}
			BallState orCaptureBall = GetOrCaptureBall(ball);
			if (orCaptureBall != null)
			{
				float multiplier = ((BuildCameraConfig.DemisterRangeMultiplier != null) ? BuildCameraConfig.DemisterRangeMultiplier.Value : 1f);
				ApplyRange(orCaptureBall, multiplier);
				ball.transform.position = cameraPosition;
				PruneDestroyedBalls();
			}
		}

		internal static void BeforeRemoveEffects(SE_Demister effect)
		{
			EnsureSubscribed();
			RestoreEffect(effect);
			PruneDestroyedBalls();
		}

		internal static void RefreshActiveState()
		{
			EnsureSubscribed();
			if (BuildCameraConfig.DemisterFollowCamera == null || !BuildCameraConfig.DemisterFollowCamera.Value || !BuildCameraRuntime.TryGetActiveContext(out var _, out var _, out var _))
			{
				RestoreAll();
			}
			else
			{
				PruneDestroyedBalls();
			}
		}

		internal static void OnCameraExit()
		{
			RestoreAll();
		}

		internal static void Shutdown()
		{
			if (_subscribed)
			{
				BuildCameraConfig.Changed -= OnConfigurationChanged;
				_subscribed = false;
			}
			RestoreAll();
		}

		private static BallState GetOrCaptureBall(GameObject ball)
		{
			int instanceID = ((Object)ball).GetInstanceID();
			if (BallStates.TryGetValue(instanceID, out var value) && value.Ball == ball)
			{
				return value;
			}
			if (value != null)
			{
				RestoreState(value);
			}
			ParticleSystemForceField[] array;
			try
			{
				array = ball.GetComponentsInChildren<ParticleSystemForceField>(true);
			}
			catch
			{
				array = Array.Empty<ParticleSystemForceField>();
			}
			List<ForceFieldBaseline> list = new List<ForceFieldBaseline>(array.Length);
			ParticleSystemForceField[] array2 = array;
			foreach (ParticleSystemForceField val in array2)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					list.Add(new ForceFieldBaseline(val, val.endRange));
				}
			}
			BallState ballState = new BallState(ball, list.ToArray());
			BallStates[instanceID] = ballState;
			return ballState;
		}

		private static void ApplyRange(BallState state, float multiplier)
		{
			float num = ((IsFinite(multiplier) && multiplier > 0f) ? multiplier : 1f);
			ForceFieldBaseline[] forceFields = state.ForceFields;
			for (int i = 0; i < forceFields.Length; i++)
			{
				ForceFieldBaseline forceFieldBaseline = forceFields[i];
				ParticleSystemForceField field = forceFieldBaseline.Field;
				if (Object.op_Implicit((Object)(object)field))
				{
					double num2 = (double)forceFieldBaseline.EndRange * (double)num;
					field.endRange = ((double.IsNaN(num2) || double.IsInfinity(num2) || num2 > 3.4028234663852886E+38 || num2 < -3.4028234663852886E+38) ? forceFieldBaseline.EndRange : ((float)num2));
				}
			}
		}

		private static void RestoreEffect(SE_Demister effect)
		{
			if ((Object)(object)effect == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)effect).GetInstanceID();
			if (EffectBallIds.TryGetValue(instanceID, out var value))
			{
				RestoreBall(value);
				EffectBallIds.Remove(instanceID);
				return;
			}
			GameObject ball = GetBall(effect);
			if (Object.op_Implicit((Object)(object)ball))
			{
				RestoreBall(((Object)ball).GetInstanceID());
			}
		}

		private static void RestoreBall(int ballId)
		{
			if (!BallStates.TryGetValue(ballId, out var value))
			{
				return;
			}
			RestoreState(value);
			BallStates.Remove(ballId);
			List<int> list = null;
			foreach (KeyValuePair<int, int> effectBallId in EffectBallIds)
			{
				if (effectBallId.Value == ballId)
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(effectBallId.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (int item in list)
			{
				EffectBallIds.Remove(item);
			}
		}

		private static void RestoreState(BallState state)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			ForceFieldBaseline[] forceFields = state.ForceFields;
			for (int i = 0; i < forceFields.Length; i++)
			{
				ForceFieldBaseline forceFieldBaseline = forceFields[i];
				ParticleSystemForceField field = forceFieldBaseline.Field;
				if (Object.op_Implicit((Object)(object)field))
				{
					field.endRange = forceFieldBaseline.EndRange;
				}
			}
			Player localPlayer = Player.m_localPlayer;
			if (Object.op_Implicit((Object)(object)state.Ball) && Object.op_Implicit((Object)(object)localPlayer))
			{
				state.Ball.transform.position = ((Character)localPlayer).GetCenterPoint();
			}
		}

		private static void RestoreAll()
		{
			foreach (BallState value in BallStates.Values)
			{
				RestoreState(value);
			}
			BallStates.Clear();
			EffectBallIds.Clear();
		}

		private static void PruneDestroyedBalls()
		{
			List<int> list = null;
			foreach (KeyValuePair<int, BallState> ballState in BallStates)
			{
				if (!Object.op_Implicit((Object)(object)ballState.Value.Ball))
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(ballState.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (int item in list)
			{
				RestoreBall(item);
			}
		}

		private static GameObject GetBall(SE_Demister effect)
		{
			if ((Object)(object)effect == (Object)null || BallInstanceField == null || BallInstanceField.FieldType != typeof(GameObject) || BallInstanceField.IsStatic)
			{
				return null;
			}
			try
			{
				object? value = BallInstanceField.GetValue(effect);
				return (GameObject)((value is GameObject) ? value : null);
			}
			catch
			{
				return null;
			}
		}

		private static void EnsureSubscribed()
		{
			if (!_subscribed)
			{
				BuildCameraConfig.Changed += OnConfigurationChanged;
				_subscribed = true;
			}
		}

		private static void OnConfigurationChanged()
		{
			RestoreAll();
		}

		private static bool IsFinite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (IsFinite(value.x) && IsFinite(value.y))
			{
				return IsFinite(value.z);
			}
			return false;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	[HarmonyPatch]
	internal static class GameCameraRemoteEffectsPatch
	{
		private static MethodBase TargetMethod()
		{
			return ValheimAdapter.UpdateCameraMethod;
		}

		private static void Postfix()
		{
			try
			{
				RemotePickupRuntime.Tick();
				DemisterRuntime.RefreshActiveState();
			}
			catch (Exception exception)
			{
				RemotePickupRuntime.ReportFailure(exception);
				RemotePickupRuntime.Reset();
				DemisterRuntime.OnCameraExit();
			}
		}
	}
	[HarmonyPatch(typeof(SE_Demister), "UpdateStatusEffect")]
	internal static class DemisterUpdateStatusEffectPatch
	{
		private static void Postfix(SE_Demister __instance)
		{
			try
			{
				DemisterRuntime.AfterStatusEffectUpdate(__instance);
			}
			catch
			{
				DemisterRuntime.OnCameraExit();
			}
		}
	}
	[HarmonyPatch(typeof(SE_Demister), "RemoveEffects")]
	internal static class DemisterRemoveEffectsPatch
	{
		private static void Prefix(SE_Demister __instance)
		{
			try
			{
				DemisterRuntime.BeforeRemoveEffects(__instance);
			}
			catch
			{
				DemisterRuntime.OnCameraExit();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "SetLocalPlayer")]
	internal static class RemoteEffectsLocalPlayerCleanupPatch
	{
		private static void Prefix()
		{
			RemotePickupRuntime.Reset();
			DemisterRuntime.OnCameraExit();
		}
	}
	internal static class PlayerWardPickupAccess
	{
		private static readonly FieldInfo AreasField = AccessTools.Field(typeof(PrivateArea), "m_allAreas");

		private static readonly Func<PrivateArea, bool> Enabled = AccessTools.MethodDelegate<Func<PrivateArea, bool>>(AccessTools.Method(typeof(PrivateArea), "IsEnabled", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<PrivateArea, Vector3, float, bool> Inside = AccessTools.MethodDelegate<Func<PrivateArea, Vector3, float, bool>>(AccessTools.Method(typeof(PrivateArea), "IsInside", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<PrivateArea, bool> LocalAccess = AccessTools.MethodDelegate<Func<PrivateArea, bool>>(AccessTools.Method(typeof(PrivateArea), "HaveLocalAccess", (Type[])null, (Type[])null), (object)null, true);

		internal static bool Allows(Vector3 target)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			foreach (PrivateArea item in (AreasField?.GetValue(null) as List<PrivateArea>) ?? throw new InvalidOperationException("Cannot read the player ward registry."))
			{
				if (Object.op_Implicit((Object)(object)item))
				{
					Piece component = ((Component)item).GetComponent<Piece>();
					if (Object.op_Implicit((Object)(object)component) && component.IsPlacedByPlayer() && Enabled(item) && Inside(item, target, 0f) && !LocalAccess(item))
					{
						return false;
					}
				}
			}
			return true;
		}
	}
	internal static class RemotePickupRuntime
	{
		private readonly struct CooldownEntry
		{
			internal float RetryAt { get; }

			internal long Ordinal { get; }

			internal CooldownEntry(float retryAt, long ordinal)
			{
				RetryAt = retryAt;
				Ordinal = ordinal;
			}
		}

		private static readonly ItemDrop[] ItemBuffer = (ItemDrop[])(object)new ItemDrop[128];

		private static readonly HashSet<ZDOID> SeenThisScan = new HashSet<ZDOID>();

		private static readonly Dictionary<ZDOID, CooldownEntry> Cooldowns = new Dictionary<ZDOID, CooldownEntry>();

		private static readonly FieldInfo EnableAutoPickupField = AccessTools.Field(typeof(Player), "m_enableAutoPickup");

		private static readonly FieldInfo ItemInstancesField = AccessTools.Field(typeof(ItemDrop), "s_instances");

		private static bool _subscribed;

		private static bool _wasActive;

		private static Player _lastPlayer;

		private static float _nextScanAt;

		private static long _cooldownOrdinal;

		private static int _scanCursor;

		private static int _pendingCount;

		private static int _pendingIndex;

		private static float _nextDiagnosticAt;

		private static float _nextWarningAt;

		private static bool _autoPickupOffShown;

		internal static void ReportFailure(Exception exception)
		{
			Report("Camera pickup update failed: " + exception);
		}

		internal static void Tick()
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			EnsureSubscribed();
			Player player;
			Vector3 cameraPosition;
			Quaternion cameraRotation;
			bool flag = BuildCameraRuntime.TryGetActiveContext(out player, out cameraPosition, out cameraRotation);
			bool num = BuildCameraRuntime.IsActive && BuildCameraConfig.PickupEnabled != null && BuildCameraConfig.PickupEnabled.Value && flag;
			if (!num)
			{
				_autoPickupOffShown = false;
			}
			if (!num || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsTeleporting() || !TryReadAutoPickupState(player, out var enabled) || !enabled)
			{
				if (_wasActive || (Object)(object)_lastPlayer != (Object)null || Cooldowns.Count != 0)
				{
					ResetTransientState();
				}
				return;
			}
			if ((Object)(object)player != (Object)(object)_lastPlayer)
			{
				ResetTransientState();
				_lastPlayer = player;
			}
			_wasActive = true;
			float time = Time.time;
			if (!PickupPolicy.IsScanDue(time, _nextScanAt))
			{
				return;
			}
			float num2 = ((BuildCameraConfig.PickupIntervalSeconds != null) ? BuildCameraConfig.PickupIntervalSeconds.Value : 0.2f);
			_nextScanAt = PickupPolicy.NextScanAt(time, num2);
			float num3 = ((BuildCameraConfig.PickupRange != null) ? BuildCameraConfig.PickupRange.Value : 0f);
			if (!float.IsNaN(num3) && !float.IsInfinity(num3) && !(num3 <= 0f) && IsFinite(cameraPosition))
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory != null)
				{
					Scan(player, inventory, cameraPosition, num3, num2, time);
				}
			}
		}

		internal static void Reset()
		{
			ResetTransientState();
		}

		internal static void Shutdown()
		{
			if (_subscribed)
			{
				BuildCameraConfig.Changed -= OnConfigurationChanged;
				_subscribed = false;
			}
			ResetTransientState();
		}

		private static void Scan(Player player, Inventory inventory, Vector3 cameraPosition, float range, float interval, float now)
		{
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			int pendingCount;
			try
			{
				if (!(ItemInstancesField?.GetValue(null) is List<ItemDrop> items))
				{
					Report("Camera pickup could not read Valheim's loaded item registry.");
					return;
				}
				if (_pendingIndex >= _pendingCount)
				{
					_pendingCount = PickupScanWindow.CopyNext(items, ItemBuffer, ref _scanCursor);
					_pendingIndex = 0;
				}
				pendingCount = _pendingCount;
			}
			catch (Exception ex)
			{
				Report("Camera pickup discovery failed: " + ex);
				return;
			}
			SeenThisScan.Clear();
			PruneExpiredCooldowns(now);
			int num = 0;
			try
			{
				int num2 = Math.Min(pendingCount, ItemBuffer.Length);
				while (_pendingIndex < num2 && num < 16)
				{
					ItemDrop val = ItemBuffer[_pendingIndex];
					ItemBuffer[_pendingIndex++] = null;
					if (!Object.op_Implicit((Object)(object)val) || !((Behaviour)val).isActiveAndEnabled || !((Component)val).gameObject.activeInHierarchy)
					{
						continue;
					}
					ZNetView component = ((Component)val).GetComponent<ZNetView>();
					ZDO val2 = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
					if (val2 == null || ((ZDOID)(ref val2.m_uid)).IsNone() || !SeenThisScan.Add(val2.m_uid))
					{
						continue;
					}
					try
					{
						val.Load();
						if (!TryEvaluate(val, val2.m_uid, player, inventory, cameraPosition, range, now))
						{
							continue;
						}
						val.RequestOwn();
						num++;
						if (!val.CanPickup(true))
						{
							SetCooldown(val2.m_uid, PickupPolicy.OwnershipRetryAt(now, interval));
							continue;
						}
						val.Load();
						if (!TryEvaluate(val, val2.m_uid, player, inventory, cameraPosition, range, now, ignoreCooldown: true))
						{
							SetCooldown(val2.m_uid, PickupPolicy.FailedPickupRetryAt(now));
							continue;
						}
						bool flag = ((Humanoid)player).Pickup(((Component)val).gameObject, true, true);
						if (!flag)
						{
							Report("Camera pickup declined by the normal pickup path or another mod.", warning: false);
						}
						SetCooldown(val2.m_uid, flag ? PickupPolicy.CompletedPickupRetryAt(now) : PickupPolicy.FailedPickupRetryAt(now));
					}
					catch (Exception ex2)
					{
						Report("Camera pickup failed: " + ex2);
						SetCooldown(val2.m_uid, PickupPolicy.FailedPickupRetryAt(now));
					}
				}
			}
			finally
			{
				SeenThisScan.Clear();
			}
		}

		private static bool TryEvaluate(ItemDrop itemDrop, ZDOID id, Player player, Inventory inventory, Vector3 cameraPosition, float range, float now, bool ignoreCooldown = false)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			ItemData itemData = itemDrop.m_itemData;
			SharedData val = itemData?.m_shared;
			if (itemData == null || val == null)
			{
				return false;
			}
			CooldownEntry value;
			bool isCoolingDown = !ignoreCooldown && Cooldowns.TryGetValue(id, out value) && now < value.RetryAt;
			Vector3 position = ((Component)itemDrop).transform.position;
			int num;
			if (IsFinite(position))
			{
				Vector3 val2 = position - cameraPosition;
				num = (PickupPolicy.IsWithinRange(((Vector3)(ref val2)).sqrMagnitude, range) ? 1 : 0);
			}
			else
			{
				num = 0;
			}
			bool flag = (byte)num != 0;
			bool flag2 = (Object)(object)((Component)itemDrop).GetComponent<Fish>() != (Object)null;
			if (flag && !flag2 && !PlayerWardPickupAccess.Allows(position))
			{
				Report("Camera pickup rejected " + val.m_name + ": PlayerWardDenied", warning: false);
				return false;
			}
			bool isUniqueOrQuestItem = val.m_questItem || ((Humanoid)player).HaveUniqueKey(val.m_name);
			bool inventoryCanAdd = inventory.CanAddItem(itemData, -1);
			float weight = itemData.GetWeight(-1);
			float num2 = inventory.GetTotalWeight() + weight;
			float maxCarryWeight = player.GetMaxCarryWeight();
			bool wouldExceedCarryWeight = float.IsNaN(weight) || float.IsInfinity(weight) || weight < 0f || float.IsNaN(num2) || float.IsInfinity(num2) || float.IsNaN(maxCarryWeight) || float.IsInfinity(maxCarryWeight) || num2 > maxCarryWeight;
			PickupRejectionReason pickupRejectionReason = PickupPolicy.Evaluate(new PickupCandidateFacts(!((ZDOID)(ref id)).IsNone(), flag, flag2, itemDrop.m_autoPickup, itemDrop.IsPiece(), itemDrop.InTar(), isUniqueOrQuestItem, inventoryCanAdd, wouldExceedCarryWeight, isCoolingDown));
			if (flag && pickupRejectionReason != PickupRejectionReason.None && pickupRejectionReason != PickupRejectionReason.CoolingDown)
			{
				Report("Camera pickup rejected " + val.m_name + ": " + pickupRejectionReason, warning: false);
			}
			return pickupRejectionReason == PickupRejectionReason.None;
		}

		private static void SetCooldown(ZDOID id, float retryAt)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (!((ZDOID)(ref id)).IsNone())
			{
				if (!Cooldowns.ContainsKey(id) && Cooldowns.Count >= 256)
				{
					RemoveOldestCooldown();
				}
				if (_cooldownOrdinal == long.MaxValue)
				{
					Cooldowns.Clear();
					_cooldownOrdinal = 0L;
				}
				long ordinal = ++_cooldownOrdinal;
				Cooldowns[id] = new CooldownEntry(retryAt, ordinal);
			}
		}

		private static void PruneExpiredCooldowns(float now)
		{
			//IL_0042: 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)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			if (Cooldowns.Count == 0)
			{
				return;
			}
			List<ZDOID> list = null;
			foreach (KeyValuePair<ZDOID, CooldownEntry> cooldown in Cooldowns)
			{
				if (!(now < cooldown.Value.RetryAt))
				{
					if (list == null)
					{
						list = new List<ZDOID>();
					}
					list.Add(cooldown.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (ZDOID item in list)
			{
				Cooldowns.Remove(item);
			}
		}

		private static void RemoveOldestCooldown()
		{
			//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_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_007a: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			ZDOID key = ZDOID.None;
			long num = long.MaxValue;
			foreach (KeyValuePair<ZDOID, CooldownEntry> cooldown in Cooldowns)
			{
				if (!flag || cooldown.Value.Ordinal < num)
				{
					flag = true;
					key = cooldown.Key;
					num = cooldown.Value.Ordinal;
				}
			}
			if (flag)
			{
				Cooldowns.Remove(key);
			}
		}

		private static void EnsureSubscribed()
		{
			if (!_subscribed)
			{
				BuildCameraConfig.Changed += OnConfigurationChanged;
				_subscribed = true;
			}
		}

		private static void OnConfigurationChanged()
		{
			ResetTransientState();
		}

		private static void ResetTransientState()
		{
			_wasActive = false;
			_lastPlayer = null;
			_nextScanAt = 0f;
			_cooldownOrdinal = 0L;
			_scanCursor = 0;
			_pendingCount = 0;
			_pendingIndex = 0;
			Cooldowns.Clear();
			SeenThisScan.Clear();
			Array.Clear(ItemBuffer, 0, ItemBuffer.Length);
		}

		private static bool TryReadAutoPickupState(Player player, out bool enabled)
		{
			enabled = false;
			if ((Object)(object)player == (Object)null || EnableAutoPickupField == null || EnableAutoPickupField.FieldType != typeof(bool) || !EnableAutoPickupField.IsStatic)
			{
				Report("Camera pickup could not read Valheim's auto-pickup setting.");
				return false;
			}
			try
			{
				enabled = (bool)EnableAutoPickupField.GetValue(null);
				if (!enabled && !_autoPickupOffShown)
				{
					((Character)player).Message((MessageType)1, "$hud_autopickup: $hud_off", 0, (Sprite)null, false);
				}
				_autoPickupOffShown = !enabled;
				if (!enabled)
				{
					Report("Camera pickup paused: player auto-pickup is off.", warning: false);
				}
				return true;
			}
			catch (Exception ex)
			{
				Report("Camera pickup state read failed: " + ex);
				return false;
			}
		}

		private static void Report(string message, bool warning = true)
		{
			if (warning)
			{
				if (!(Time.time < _nextWarningAt))
				{
					_nextWarningAt = Time.time + 5f;
					Diagnostics.Warn(message);
				}
			}
			else if (!(Time.time < _nextDiagnosticAt))
			{
				_nextDiagnosticAt = Time.time + 5f;
				Diagnostics.Verbose(message);
			}
		}

		private static bool IsFinite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z))
			{
				return !float.IsInfinity(value.z);
			}
			return false;
		}
	}
	internal static class ValheimAdapter
	{
		private delegate bool TakeInputDelegate(Player player);

		internal sealed class RangeState
		{
			internal Player Player;

			internal float Original;

			internal float Requested;

			internal int Depth;
		}

		internal sealed class RangeLease : IDisposable
		{
			private readonly int _playerId;

			private readonly RangeState _state;

			private bool _disposed;

			internal RangeLease(int playerId, RangeState state)
			{
				_playerId = playerId;
				_state = state;
			}

			public void Dispose()
			{
				if (!_disposed)
				{
					_disposed = true;
					ExitRange(_playerId, _state);
				}
			}
		}

		private static readonly Dictionary<int, RangeState> RangeStates = new Dictionary<int, RangeState>();

		private static FieldRef<Player, float> _maxPlaceDistance;

		private static FieldRef<Humanoid, ItemData> _rightItem;

		private static TakeInputDelegate _takeInput;

		private static bool _initialized;

		internal static MethodInfo UpdatePlacementMethod { get; private set; }

		internal static MethodInfo UpdatePlacementGhostMethod { get; private set; }

		internal static MethodInfo UpdateCameraMethod { get; private set; }

		internal static MethodInfo SetControlsMethod { get; private set; }

		internal static bool Initialize(out string error)
		{
			error = null;
			if (_initialized)
			{
				return true;
			}
			try
			{
				_maxPlaceDistance = AccessTools.FieldRefAccess<Player, float>(RequireField(typeof(Player), "m_maxPlaceDistance", typeof(float)));
				_rightItem = AccessTools.FieldRefAccess<Humanoid, ItemData>(RequireField(typeof(Humanoid), "m_rightItem", typeof(ItemData)));
				UpdatePlacementMethod = RequireMethod(typeof(Player), "UpdatePlacement", typeof(void), typeof(bool), typeof(float));
				UpdatePlacementGhostMethod = RequireMethod(typeof(Player), "UpdatePlacementGhost", typeof(void), typeof(bool));
				UpdateCameraMethod = RequireMethod(typeof(GameCamera), "UpdateCamera", typeof(void), typeof(float));
				SetControlsMethod = RequireMethod(typeof(Player), "SetControls", typeof(void), typeof(Vector3), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool));
				MethodInfo methodInfo = AccessTools.Method(typeof(Player), "TakeInput", Type.EmptyTypes, (Type[])null);
				if (methodInfo == null || methodInfo.ReturnType != typeof(bool))
				{
					throw new MissingMethodException(typeof(Player).FullName, "TakeInput()");
				}
				_takeInput = AccessTools.MethodDelegate<TakeInputDelegate>(methodInfo, (object)null, true);
				_initialized = true;
				return true;
			}
			catch (Exception ex)
			{
				Shutdown();
				error = RunicText.Get("text_d8ff831563aa") + ex.GetType().Name + ": " + ex.Message;
				return false;
			}
		}

		internal static void Shutdown()
		{
			ForceRestoreRanges();
			_maxPlaceDistance = null;
			_rightItem = null;
			_takeInput = null;
			UpdatePlacementMethod = null;
			UpdatePlacementGhostMethod = null;
			UpdateCameraMethod = null;
			SetControlsMethod = null;
			_initialized = false;
		}

		internal static bool IsLocalPlayer(Player player)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				return (Object)(object)player == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		internal static bool IsUsableLocalPlayer(Player player)
		{
			if (!IsLocalPlayer(player))
			{
				return false;
			}
			try
			{
				return !((Character)player).IsDead() && !((Character)player).IsTeleporting();
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsBuildToolEquipped(Player player)
		{
			if (!IsUsableLocalPlayer(player))
			{
				return false;
			}
			try
			{
				ItemData val = ((_rightItem != null) ? _rightItem.Invoke((Humanoid)(object)player) : null);
				return val != null && val.m_shared != null && (Object)(object)val.m_shared.m_buildPieces != (Object)null && ((Character)player).InPlaceMode();
			}
			catch
			{
				return false;
			}
		}

		internal static bool CanTakeInput(Player player)
		{
			if (!IsUsableLocalPlayer(player) || _takeInput == null)
			{
				return false;
			}
			try
			{
				return _takeInput(player);
			}
			catch
			{
				return false;
			}
		}

		internal static RangeLease EnterRemoteActionRange(Player player, float requestedPlayerRange, float requestedStationRange)
		{
			if (!_initialized || !IsLocalPlayer(player) || _maxPlaceDistance == null || !IsFinite(requestedPlayerRange) || !IsPositiveFinite(requestedStationRange))
			{
				return null;
			}
			int instanceID = ((Object)player).GetInstanceID();
			if (!RangeStates.TryGetValue(instanceID, out var value))
			{
				value = new RangeState
				{
					Player = player,
					Original = _maxPlaceDistance.Invoke(player),
					Requested = requestedStationRange,
					Depth = 0
				};
				RangeStates.Add(instanceID, value);
			}
			value.Depth++;
			_maxPlaceDistance.Invoke(player) = requestedPlayerRange;
			return new RangeLease(instanceID, value);
		}

		internal static void ForceRestoreRanges()
		{
			foreach (RangeState value in RangeStates.Values)
			{
				try
				{
					if ((Object)(object)value.Player != (Object)null && _maxPlaceDistance != null)
					{
						_maxPlaceDistance.Invoke(value.Player) = value.Original;
					}
				}
				catch
				{
				}
			}
			RangeStates.Clear();
		}

		internal static bool TryGetScopedStationRange(out float range)
		{
			foreach (RangeState value in RangeStates.Values)
			{
				if (value.Depth > 0 && IsPositiveFinite(value.Requested))
				{
					range = value.Requested;
					return true;
				}
			}
			range = 0f;
			return false;
		}

		private static void ExitRange(int playerId, RangeState expected)
		{
			if (!RangeStates.TryGetValue(playerId, out var value) || value != expected)
			{
				return;
			}
			value.Depth--;
			if (value.Depth <= 0)
			{
				if ((Object)(object)value.Player != (Object)null && _maxPlaceDistance != null)
				{
					_maxPlaceDistance.Invoke(value.Player) = value.Original;
				}
				RangeStates.Remove(playerId);
			}
		}

		private static FieldInfo RequireField(Type owner, string name, Type fieldType)
		{
			FieldInfo fieldInfo = AccessTools.Field(owner, name);
			if (fieldInfo == null || fieldInfo.FieldType != fieldType)
			{
				throw new MissingFieldException(owner.FullName, name);
			}
			return fieldInfo;
		}

		private static MethodInfo RequireMethod(Type owner, string name, Type returnType, params Type[] parameters)
		{
			MethodInfo methodInfo = AccessTools.Method(owner, name, parameters, (Type[])null);
			if (methodInfo == null || methodInfo.ReturnType != returnType)
			{
				throw new MissingMethodException(owner.FullName, name);
			}
			return methodInfo;
		}

		private static bool IsPositiveFinite(float value)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return value > 0f;
			}
			return false;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
}
namespace RunicBuildCamera.Core
{
	internal sealed class BuildCameraSession
	{
		internal bool IsActive { get; private set; }

		internal int PlayerInstanceId { get; private set; }

		internal Vector3 Anchor { get; private set; }

		internal Vector3 Position { get; private set; }

		internal float Yaw { get; private set; }

		internal float Pitch { get; private set; }

		internal float Range { get; private set; }

		internal Quaternion Rotation => Quaternion.Euler(Pitch, Yaw, 0f);

		internal bool Begin(int playerInstanceId, Vector3 anchor, Vector3 cameraPosition, Quaternion cameraRotation, float range)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			if (playerInstanceId == 0 || !IsFinite(anchor) || !IsFinite(cameraPosition) || !IsFinite(cameraRotation) || !IsPositiveFinite(range))
			{
				return false;
			}
			Vector3 eulerAngles = ((Quaternion)(ref cameraRotation)).eulerAngles;
			IsActive = true;
			PlayerInstanceId = playerInstanceId;
			Anchor = anchor;
			Range = range;
			Yaw = CameraMotion.NormalizeAngle(eulerAngles.y);
			Pitch = Mathf.Clamp(CameraMotion.NormalizeAngle(eulerAngles.x), -89f, 89f);
			Position = CameraMotion.ClampToAnchor(cameraPosition, anchor, range);
			return true;
		}

		internal bool BelongsTo(int playerInstanceId)
		{
			if (IsActive && playerInstanceId != 0)
			{
				return PlayerInstanceId == playerInstanceId;
			}
			return false;
		}

		internal void Reanchor(Vector3 anchor)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_001d: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (IsActive && IsFinite(anchor))
			{
				Vector3 val = anchor - Anchor;
				Anchor = anchor;
				Position = CameraMotion.ClampToAnchor(Position + val, Anchor, Range);
			}
		}

		internal void SetPose(in CameraPose pose)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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)
			if (IsActive && IsFinite(pose.Position) && IsFinite(pose.Yaw) && IsFinite(pose.Pitch))
			{
				Position = CameraMotion.ClampToAnchor(pose.Position, Anchor, Range);
				Yaw = CameraMotion.NormalizeAngle(pose.Yaw);
				Pitch = Mathf.Clamp(pose.Pitch, -89f, 89f);
			}
		}

		internal void UpdateRange(float range)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (IsActive && IsPositiveFinite(range))
			{
				Range = range;
				Position = CameraMotion.ClampToAnchor(Position, Anchor, Range);
			}
		}

		internal void End()
		{
			//IL_000f: 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)
			IsActive = false;
			PlayerInstanceId = 0;
			Anchor = Vector3.zero;
			Position = Vector3.zero;
			Yaw = 0f;
			Pitch = 0f;
			Range = 0f;
		}

		private static bool IsFinite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (IsFinite(value.x) && IsFinite(value.y))
			{
				return IsFinite(value.z);
			}
			return false;
		}

		private static bool IsFinite(Quaternion value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (IsFinite(value.x) && IsFinite(value.y) && IsFinite(value.z))
			{
				return IsFinite(value.w);
			}
			return false;
		}

		private static bool IsPositiveFinite(float value)
		{
			if (IsFinite(value))
			{
				return value > 0f;
			}
			return false;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal static class CameraExitInput
	{
		private static readonly string[] HotbarButtons = new string[8] { "Hotbar1", "Hotbar2", "Hotbar3", "Hotbar4", "Hotbar5", "Hotbar6", "Hotbar7", "Hotbar8" };

		internal static bool ReleaseIfRequested(bool nativeInput, Func<bool> requested, Action stop)
		{
			if (!nativeInput || !requested())
			{
				return false;
			}
			stop();
			return true;
		}

		internal static bool HotbarRequested(Func<string, bool> down)
		{
			string[] hotbarButtons = HotbarButtons;
			foreach (string arg in hotbarButtons)
			{
				if (down(arg))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool HideRequested(bool keyboardHide, bool alternateGamepadLayout, bool shortControllerRelease, bool inPlaceMode, bool altKeys)
		{
			if (alternateGamepadLayout)
			{
				return !inPlaceMode && shortControllerRelease && altKeys;
			}
			if (!keyboardHide)
			{
				if (shortControllerRelease && !altKeys)
				{
					return !inPlaceMode;
				}
				return false;
			}
			return true;
		}
	}
	internal readonly struct CameraMotionInput
	{
		internal float Right { get; }

		internal float Up { get; }

		internal float Forward { get; }

		internal float YawDelta { get; }

		internal float PitchDelta { get; }

		internal bool Fast { get; }

		internal CameraMotionInput(float right, float up, float forward, float yawDelta, float pitchDelta, bool fast)
		{
			Right = Mathf.Clamp(right, -1f, 1f);
			Up = Mathf.Clamp(up, -1f, 1f);
			Forward = Mathf.Clamp(forward, -1f, 1f);
			YawDelta = yawDelta;
			PitchDelta = pitchDelta;
			Fast = fast;
		}
	}
	internal readonly struct CameraPose
	{
		internal Vector3 Position { get; }

		internal float Yaw { get; }

		internal float Pitch { get; }

		internal Quaternion Rotation => Quaternion.Euler(Pitch, Yaw, 0f);

		internal CameraPose(Vector3 position, float yaw, float pitch)
		{
			//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)
			Position = position;
			Yaw = yaw;
			Pitch = pitch;
		}
	}
	internal static class CameraMotion
	{
		internal static CameraPose Step(Vector3 position, float yaw, float pitch, Vector3 anchor, float range, in CameraMotionInput input, float deltaTime, float moveSpeed, float fastMoveMultiplier, bool worldRelativeMovement)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			float num = SanitizeDeltaTime(deltaTime);
			float num2 = NormalizeAngle(yaw + Sanitize(input.YawDelta));
			float num3 = Mathf.Clamp(pitch + Sanitize(input.PitchDelta), -89f, 89f);
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(input.Right, input.Up, input.Forward);
			if (((Vector3)(ref val)).sqrMagnitude > 1f)
			{
				((Vector3)(ref val)).Normalize();
			}
			Vector3 val2;
			if (worldRelativeMovement)
			{
				val2 = val;
			}
			else
			{
				val2 = Quaternion.Euler(num3, num2, 0f) * new Vector3(val.x, 0f, val.z) + Vector3.up * val.y;
				if (((Vector3)(ref val2)).sqrMagnitude > 1f)
				{
					((Vector3)(ref val2)).Normalize();
				}
			}
			float num4 = Mathf.Max(0f, Sanitize(moveSpeed));
			if (input.Fast)
			{
				num4 *= Mathf.Max(1f, Sanitize(fastMoveMultiplier));
			}
			Vector3 position2 = position + val2 * (num4 * num);
			float range2 = ((IsFinite(range) && range > 0f) ? range : 0.01f);
			return new CameraPose(ClampToAnchor(position2, anchor, range2), num2, num3);
		}

		internal static Vector3 ClampToAnchor(Vector3 position, Vector3 anchor, float range)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_0024: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!IsFinite(position) || !IsFinite(anchor))
			{
				return anchor;
			}
			if (!IsFinite(range) || range <= 0f)
			{
				return anchor;
			}
			Vector3 val = position - anchor;
			float num = range * range;
			if (((Vector3)(ref val)).sqrMagnitude <= num)
			{
				return position;
			}
			return anchor + ((Vector3)(ref val)).normalized * range;
		}

		internal static float NormalizeAngle(float degrees)
		{
			if (!IsFinite(degrees))
			{
				return 0f;
			}
			degrees %= 360f;
			if (degrees > 180f)
			{
				degrees -= 360f;
			}
			if (degrees <= -180f)
			{
				degrees += 360f;
			}
			return degrees;
		}

		private static float SanitizeDeltaTime(float value)
		{
			if (!IsFinite(value))
			{
				return 0f;
			}
			return Mathf.Clamp(value, 0f, 0.1f);
		}

		private static float Sanitize(float value)
		{
			if (!IsFinite(value))
			{
				return 0f;
			}
			return value;
		}

		private static bool IsFinite(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (IsFinite(value.x) && IsFinite(value.y))
			{
				return IsFinite(value.z);
			}
			return false;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal enum PickupRejectionReason
	{
		None,
		InvalidNetworkIdentity,
		OutsideRange,
		Fish,
		AutoPickupDisabled,
		Piece,
		InTar,
		UniqueOrQuestItem,
		InventoryFull,
		TooHeavy,
		CoolingDown
	}
	internal readonly struct PickupCandidateFacts
	{
		internal bool HasValidNetworkIdentity { get; }

		internal bool IsWithinRange { get; }

		internal bool IsFish { get; }

		internal bool AutoPickupEnabled { get; }

		internal bool IsPiece { get; }

		internal bool IsInTar { get; }

		internal bool IsUniqueOrQuestItem { get; }

		internal bool InventoryCanAdd { get; }

		internal bool WouldExceedCarryWeight { get; }

		internal bool IsCoolingDown { get; }

		internal PickupCandidateFacts(bool hasValidNetworkIdentity, bool isWithinRange, bool isFish, bool autoPickupEnabled, bool isPiece, bool isInTar, bool isUniqueOrQuestItem, bool inventoryCanAdd, bool wouldExceedCarryWeight, bool isCoolingDown)
		{
			HasValidNetworkIdentity = hasValidNetworkIdentity;
			IsWithinRange = isWithinRange;
			IsFish = isFish;
			AutoPickupEnabled = autoPickupEnabled;
			IsPiece = isPiece;
			IsInTar = isInTar;
			IsUniqueOrQuestItem = isUniqueOrQuestItem;
			InventoryCanAdd = inventoryCanAdd;
			WouldExceedCarryWeight = wouldExceedCarryWeight;
			IsCoolingDown = isCoolingDown;
		}
	}
	internal static class PickupPolicy
	{
		internal const int CandidateCapacity = 128;

		internal const int MaximumAttemptsPerScan = 16;

		internal const int MaximumCooldownEntries = 256;

		internal const float MinimumOwnershipRetrySeconds = 0.2f;

		internal const float FailedPickupRetrySeconds = 0.5f;

		internal const float CompletedPickupDedupeSeconds = 5f;

		internal static bool IsScanDue(float now, float nextScanAt)
		{
			if (IsFinite(now) && IsFinite(nextScanAt))
			{
				return now >= nextScanAt;
			}
			return false;
		}

		internal static float NextScanAt(float now, float configuredIntervalSeconds)
		{
			float right = (IsFinite(configuredIntervalSeconds) ? Math.Max(0.02f, configuredIntervalSeconds) : 0.2f);
			return SaturatingAdd(now, right);
		}

		internal static bool IsWithinRange(float squaredDistance, float rangeMeters)
		{
			if (!IsFinite(squaredDistance) || squaredDistance < 0f || !IsFinite(rangeMeters) || rangeMeters <= 0f)
			{
				return false;
			}
			double num = rangeMeters;
			return (double)squaredDistance <= num * num;
		}

		internal static PickupRejectionReason Evaluate(in PickupCandidateFacts facts)
		{
			if (!facts.HasValidNetworkIdentity)
			{
				return PickupRejectionReason.InvalidNetworkIdentity;
			}
			if (!facts.IsWithinRange)
			{
				return PickupRejectionReason.OutsideRange;
			}
			if (facts.IsFish)
			{
				return PickupRejectionReason.Fish;
			}
			if (!facts.AutoPickupEnabled)
			{
				return PickupRejectionReason.AutoPickupDisabled;
			}
			if (facts.IsPiece)
			{
				return PickupRejectionReason.Piece;
			}
			if (facts.IsInTar)
			{
				return PickupRejectionReason.InTar;
			}
			if (facts.IsUniqueOrQuestItem)
			{
				return PickupRejectionReason.UniqueOrQuestItem;
			}
			if (!facts.InventoryCanAdd)
			{
				return PickupRejectionReason.InventoryFull;
			}
			if (facts.WouldExceedCarryWeight)
			{
				return PickupRejectionReason.TooHeavy;
			}
			if (facts.IsCoolingDown)
			{
				return PickupRejectionReason.CoolingDown;
			}
			return PickupRejectionReason.None;
		}

		internal static float OwnershipRetryAt(float now, float configuredIntervalSeconds)
		{
			return SaturatingAdd(now, Math.Max(0.2f, IsFinite(configuredIntervalSeconds) ? configuredIntervalSeconds : 0f));
		}

		internal static float FailedPickupRetryAt(float now)
		{
			return SaturatingAdd(now, 0.5f);
		}

		internal static float CompletedPickupRetryAt(float now)
		{
			return SaturatingAdd(now, 5f);
		}

		private static float SaturatingAdd(float left, float right)
		{
			if (!IsFinite(left) || !IsFinite(right))
			{
				return float.MaxValue;
			}
			double num = (double)left + (double)right;
			if (!(num >= 3.4028234663852886E+38))
			{
				return (float)num;
			}
			return float.MaxValue;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal static class PickupScanWindow
	{
		internal static int CopyNext<T>(IReadOnlyList<T> items, T[] buffer, ref int cursor)
		{
			if (items == null || items.Count == 0 || buffer.Length == 0)
			{
				cursor = 0;
				return 0;
			}
			int count = items.Count;
			if (cursor < 0 || cursor >= count)
			{
				cursor = 0;
			}
			int num = Math.Min(count, buffer.Length);
			for (int i = 0; i < num; i++)
			{
				buffer[i] = items[cursor];
				cursor = (cursor + 1) % count;
			}
			return num;
		}
	}
}