Decompiled source of AstvardServerMod v2.0.2

BepInEx/plugins/Astvard.Core.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Astvard.Core.Net;
using Astvard.Core.Placement;
using Astvard.Core.Ui;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("Astvard.Dev")]
[assembly: InternalsVisibleTo("Astvard.Admin")]
[assembly: InternalsVisibleTo("Astvard.Server")]
[assembly: InternalsVisibleTo("Astvard.Player")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Astvard.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d8721dbc184a53c5b52947c0c2c8980b874b1ebb")]
[assembly: AssemblyProduct("Astvard.Core")]
[assembly: AssemblyTitle("Astvard.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Astvard.Core
{
	internal static class Access
	{
		internal static Func<bool> Admin = () => false;

		internal static bool AdminInstalled;

		internal static bool AdminPays;

		internal static string AdminPaysWhy = "так настроено в админке";

		internal static bool IsAdmin => Admin();
	}
	internal static class Build
	{
		private const string Prefix = "Astvard.";

		private static readonly Dictionary<string, string> Called = new Dictionary<string, string>
		{
			{ "Core", "ядро" },
			{ "Player", "игрок" },
			{ "Admin", "админка" },
			{ "Dev", "отладка" },
			{ "Server", "сервер" }
		};

		private static string _here;

		private static ZNet _toldOn;

		internal static string Here => _here ?? (_here = Pack());

		internal static string Say(string packed)
		{
			if (string.IsNullOrEmpty(packed))
			{
				return "";
			}
			int num = packed.IndexOf(": ", StringComparison.Ordinal);
			string text = ((num > 0) ? packed.Substring(0, num) : "");
			string obj = ((num > 0) ? packed.Substring(num + 2) : packed);
			List<string> list = new List<string>();
			Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
			string[] array = obj.Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				int num2 = text2.LastIndexOf(' ');
				if (num2 > 0)
				{
					string text3 = text2.Substring(0, num2);
					string text4 = text2.Substring(num2 + 1);
					if (!dictionary.TryGetValue(text4, out var value))
					{
						value = (dictionary[text4] = new List<string>());
						list.Add(text4);
					}
					value.Add(Called.TryGetValue(text3, out var value2) ? value2 : text3);
				}
			}
			string text5 = ((text.Length > 0) ? (text + ", ") : "");
			if (list.Count == 0)
			{
				return text;
			}
			if (list.Count == 1)
			{
				return text5 + "сборка " + list[0];
			}
			List<string> list3 = new List<string>();
			foreach (string item in list)
			{
				list3.Add(item + " (" + string.Join(", ", dictionary[item]) + ")");
			}
			return text5 + "сборки " + string.Join(", ", list3);
		}

		internal static string VersionOf(string packed)
		{
			if (string.IsNullOrEmpty(packed))
			{
				return "";
			}
			int num = packed.IndexOf(": ", StringComparison.Ordinal);
			if (num <= 0)
			{
				return "";
			}
			return packed.Substring(0, num);
		}

		internal static int Compare(string mine, string theirs)
		{
			string[] array = (mine ?? "").Split(new char[1] { '.' });
			string[] array2 = (theirs ?? "").Split(new char[1] { '.' });
			for (int i = 0; i < 3; i++)
			{
				if (i >= array.Length || i >= array2.Length)
				{
					return 0;
				}
				if (!int.TryParse(array[i], out var result) || !int.TryParse(array2[i], out var result2))
				{
					return 0;
				}
				if (result != result2)
				{
					if (result >= result2)
					{
						return 1;
					}
					return -1;
				}
			}
			return 0;
		}

		internal static string Differs(string serverPacked)
		{
			string theirs = VersionOf(serverPacked);
			int num = Compare("2.0.2", theirs);
			if (num < 0)
			{
				return "На сервере мод новее — обнови: часть кнопок может работать не так";
			}
			if (num > 0)
			{
				return "На сервере мод старше: заходить это не мешает, но нового он ещё не умеет";
			}
			return "";
		}

		internal static void TellIfOther(string serverPacked)
		{
			ZNet instance = ZNet.instance;
			string text = Differs(serverPacked);
			if (!((Object)(object)instance == (Object)null) && text.Length != 0 && instance != _toldOn)
			{
				_toldOn = instance;
				string text2 = VersionOf(serverPacked);
				Plugin.Log.LogInfo((object)("The server runs the mod " + text2 + ", this client 2.0.2."));
				Chat instance2 = Chat.instance;
				if (instance2 != null)
				{
					((Terminal)instance2).AddString("Astvard: у тебя мод 2.0.2, на сервере " + text2 + ". " + text + ".");
				}
			}
		}

		private static string Pack()
		{
			List<string> list = new List<string>();
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				string name = assembly.GetName().Name;
				if (name != null && name.StartsWith("Astvard.", StringComparison.Ordinal))
				{
					list.Add(name.Substring("Astvard.".Length) + " " + CommitOf(assembly));
				}
			}
			list.Sort(delegate(string a, string b)
			{
				bool flag = a.StartsWith("Core ", StringComparison.Ordinal);
				bool flag2 = b.StartsWith("Core ", StringComparison.Ordinal);
				return (flag != flag2) ? ((!flag) ? 1 : (-1)) : string.CompareOrdinal(a, b);
			});
			return "2.0.2: " + string.Join(", ", list);
		}

		private static string CommitOf(Assembly assembly)
		{
			string text = ((AssemblyInformationalVersionAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)))?.InformationalVersion ?? "";
			int num = text.IndexOf('+');
			if (num < 0 || num == text.Length - 1)
			{
				return "?";
			}
			string text2 = text.Substring(num + 1);
			if (text2.Length <= 7)
			{
				return text2;
			}
			return text2.Substring(0, 7);
		}
	}
	internal static class Headless
	{
		private static bool? _yes;

		internal static bool Yes
		{
			get
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Invalid comparison between Unknown and I4
				if (!_yes.HasValue)
				{
					_yes = (int)SystemInfo.graphicsDeviceType == 4;
				}
				return _yes.Value;
			}
		}
	}
	[BepInPlugin("astvard.core", "Astvard Core", "2.0.2")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal const string Guid = "astvard.core";

		internal const string Title = "Astvard Core";

		internal const string Version = "2.0.2";

		internal const int Protocol = 1;

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			((Component)this).gameObject.AddComponent<Projection.Driver>();
			((Component)this).gameObject.AddComponent<Rules.Driver>();
			((Component)this).gameObject.AddComponent<Weather.Driver>();
			((Component)this).gameObject.AddComponent<Wind.Driver>();
			if (!Headless.Yes)
			{
				((Component)this).gameObject.AddComponent<CraftTabs.Driver>();
				((Component)this).gameObject.AddComponent<Corner.Driver>();
			}
			_harmony = new Harmony("astvard.core");
			_harmony.PatchAll();
			Log.LogInfo((object)("Astvard Core 2.0.2 (protocol " + 1 + ") loaded, " + (Headless.Yes ? "headless" : "with a screen") + "."));
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace Astvard.Core.Ui
{
	internal static class Corner
	{
		internal sealed class Driver : MonoBehaviour
		{
			private void Update()
			{
				Tick();
			}
		}

		private const float Every = 0.25f;

		private const int Size = 20;

		private const float Width = 520f;

		private const float Gap = 10f;

		private const float FurthestBelow = 170f;

		private const string HeadInk = "#FFC759";

		private static readonly List<Func<string>> Sayers = new List<Func<string>>();

		private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4];

		private static GameObject _root;

		private static Text _text;

		private static HotkeyBar _bar;

		private static string _shown;

		private static float _at;

		private static bool _told;

		private static bool _toldNoBar;

		private static readonly HashSet<string> Troubles = new HashSet<string>();

		internal static void Add(Func<string> says)
		{
			Sayers.Add(says);
		}

		internal static string Head(string words)
		{
			return "<color=#FFC759>" + Plain(words) + "</color>";
		}

		internal static string Plain(string words)
		{
			return (words ?? "").Replace('<', '‹').Replace('>', '›');
		}

		private static void Tick()
		{
			if (Time.unscaledTime < _at)
			{
				return;
			}
			_at = Time.unscaledTime + 0.25f;
			string text = (Covered() ? null : Gather());
			if (string.IsNullOrEmpty(text))
			{
				if ((Object)(object)_root != (Object)null && _root.activeSelf)
				{
					_root.SetActive(false);
				}
			}
			else if (!((Object)(object)_root == (Object)null) || Build())
			{
				if (text != _shown)
				{
					_shown = text;
					_text.text = text;
				}
				bool flag = Place();
				if (_root.activeSelf != flag)
				{
					_root.SetActive(flag);
				}
			}
		}

		private static bool Covered()
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Root.Canvas == (Object)null) && !((Object)(object)Hud.instance == (Object)null) && Hud.instance.IsVisible() && !InventoryGui.IsVisible() && !Minimap.IsOpen() && !Menu.IsVisible())
			{
				return StoreGui.IsVisible();
			}
			return true;
		}

		private static string Gather()
		{
			string text = null;
			foreach (Func<string> sayer in Sayers)
			{
				string text2;
				try
				{
					text2 = sayer();
				}
				catch (Exception ex)
				{
					if (Troubles.Add(ex.GetType().Name + ": " + ex.Message))
					{
						Plugin.Log.LogError((object)("A line of the corner could not be said: " + ex));
					}
					continue;
				}
				if (!string.IsNullOrEmpty(text2))
				{
					text = ((text == null) ? text2 : (text + "\n\n" + text2));
				}
			}
			return text;
		}

		private static bool Build()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Root.Canvas == (Object)null)
			{
				return false;
			}
			_root = new GameObject("corner", new Type[1] { typeof(RectTransform) });
			_root.transform.SetParent(Root.Canvas, false);
			RectTransform val = (RectTransform)_root.transform;
			val.anchorMin = Vector2.zero;
			val.anchorMax = Vector2.zero;
			val.pivot = new Vector2(0f, 1f);
			val.sizeDelta = new Vector2(520f, 40f);
			_text = Widgets.Label(_root.transform, "words", "", 20, (TextAnchor)0);
			_text.horizontalOverflow = (HorizontalWrapMode)0;
			_text.verticalOverflow = (VerticalWrapMode)1;
			_text.supportRichText = true;
			RectTransform rectTransform = ((Graphic)_text).rectTransform;
			rectTransform.anchorMin = Vector2.zero;
			rectTransform.anchorMax = Vector2.one;
			rectTransform.offsetMin = Vector2.zero;
			rectTransform.offsetMax = Vector2.zero;
			Outline obj = ((Component)_text).gameObject.AddComponent<Outline>();
			((Shadow)obj).effectColor = new Color(0f, 0f, 0f, 0.85f);
			((Shadow)obj).effectDistance = new Vector2(1.5f, -1.5f);
			_shown = null;
			return true;
		}

		private static bool Place()
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_bar == (Object)null)
			{
				_bar = ((Component)Hud.instance).GetComponentInChildren<HotkeyBar>(true);
			}
			if ((Object)(object)_bar == (Object)null)
			{
				if (!_toldNoBar)
				{
					Plugin.Log.LogWarning((object)"Corner: no hotkey bar in the game's interface — its words are not shown.");
				}
				_toldNoBar = true;
				return false;
			}
			float left = float.MaxValue;
			float bottom = float.MaxValue;
			Transform transform = ((Component)_bar).transform;
			Reach((RectTransform)(object)((transform is RectTransform) ? transform : null), ref left, ref bottom);
			foreach (Transform item in ((Component)_bar).transform)
			{
				object obj = (object)item;
				Reach((RectTransform)((obj is RectTransform) ? obj : null), ref left, ref bottom);
			}
			if (left == float.MaxValue)
			{
				return false;
			}
			float y = _root.transform.lossyScale.y;
			float num = bottom - 10f * y;
			float num2 = float.MaxValue;
			TMP_Text val = (((Object)(object)MessageHud.instance != (Object)null) ? MessageHud.instance.m_messageText : null);
			if ((Object)(object)val != (Object)null)
			{
				val.rectTransform.GetWorldCorners(Corners);
				Vector3[] corners = Corners;
				foreach (Vector3 val2 in corners)
				{
					num2 = Mathf.Min(num2, val2.y);
				}
				if (num2 < num && num2 > bottom - 170f * y)
				{
					num = num2 - 4f * y;
				}
			}
			_root.transform.position = new Vector3(left, num, 0f);
			bool flag = left >= 0f && left < (float)Screen.width && num > 0f && num <= (float)Screen.height;
			if (!_told)
			{
				_told = true;
				Plugin.Log.LogInfo((object)("Corner: words under the hotkey bar at (" + Px(left) + ", " + Px(num) + ") of a screen " + Screen.width + "x" + Screen.height + " — the bar's left edge " + Px(left) + " and its bottom " + Px(bottom) + " with " + ((Component)_bar).transform.childCount + " slots, the game's own words there down to " + ((num2 == float.MaxValue) ? "nothing found" : Px(num2)) + ", our canvas scaled " + y.ToString("0.###", CultureInfo.InvariantCulture) + (flag ? "." : " — off the screen, not shown.")));
			}
			return flag;
		}

		private static void Reach(RectTransform rect, ref float left, ref float bottom)
		{
			//IL_0021: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)rect == (Object)null))
			{
				rect.GetWorldCorners(Corners);
				Vector3[] corners = Corners;
				foreach (Vector3 val in corners)
				{
					left = Mathf.Min(left, val.x);
					bottom = Mathf.Min(bottom, val.y);
				}
			}
		}

		private static string Px(float value)
		{
			return value.ToString("0", CultureInfo.InvariantCulture);
		}
	}
	internal static class CraftTabs
	{
		private sealed class Built
		{
			internal Button Button;

			internal MenuPage Page;
		}

		internal sealed class Driver : MonoBehaviour
		{
			private void Update()
			{
				Tick();
			}
		}

		private static readonly List<Built> Tabs = new List<Built>();

		private static InventoryGui _gui;

		private static int _open = -1;

		private static GameObject _gameList;

		private static GameObject _gameDetails;

		private static bool _gameHidden;

		internal static void Build(InventoryGui gui)
		{
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			_gui = gui;
			Tabs.Clear();
			_gameHidden = false;
			if (MenuTabs.All.Count == 0)
			{
				return;
			}
			RectTransform crafting = gui.m_crafting;
			Transform val = (((Object)(object)crafting != (Object)null) ? PaneHolding((Transform)(object)crafting, (Transform)(object)gui.m_recipeListRoot) : null);
			Transform val2 = (((Object)(object)crafting != (Object)null) ? ((Transform)crafting).Find("Decription") : null);
			if ((Object)(object)gui.m_tabUpgrade == (Object)null || (Object)(object)gui.m_tabCraft == (Object)null || (Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)gui.m_recipeElementPrefab == (Object)null)
			{
				Plugin.Log.LogWarning((object)"The crafting window is not built the way it was on 24.09.2026 (tabs / RecipeList / Decription / recipe row) — our tabs are not added.");
				return;
			}
			_gameList = ((Component)val).gameObject;
			_gameDetails = ((Component)val2).gameObject;
			StringBuilder stringBuilder = new StringBuilder();
			Transform transform = ((Component)gui.m_tabUpgrade).transform;
			for (int i = 0; i < MenuTabs.All.Count; i++)
			{
				MenuTabs.Tab tab = MenuTabs.All[i];
				int index = i;
				MenuPage page = MenuPage.Build(gui, tab, val, val2);
				Button val3 = GameLook.Copy(gui.m_tabUpgrade, transform.parent, tab.Caption, (UnityAction)delegate
				{
					Select(index);
				});
				((Component)val3).transform.SetSiblingIndex(transform.GetSiblingIndex() + 1);
				transform = ((Component)val3).transform;
				Tabs.Add(new Built
				{
					Button = val3,
					Page = page
				});
				stringBuilder.Append((i == 0) ? "" : ", ").Append('«').Append(tab.Caption)
					.Append('»');
			}
			if (_open >= Tabs.Count)
			{
				_open = -1;
			}
			Paint();
			Plugin.Log.LogInfo((object)("Tabs added to the crafting window: " + stringBuilder?.ToString() + "."));
		}

		internal static void CraftingUpdated()
		{
			if (Tabs.Count != 0)
			{
				Place();
				Paint();
			}
		}

		internal static void GameTabChosen()
		{
			if (_open >= 0)
			{
				_open = -1;
				Paint();
			}
		}

		internal static void Refresh()
		{
			if (_open >= 0 && _open < Tabs.Count)
			{
				Tabs[_open].Page.Refresh();
			}
		}

		private static void Tick()
		{
			if (_open >= 0 && _open < Tabs.Count && InventoryGui.IsVisible())
			{
				Tabs[_open].Page.Tick();
			}
		}

		private static void Select(int index)
		{
			if (_open != index)
			{
				_open = index;
				Paint();
			}
		}

		private static void Paint()
		{
			if (Tabs.Count == 0)
			{
				return;
			}
			for (int i = 0; i < Tabs.Count; i++)
			{
				((Selectable)Tabs[i].Button).interactable = i != _open;
				if (i != _open)
				{
					Tabs[i].Page.Show(shown: false);
				}
			}
			HideGame(_open >= 0);
			if (_open >= 0)
			{
				Tabs[_open].Page.Show(shown: true);
				((Selectable)_gui.m_tabCraft).interactable = true;
				((Selectable)_gui.m_tabUpgrade).interactable = true;
			}
		}

		private static void HideGame(bool hide)
		{
			if (hide != _gameHidden && !((Object)(object)_gameList == (Object)null))
			{
				_gameHidden = hide;
				_gameList.SetActive(!hide);
				_gameDetails.SetActive(!hide);
			}
		}

		private static void Place()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)((Component)_gui.m_tabCraft).transform;
			RectTransform val2 = (RectTransform)((Component)_gui.m_tabUpgrade).transform;
			Transform val3 = (Transform)(object)val2;
			foreach (Built tab in Tabs)
			{
				((Component)tab.Button).transform.SetSiblingIndex(val3.GetSiblingIndex() + 1);
				val3 = ((Component)tab.Button).transform;
			}
			if ((Object)(object)((Component)((Transform)val2).parent).GetComponent<HorizontalOrVerticalLayoutGroup>() != (Object)null || (!((Component)val2).gameObject.activeSelf && !((Component)val).gameObject.activeSelf))
			{
				return;
			}
			float num = Left(val2) - Right(val);
			if (num <= 0f)
			{
				num = 4f;
			}
			RectTransform val4 = (((Component)val2).gameObject.activeSelf ? val2 : val);
			foreach (Built tab2 in Tabs)
			{
				RectTransform val5 = (RectTransform)((Component)tab2.Button).transform;
				TMP_Text componentInChildren = ((Component)tab2.Button).GetComponentInChildren<TMP_Text>(true);
				Rect rect = val2.rect;
				float num2 = ((Rect)(ref rect)).width;
				if ((Object)(object)componentInChildren != (Object)null)
				{
					num2 = Mathf.Max(num2, componentInChildren.preferredWidth + 24f);
				}
				val5.SetSizeWithCurrentAnchors((Axis)0, num2);
				float num3 = Right(val4) + num;
				val5.anchoredPosition = new Vector2(num3 + val5.pivot.x * num2, val4.anchoredPosition.y);
				val4 = val5;
			}
		}

		private static float Left(RectTransform r)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			float x = r.anchoredPosition.x;
			float x2 = r.pivot.x;
			Rect rect = r.rect;
			return x - x2 * ((Rect)(ref rect)).width;
		}

		private static float Right(RectTransform r)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			float x = r.anchoredPosition.x;
			float num = 1f - r.pivot.x;
			Rect rect = r.rect;
			return x + num * ((Rect)(ref rect)).width;
		}

		private static Transform PaneHolding(Transform window, Transform inside)
		{
			Transform val = inside;
			while ((Object)(object)val != (Object)null)
			{
				if ((Object)(object)val.parent == (Object)(object)window)
				{
					return val;
				}
				val = val.parent;
			}
			return null;
		}
	}
	internal static class Fields
	{
		internal sealed class Look
		{
			internal TMP_FontAsset Font;

			internal Material Material;

			internal float Size = 20f;

			internal Color Ink = Theme.Ink;

			internal Sprite Backing;
		}

		private static readonly List<TMP_InputField> Live = new List<TMP_InputField>();

		private static int _askedFrame = -1;

		private static bool _typing;

		private static bool _told;

		private const float TextScale = 0.8f;

		internal static bool Typing
		{
			get
			{
				if (_askedFrame == Time.frameCount)
				{
					return _typing;
				}
				_askedFrame = Time.frameCount;
				_typing = false;
				for (int num = Live.Count - 1; num >= 0; num--)
				{
					TMP_InputField val = Live[num];
					if ((Object)(object)val == (Object)null)
					{
						Live.RemoveAt(num);
					}
					else if (val.isFocused)
					{
						_typing = true;
					}
				}
				return _typing;
			}
		}

		internal static Look LookOf(InventoryGui gui, Transform details)
		{
			//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)
			Look look = new Look();
			TMP_Text componentInChildren = ((Component)gui.m_craftButton).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				look.Font = componentInChildren.font;
				look.Material = componentInChildren.fontSharedMaterial;
				look.Size = componentInChildren.fontSize;
				look.Ink = ((Graphic)componentInChildren).color;
			}
			look.Backing = BackingOf(details);
			if (!_told)
			{
				_told = true;
				Plugin.Log.LogInfo((object)("Fields wear " + (((Object)(object)look.Font != (Object)null) ? ((Object)look.Font).name : "TextMeshPro's default font") + " at " + look.Size + " on " + (((Object)(object)look.Backing != (Object)null) ? ((Object)look.Backing).name : "a plain dark box") + "."));
			}
			return look;
		}

		private static Sprite BackingOf(Transform details)
		{
			if ((Object)(object)details == (Object)null)
			{
				return null;
			}
			Image[] componentsInChildren = ((Component)details).GetComponentsInChildren<Image>(true);
			foreach (Image val in componentsInChildren)
			{
				if ((Object)(object)val.sprite != (Object)null && ((Object)val.sprite).name.Contains("background"))
				{
					return val.sprite;
				}
			}
			Image component = ((Component)details).GetComponent<Image>();
			if (!((Object)(object)component != (Object)null))
			{
				return null;
			}
			return component.sprite;
		}

		internal static TMP_InputField Make(Transform parent, MenuTabs.Entry entry, Look look)
		{
			//IL_000f: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: 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)
			//IL_01f6: Expected O, but got Unknown
			GameObject obj = TMP_DefaultControls.CreateInputField(new Resources
			{
				inputField = look.Backing
			});
			((Object)obj).name = "Astvard " + (entry.Caption ?? entry.Hint) + " field";
			obj.transform.SetParent(parent, false);
			((Graphic)obj.GetComponent<Image>()).color = (Color)(((Object)(object)look.Backing != (Object)null) ? Color.white : new Color(0f, 0f, 0f, 0.5f));
			TMP_InputField component = obj.GetComponent<TMP_InputField>();
			if ((Object)(object)look.Font != (Object)null)
			{
				component.fontAsset = look.Font;
			}
			component.pointSize = look.Size * 0.8f;
			TMP_Text[] componentsInChildren = obj.GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val in componentsInChildren)
			{
				if ((Object)(object)look.Material != (Object)null)
				{
					val.fontSharedMaterial = look.Material;
				}
				val.alignment = (TextAlignmentOptions)513;
			}
			((Graphic)component.textComponent).color = look.Ink;
			Graphic placeholder = component.placeholder;
			TMP_Text val2 = (TMP_Text)(object)((placeholder is TMP_Text) ? placeholder : null);
			if (val2 != null)
			{
				val2.text = entry.Hint;
				val2.fontStyle = (FontStyles)0;
				((Graphic)val2).color = new Color(look.Ink.r, look.Ink.g, look.Ink.b, 0.45f);
			}
			component.customCaretColor = true;
			component.caretColor = look.Ink;
			component.selectionColor = new Color(look.Ink.r, look.Ink.g, look.Ink.b, 0.3f);
			component.characterLimit = entry.Limit;
			if (entry.Accepts != null)
			{
				component.onValidateInput = (OnValidateInput)((string text, int index, char added) => (entry.Accepts.IndexOf(added) >= 0) ? added : '\0');
			}
			component.SetTextWithoutNotify(entry.Text);
			((UnityEvent<string>)(object)component.onValueChanged).AddListener((UnityAction<string>)delegate(string typed)
			{
				entry.Text = typed;
			});
			Live.Add(component);
			((UnityEvent<string>)(object)component.onSubmit).AddListener((UnityAction<string>)delegate(string typed)
			{
				if (!entry.IsBare)
				{
					Widgets.Announce(entry.Caption);
					entry.Apply(typed);
				}
			});
			return component;
		}
	}
	internal static class GameLook
	{
		private static readonly HashSet<string> ToldOf = new HashSet<string>();

		internal static Button Copy(Button source, Transform parent, string caption, UnityAction onClick)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			Button obj = Object.Instantiate<Button>(source, parent);
			((Object)obj).name = "Astvard " + caption;
			LeaveUnselected((Selectable)(object)obj, ((Object)source).name);
			obj.onClick = new ButtonClickedEvent();
			((UnityEvent)obj.onClick).AddListener((UnityAction)delegate
			{
				Widgets.Announce(caption);
			});
			((UnityEvent)obj.onClick).AddListener(onClick);
			((Selectable)obj).interactable = true;
			((Component)obj).gameObject.SetActive(true);
			Caption(obj, caption);
			return obj;
		}

		internal static void LeaveUnselected(Selectable selectable, string from)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (ToldOf.Add(from))
			{
				Plugin.Log.LogInfo((object)("Buttons: «" + from + "» came with " + Describe(selectable) + "."));
			}
			Navigation navigation = default(Navigation);
			((Navigation)(ref navigation)).mode = (Mode)0;
			selectable.navigation = navigation;
		}

		private static string Describe(Selectable selectable)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0028: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			ColorBlock colors = selectable.colors;
			Graphic targetGraphic = selectable.targetGraphic;
			string[] obj = new string[17]
			{
				"navigation ", null, null, null, null, null, null, null, null, null,
				null, null, null, null, null, null, null
			};
			Navigation navigation = selectable.navigation;
			obj[1] = ((object)((Navigation)(ref navigation)).mode/*cast due to .constrained prefix*/).ToString();
			obj[2] = ", transition ";
			obj[3] = ((object)selectable.transition/*cast due to .constrained prefix*/).ToString();
			obj[4] = ", tinting ";
			obj[5] = (((Object)(object)targetGraphic != (Object)null) ? ("«" + ((Object)targetGraphic).name + "»") : "nothing");
			obj[6] = ", normal ";
			obj[7] = Hex(((ColorBlock)(ref colors)).normalColor);
			obj[8] = ", highlighted ";
			obj[9] = Hex(((ColorBlock)(ref colors)).highlightedColor);
			obj[10] = ", pressed ";
			obj[11] = Hex(((ColorBlock)(ref colors)).pressedColor);
			obj[12] = ", selected ";
			obj[13] = Hex(((ColorBlock)(ref colors)).selectedColor);
			obj[14] = ", fade ";
			obj[15] = ((ColorBlock)(ref colors)).fadeDuration.ToString("0.##", CultureInfo.InvariantCulture);
			obj[16] = " s";
			return string.Concat(obj);
		}

		private static string Hex(Color color)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return "#" + ColorUtility.ToHtmlStringRGBA(color);
		}

		internal static TMP_Text Caption(Button button, string caption)
		{
			TMP_Text componentInChildren = ((Component)button).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				Plugin.Log.LogWarning((object)("The copied button \"" + caption + "\" has no TextMeshPro caption."));
				return null;
			}
			componentInChildren.text = caption;
			return componentInChildren;
		}
	}
	internal sealed class MenuPage
	{
		private sealed class ScrollPassOn : MonoBehaviour, IScrollHandler, IEventSystemHandler
		{
			internal ScrollRect List;

			public void OnScroll(PointerEventData data)
			{
				if ((Object)(object)List != (Object)null)
				{
					List.OnScroll(data);
				}
			}
		}

		private sealed class Heading
		{
			internal GameObject Root;

			internal TMP_Text Name;

			internal TMP_Text Count;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__66_0;

			internal void <NewTile>b__66_0()
			{
			}
		}

		private static readonly string[] KeptInDetails = new string[2] { "Icon", "Name" };

		private const float ButtonHeight = 44f;

		private const float ButtonGap = 8f;

		private const float WordsScale = 0.8f;

		private const float TitleScale = 0.7f;

		private const float SettingScale = 0.65f;

		private const float LabelScale = 0.6f;

		private const float HeadingHeight = 28f;

		private const float HeadingPad = 5f;

		private const float CountWidth = 44f;

		private const float TileHeight = 36f;

		private const float TileGap = 6f;

		private const float EdgeWidth = 4f;

		private const float FieldHeight = 38f;

		private const float FramePad = 6f;

		private const float FrameGap = 4f;

		private const float ScrollGap = 4f;

		private const string Back = "Назад";

		private const float LiveEvery = 0.25f;

		private static readonly Color HeadingInk = new Color(1f, 0.78f, 0.35f);

		private static readonly Color StripInk = new Color(0.23f, 0.17f, 0.12f, 0.85f);

		private static readonly Color CountInk = new Color(0.7f, 0.6f, 0.47f);

		private readonly MenuTabs.Tab _tab;

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

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

		private readonly List<List<KeyValuePair<TMP_Text, Func<string>>>> _words = new List<List<KeyValuePair<TMP_Text, Func<string>>>>();

		private readonly Dictionary<TMP_Text, GameObject> _blockOf = new Dictionary<TMP_Text, GameObject>();

		private readonly List<List<KeyValuePair<TMP_InputField, MenuTabs.Entry>>> _filled = new List<List<KeyValuePair<TMP_InputField, MenuTabs.Entry>>>();

		private readonly List<List<KeyValuePair<TMP_InputField, MenuTabs.Entry>>> _shared = new List<List<KeyValuePair<TMP_InputField, MenuTabs.Entry>>>();

		private readonly List<string> _filledFor = new List<string>();

		private readonly List<Button> _listTiles = new List<Button>();

		private readonly List<Heading> _listHeadings = new List<Heading>();

		private readonly List<Heading> _listNotes = new List<Heading>();

		private readonly List<GridLayoutGroup> _listBlocks = new List<GridLayoutGroup>();

		private InventoryGui _gui;

		private GameObject _sectionsPane;

		private RectTransform _rowsRoot;

		private float _baseHeight;

		private GameObject _details;

		private Image _icon;

		private TMP_Text _name;

		private GameObject _listView;

		private ScrollRect _listScroll;

		private RectTransform _listContent;

		private TMP_Text _listEmpty;

		private Fields.Look _look;

		private float _listWidth;

		private ScrollRect _gameScroll;

		private float _wideWidth;

		private float _narrowWidth;

		private int _listFor = -1;

		private float _liveAt;

		private static bool _toldTakers;

		private static readonly HashSet<string> MeasuredTabs = new HashSet<string>();

		private static bool _toldRow;

		private MenuPage(MenuTabs.Tab tab)
		{
			_tab = tab;
		}

		internal static MenuPage Build(InventoryGui gui, MenuTabs.Tab tab, Transform listSource, Transform detailsSource)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Expected O, but got Unknown
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Expected O, but got Unknown
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			MenuPage menuPage = new MenuPage(tab)
			{
				_gui = gui
			};
			RectTransform crafting = gui.m_crafting;
			menuPage._sectionsPane = Object.Instantiate<GameObject>(((Component)listSource).gameObject, (Transform)(object)crafting);
			((Object)menuPage._sectionsPane).name = "Astvard " + tab.Caption + " sections";
			RectTransform val = (RectTransform)menuPage._sectionsPane.transform.Find(PathBetween(listSource, (Transform)(object)gui.m_recipeListRoot));
			List<GameObject> list = new List<GameObject>();
			foreach (Transform item in (Transform)val)
			{
				Transform val2 = item;
				list.Add(((Component)val2).gameObject);
			}
			foreach (GameObject item2 in list)
			{
				Object.Destroy((Object)(object)item2);
			}
			menuPage._rowsRoot = val;
			menuPage._details = Object.Instantiate<GameObject>(((Component)detailsSource).gameObject, (Transform)(object)crafting);
			((Object)menuPage._details).name = "Astvard " + tab.Caption + " section";
			foreach (Transform item3 in menuPage._details.transform)
			{
				Transform val3 = item3;
				((Component)val3).gameObject.SetActive(Array.IndexOf(KeptInDetails, ((Object)val3).name) >= 0);
			}
			Transform obj = menuPage._details.transform.Find("Icon");
			menuPage._icon = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null);
			Transform obj2 = menuPage._details.transform.Find("Name");
			menuPage._name = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null);
			RectTransform val4 = ActionsArea((RectTransform)menuPage._details.transform);
			Fields.Look look = Fields.LookOf(gui, detailsSource);
			menuPage._look = look;
			menuPage._gameScroll = menuPage._sectionsPane.GetComponentInChildren<ScrollRect>(true);
			menuPage._wideWidth = ButtonWidth(gui);
			Rect rect = val4.rect;
			if (((Rect)(ref rect)).width >= 1f)
			{
				float wideWidth = menuPage._wideWidth;
				rect = val4.rect;
				menuPage._wideWidth = Mathf.Min(wideWidth, ((Rect)(ref rect)).width);
			}
			menuPage._narrowWidth = menuPage._wideWidth - menuPage.BarRoom();
			bool flag = false;
			for (int i = 0; i < tab.Sections.Count; i++)
			{
				MenuTabs.Section section = tab.Sections[i];
				bool flag2 = section.Rows != null;
				flag = flag || flag2;
				menuPage._rows.Add(menuPage.Row(gui, val, section, i));
				List<KeyValuePair<TMP_Text, Func<string>>> list2 = new List<KeyValuePair<TMP_Text, Func<string>>>();
				List<KeyValuePair<TMP_InputField, MenuTabs.Entry>> list3 = new List<KeyValuePair<TMP_InputField, MenuTabs.Entry>>();
				List<KeyValuePair<TMP_InputField, MenuTabs.Entry>> list4 = new List<KeyValuePair<TMP_InputField, MenuTabs.Entry>>();
				menuPage._groups.Add(menuPage.Group(val4, section, list2, list3, list4, flag2 ? new UnityAction(menuPage.CloseRow) : ((UnityAction)null)));
				menuPage._words.Add(list2);
				menuPage._filled.Add(list3);
				menuPage._shared.Add(list4);
				menuPage._filledFor.Add(null);
			}
			if (flag)
			{
				menuPage.ListView(val4);
			}
			rect = val.rect;
			menuPage._baseHeight = ((Rect)(ref rect)).height;
			menuPage.Size(tab.Sections.Count);
			menuPage.TellListOnce();
			menuPage.Show(shown: false);
			return menuPage;
		}

		internal void Show(bool shown)
		{
			_sectionsPane.SetActive(shown);
			_details.SetActive(shown);
			if (shown)
			{
				_sectionsPane.transform.SetAsLastSibling();
				_details.transform.SetAsLastSibling();
				Choose(_tab.Chosen);
			}
		}

		internal void Refresh()
		{
			if (_details.activeSelf)
			{
				Choose(_tab.Chosen);
			}
		}

		internal void Tick()
		{
			if (_details.activeSelf && !(Time.unscaledTime < _liveAt))
			{
				int chosen = _tab.Chosen;
				if (chosen >= 0 && chosen < _tab.Sections.Count && _tab.Sections[chosen].IsLive)
				{
					_liveAt = Time.unscaledTime + 0.25f;
					Say(chosen);
				}
			}
		}

		private void Choose(int index)
		{
			List<MenuTabs.Section> sections = _tab.Sections;
			if (sections.Count == 0)
			{
				return;
			}
			index = Mathf.Clamp(index, 0, sections.Count - 1);
			_tab.Chosen = index;
			MenuTabs.Section section = sections[index];
			IReadOnlyList<MenuTabs.Row> readOnlyList = null;
			MenuTabs.Row row = null;
			if (section.Rows != null)
			{
				readOnlyList = RowsOf(section);
				row = RowFor(readOnlyList, section.ChosenKey);
				section.ChosenKey = row?.Key;
			}
			else
			{
				_listFor = -1;
			}
			string text = row?.Key;
			if (text != _filledFor[index])
			{
				_filledFor[index] = text;
				if (text != null)
				{
					FillFields(index);
				}
			}
			else if (section.RefillAsked && text != null)
			{
				FillFields(index);
			}
			section.RefillAsked = false;
			ShowShared(index);
			bool flag = readOnlyList != null && row == null;
			for (int i = 0; i < _rows.Count; i++)
			{
				Transform obj = _rows[i].transform.Find("selected");
				if (obj != null)
				{
					((Component)obj).gameObject.SetActive(i == index);
				}
				_groups[i].SetActive(i == index && !flag);
				Transform obj2 = _rows[i].transform.Find("icon");
				Paint((obj2 != null) ? ((Component)obj2).GetComponent<Image>() : null, sections[i]);
			}
			if ((Object)(object)_listView != (Object)null)
			{
				_listView.SetActive(flag);
			}
			if (flag)
			{
				FillList(index, readOnlyList);
			}
			if ((Object)(object)_name != (Object)null)
			{
				_name.text = ((row != null) ? row.Title : section.Title);
			}
			Paint(_icon, section);
			Say(index);
		}

		private void OpenSection(int index)
		{
			MenuTabs.Section section = _tab.Sections[index];
			if (section.Rows != null)
			{
				section.ChosenKey = null;
			}
			if (section.Opened != null)
			{
				try
				{
					section.Opened();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("«" + section.Title + "» stumbled as it opened: " + ex));
				}
			}
			Choose(index);
		}

		private void OpenRow(string key)
		{
			_tab.Sections[_tab.Chosen].ChosenKey = key;
			Choose(_tab.Chosen);
		}

		private void CloseRow()
		{
			_tab.Sections[_tab.Chosen].ChosenKey = null;
			Choose(_tab.Chosen);
		}

		private static IReadOnlyList<MenuTabs.Row> RowsOf(MenuTabs.Section section)
		{
			try
			{
				return section.Rows() ?? new List<MenuTabs.Row>();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("The list «" + section.Title + "» could not be made: " + ex));
				return new List<MenuTabs.Row>();
			}
		}

		private static MenuTabs.Row RowFor(IReadOnlyList<MenuTabs.Row> rows, string key)
		{
			if (key == null)
			{
				return null;
			}
			foreach (MenuTabs.Row row in rows)
			{
				if (!row.IsHeading && row.Key == key)
				{
					return row;
				}
			}
			return null;
		}

		private void FillList(int index, IReadOnlyList<MenuTabs.Row> rows)
		{
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			bool flag = _listFor != index;
			_listFor = index;
			int num = 0;
			((Component)_listEmpty).gameObject.SetActive(rows.Count == 0);
			if (rows.Count == 0)
			{
				_listEmpty.text = _tab.Sections[index].Empty?.Invoke() ?? "Пусто";
				_listEmpty.transform.SetSiblingIndex(num++);
			}
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			GridLayoutGroup val = null;
			foreach (MenuTabs.Row row in rows)
			{
				if (row.IsNote)
				{
					while (_listNotes.Count <= num4)
					{
						_listNotes.Add(NewNote());
					}
					Heading heading = _listNotes[num4++];
					heading.Root.SetActive(true);
					heading.Name.text = row.Title;
					heading.Count.text = row.Words;
					heading.Root.transform.SetSiblingIndex(num++);
					val = null;
					continue;
				}
				if (row.IsHeading)
				{
					while (_listHeadings.Count <= num3)
					{
						_listHeadings.Add(NewHeading());
					}
					Heading heading2 = _listHeadings[num3++];
					heading2.Root.SetActive(true);
					heading2.Name.text = row.Title;
					heading2.Count.text = row.Aside ?? "";
					Widgets.Size(height: Mathf.Max(28f, Mathf.Ceil(heading2.Name.GetPreferredValues(row.Title, HeadingNameWidth(), 0f).y) + 10f), go: heading2.Root, width: _listWidth);
					heading2.Root.transform.SetSiblingIndex(num++);
					val = null;
					continue;
				}
				if ((Object)(object)val == (Object)null)
				{
					val = Pooled<GridLayoutGroup>(_listBlocks, num5++, (Func<GridLayoutGroup>)NewBlock);
					((Component)val).transform.SetSiblingIndex(num++);
					num6 = 0;
				}
				Button val2 = Pooled<Button>(_listTiles, num2++, (Func<Button>)NewTile);
				Wire(val2, row);
				((Component)val2).transform.SetParent(((Component)val).transform, false);
				((Component)val2).transform.SetSiblingIndex(num6++);
			}
			for (int i = num3; i < _listHeadings.Count; i++)
			{
				_listHeadings[i].Root.SetActive(false);
			}
			for (int j = num4; j < _listNotes.Count; j++)
			{
				_listNotes[j].Root.SetActive(false);
			}
			for (int k = num5; k < _listBlocks.Count; k++)
			{
				((Component)_listBlocks[k]).gameObject.SetActive(false);
			}
			for (int l = num2; l < _listTiles.Count; l++)
			{
				((Component)_listTiles[l]).gameObject.SetActive(false);
			}
			if (flag)
			{
				_listScroll.StopMovement();
				_listContent.anchoredPosition = Vector2.zero;
			}
		}

		private static T Pooled<T>(List<T> pool, int at, Func<T> make) where T : Component
		{
			while (pool.Count <= at)
			{
				pool.Add(make());
			}
			T val = pool[at];
			((Component)val).gameObject.SetActive(true);
			return val;
		}

		private void Wire(Button button, MenuTabs.Row row)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			string title = row.Title;
			string key = row.Key;
			button.onClick = new ButtonClickedEvent();
			((UnityEvent)button.onClick).AddListener((UnityAction)delegate
			{
				Widgets.Announce(title);
			});
			if (row.Act != null)
			{
				((UnityEvent)button.onClick).AddListener(row.Act);
			}
			else
			{
				((UnityEvent)button.onClick).AddListener((UnityAction)delegate
				{
					OpenRow(key);
				});
			}
			GameLook.Caption(button, title);
		}

		private Button NewTile()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			Button craftButton = _gui.m_craftButton;
			RectTransform listContent = _listContent;
			object obj = <>c.<>9__66_0;
			if (obj == null)
			{
				UnityAction val = delegate
				{
				};
				<>c.<>9__66_0 = val;
				obj = (object)val;
			}
			Button obj2 = GameLook.Copy(craftButton, (Transform)(object)listContent, "", (UnityAction)obj);
			((Object)obj2).name = "Astvard list tile " + _listTiles.Count;
			TMP_Text componentInChildren = ((Component)obj2).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.enableAutoSizing = true;
				componentInChildren.fontSizeMin = 12f;
				componentInChildren.fontSizeMax = _look.Size * 0.65f;
				componentInChildren.textWrappingMode = (TextWrappingModes)1;
				componentInChildren.overflowMode = (TextOverflowModes)1;
			}
			PassScrollOn(((Component)obj2).gameObject, _listScroll);
			return obj2;
		}

		private GridLayoutGroup NewBlock()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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)
			GameObject val = new GameObject("Astvard list block " + _listBlocks.Count, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent((Transform)(object)_listContent, false);
			GridLayoutGroup obj = val.AddComponent<GridLayoutGroup>();
			obj.cellSize = new Vector2(Mathf.Floor((_listWidth - 6f) / 2f), 36f);
			obj.spacing = new Vector2(6f, 6f);
			obj.constraint = (Constraint)1;
			obj.constraintCount = 2;
			obj.startCorner = (Corner)0;
			obj.startAxis = (Axis)0;
			((LayoutGroup)obj).childAlignment = (TextAnchor)0;
			return obj;
		}

		private static void PassScrollOn(GameObject copy, ScrollRect column)
		{
			//IL_001d: 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)
			IScrollHandler[] componentsInChildren = copy.GetComponentsInChildren<IScrollHandler>(true);
			copy.AddComponent<ScrollPassOn>().List = column;
			IScrollHandler[] array = componentsInChildren;
			for (int i = 0; i < array.Length; i++)
			{
				GameObject gameObject = ((Component)array[i]).gameObject;
				if ((Object)(object)gameObject.GetComponent<ScrollPassOn>() == (Object)null)
				{
					gameObject.AddComponent<ScrollPassOn>().List = column;
				}
			}
			if (!_toldTakers)
			{
				_toldTakers = true;
				List<string> list = new List<string>();
				array = componentsInChildren;
				foreach (IScrollHandler val in array)
				{
					list.Add(((object)val).GetType().Name + " on " + ((Object)(Component)val).name);
				}
				Plugin.Log.LogInfo((object)((list.Count == 0) ? "List buttons: nothing in the copied craft button takes the wheel." : ("List buttons: the wheel was taken by " + string.Join(", ", list) + " — handed on to the list.")));
			}
		}

		private Heading NewNote()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			Transform val = Frame((Transform)(object)_listContent, "Astvard list note " + _listNotes.Count);
			Widgets.Width(((Component)val).gameObject, _listWidth);
			TMP_Text name = NewText(val, "Astvard note name", _look, _look.Size * 0.7f, HeadingInk, (TextAlignmentOptions)513, (TextWrappingModes)0, (TextOverflowModes)1);
			TMP_Text count = NewText(val, "Astvard note words", _look, _look.Size * 0.65f, _look.Ink, (TextAlignmentOptions)257, (TextWrappingModes)1, (TextOverflowModes)0);
			return new Heading
			{
				Root = ((Component)val).gameObject,
				Name = name,
				Count = count
			};
		}

		private Heading NewHeading()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: 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_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Astvard list heading " + _listHeadings.Count, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent((Transform)(object)_listContent, false);
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).color = StripInk;
			((Graphic)obj).raycastTarget = false;
			Widgets.Size(val, _listWidth, 28f);
			Edge(val.transform);
			TMP_Text val2 = NewText(val.transform, "Astvard count", _look, _look.Size * 0.6f, CountInk, (TextAlignmentOptions)516, (TextWrappingModes)0, (TextOverflowModes)1);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.anchorMin = new Vector2(1f, 0f);
			val3.anchorMax = Vector2.one;
			val3.pivot = new Vector2(1f, 0.5f);
			val3.sizeDelta = new Vector2(44f, 0f);
			val3.anchoredPosition = new Vector2(-8f, 0f);
			TMP_Text val4 = NewText(val.transform, "Astvard name", _look, _look.Size * 0.65f, HeadingInk, (TextAlignmentOptions)513, (TextWrappingModes)1, (TextOverflowModes)1);
			RectTransform val5 = (RectTransform)val4.transform;
			val5.anchorMin = Vector2.zero;
			val5.anchorMax = Vector2.one;
			val5.offsetMin = new Vector2(12f, 0f);
			val5.offsetMax = new Vector2(-56f, 0f);
			return new Heading
			{
				Root = val,
				Name = val4,
				Count = val2
			};
		}

		private float HeadingNameWidth()
		{
			return _listWidth - 12f - 56f;
		}

		private void ListView(RectTransform actions)
		{
			_listScroll = ScrollColumn(actions, "list", stretch: false, out _listContent);
			_listView = ((Component)_listScroll).gameObject;
			_listWidth = _narrowWidth;
			_listEmpty = Words((Transform)(object)_listContent, _look, _listWidth);
			_listView.SetActive(false);
		}

		private ScrollRect ScrollColumn(RectTransform parent, string name, bool stretch, out RectTransform content)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_0097: Expected O, but got Unknown
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Expected O, but got Unknown
			GameObject val = new GameObject("Astvard " + name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent((Transform)(object)parent, false);
			Stretch((RectTransform)val.transform);
			((Graphic)val.AddComponent<Image>()).color = Color.clear;
			GameObject val2 = new GameObject("Astvard " + name + " viewport", new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent(val.transform, false);
			RectTransform val3 = (RectTransform)val2.transform;
			Stretch(val3);
			val2.AddComponent<RectMask2D>();
			GameObject val4 = new GameObject("Astvard " + name + " rows", new Type[1] { typeof(RectTransform) });
			val4.transform.SetParent((Transform)(object)val3, false);
			content = (RectTransform)val4.transform;
			content.anchorMin = new Vector2(0f, 1f);
			content.anchorMax = Vector2.one;
			content.pivot = new Vector2(0.5f, 1f);
			content.offsetMin = Vector2.zero;
			content.offsetMax = Vector2.zero;
			VerticalLayoutGroup val5 = val4.AddComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)val5).spacing = 8f;
			((LayoutGroup)val5).childAlignment = (TextAnchor)1;
			((HorizontalOrVerticalLayoutGroup)val5).childForceExpandWidth = stretch;
			((HorizontalOrVerticalLayoutGroup)val5).childForceExpandHeight = false;
			if (stretch)
			{
				Rect rect = parent.rect;
				if (((Rect)(ref rect)).width >= 1f)
				{
					rect = parent.rect;
					int num = Mathf.Max(0, Mathf.FloorToInt((((Rect)(ref rect)).width - _wideWidth) / 2f));
					((LayoutGroup)val5).padding = new RectOffset(num, num, 0, 0);
				}
			}
			val4.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			Scrollbar val6 = ScrollbarCopy(val.transform);
			ScrollRect val7 = val.AddComponent<ScrollRect>();
			val7.content = content;
			val7.viewport = val3;
			val7.horizontal = false;
			val7.vertical = true;
			if ((Object)(object)_gameScroll != (Object)null)
			{
				val7.movementType = _gameScroll.movementType;
				val7.elasticity = _gameScroll.elasticity;
				val7.inertia = _gameScroll.inertia;
				val7.decelerationRate = _gameScroll.decelerationRate;
				val7.scrollSensitivity = _gameScroll.scrollSensitivity;
			}
			else
			{
				val7.movementType = (MovementType)2;
				val7.scrollSensitivity = 44f;
			}
			if ((Object)(object)val6 != (Object)null)
			{
				val7.verticalScrollbar = val6;
				val7.verticalScrollbarVisibility = (ScrollbarVisibility)2;
				val7.verticalScrollbarSpacing = 4f;
			}
			return val7;
		}

		private float BarRoom()
		{
			//IL_0021: 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)
			Scrollbar recipeListScroll = _gui.m_recipeListScroll;
			if ((Object)(object)recipeListScroll == (Object)null)
			{
				return 0f;
			}
			Rect rect = ((RectTransform)((Component)recipeListScroll).transform).rect;
			float width = ((Rect)(ref rect)).width;
			return ((width < 1f) ? 10f : width) + 4f;
		}

		private Scrollbar ScrollbarCopy(Transform parent)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			Scrollbar recipeListScroll = _gui.m_recipeListScroll;
			if ((Object)(object)recipeListScroll == (Object)null)
			{
				return null;
			}
			float num = BarRoom() - 4f;
			Scrollbar obj = Object.Instantiate<Scrollbar>(recipeListScroll, parent);
			((Object)obj).name = "Astvard scrollbar";
			obj.onValueChanged = new ScrollEvent();
			((Component)obj).gameObject.SetActive(true);
			RectTransform val = (RectTransform)((Component)obj).transform;
			val.anchorMin = new Vector2(1f, 0f);
			val.anchorMax = Vector2.one;
			val.pivot = new Vector2(1f, 0.5f);
			val.anchoredPosition = Vector2.zero;
			val.sizeDelta = new Vector2(num, 0f);
			return obj;
		}

		private static void Stretch(RectTransform rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.offsetMin = Vector2.zero;
			rect.offsetMax = Vector2.zero;
		}

		private static void Edge(Transform parent)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Astvard edge", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			val.AddComponent<LayoutElement>().ignoreLayout = true;
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = new Vector2(0f, 1f);
			val2.pivot = new Vector2(0f, 0.5f);
			val2.sizeDelta = new Vector2(4f, 0f);
			val2.anchoredPosition = Vector2.zero;
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).color = HeadingInk;
			((Graphic)obj).raycastTarget = false;
		}

		private void Size(int count)
		{
			_rowsRoot.SetSizeWithCurrentAnchors((Axis)1, Mathf.Max(_baseHeight, (float)count * _gui.m_recipeListSpace));
		}

		private void Say(int index)
		{
			foreach (KeyValuePair<TMP_Text, Func<string>> item in _words[index])
			{
				string text;
				try
				{
					text = item.Value();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("A line of «" + _tab.Sections[index].Title + "» could not be said: " + ex));
					text = "";
				}
				item.Key.text = text ?? "";
				if (_blockOf.TryGetValue(item.Key, out var value))
				{
					value.SetActive(item.Key.text.Length > 0);
				}
			}
		}

		private GameObject Row(InventoryGui gui, RectTransform rows, MenuTabs.Section section, int index)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected O, but got Unknown
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Expected O, but got Unknown
			GameObject obj = Object.Instantiate<GameObject>(gui.m_recipeElementPrefab, (Transform)(object)rows);
			((Object)obj).name = "Astvard " + section.Title;
			obj.SetActive(true);
			((RectTransform)obj.transform).anchoredPosition = new Vector2(0f, (float)(-index) * gui.m_recipeListSpace);
			Transform obj2 = obj.transform.Find("name");
			TMP_Text val = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.text = section.Title;
				((Graphic)val).color = Color.white;
			}
			Transform obj3 = obj.transform.Find("Durability");
			if (obj3 != null)
			{
				((Component)obj3).gameObject.SetActive(false);
			}
			Transform obj4 = obj.transform.Find("QualityLevel");
			if (obj4 != null)
			{
				((Component)obj4).gameObject.SetActive(false);
			}
			Transform obj5 = obj.transform.Find("selected");
			if (obj5 != null)
			{
				((Component)obj5).gameObject.SetActive(false);
			}
			TellRowOnce(obj);
			Button component = obj.GetComponent<Button>();
			if ((Object)(object)component != (Object)null)
			{
				GameLook.LeaveUnselected((Selectable)(object)component, "recipe row");
				component.onClick = new ButtonClickedEvent();
				((UnityEvent)component.onClick).AddListener((UnityAction)delegate
				{
					Widgets.Announce(section.Title);
				});
				((UnityEvent)component.onClick).AddListener((UnityAction)delegate
				{
					OpenSection(index);
				});
			}
			return obj;
		}

		private void TellListOnce()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if (_rows.Count != 0 && MeasuredTabs.Add(_tab.Caption))
			{
				RectTransform val = (RectTransform)((!((Object)(object)_gameScroll != (Object)null)) ? null : (((Object)(object)_gameScroll.viewport != (Object)null) ? ((object)_gameScroll.viewport) : ((object)(RectTransform)((Component)_gameScroll).transform)));
				Rect rect = ((RectTransform)_rows[0].transform).rect;
				float height = ((Rect)(ref rect)).height;
				float num = (float)(_rows.Count - 1) * _gui.m_recipeListSpace + height;
				float num2;
				if (!((Object)(object)val != (Object)null))
				{
					num2 = 0f;
				}
				else
				{
					rect = val.rect;
					num2 = ((Rect)(ref rect)).height;
				}
				float num3 = num2;
				Plugin.Log.LogInfo((object)("Rows of «" + _tab.Caption + "»: " + _rows.Count + " a step of " + _gui.m_recipeListSpace.ToString("0.#", CultureInfo.InvariantCulture) + " px, a row " + height.ToString("0.#", CultureInfo.InvariantCulture) + " px tall, reaching " + num.ToString("0.#", CultureInfo.InvariantCulture) + " px down a window " + num3.ToString("0.#", CultureInfo.InvariantCulture) + " px tall" + (((Object)(object)val == (Object)null) ? " (no window found)" : ((num <= num3) ? " — all in view." : " — the last one runs past the edge."))));
			}
		}

		private static void TellRowOnce(GameObject row)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			if (_toldRow)
			{
				return;
			}
			_toldRow = true;
			List<string> list = new List<string>();
			foreach (Transform item in row.transform)
			{
				Transform val = item;
				Graphic component = ((Component)val).GetComponent<Graphic>();
				list.Add(((Object)val).name + (((Component)val).gameObject.activeSelf ? "" : " (off)") + (((Object)(object)component == (Object)null) ? "" : (component.raycastTarget ? " [catches]" : " [drawn]")));
			}
			Graphic component2 = row.GetComponent<Graphic>();
			Plugin.Log.LogInfo((object)("Rows: the recipe row is " + (((Object)(object)component2 == (Object)null) ? "no graphic" : (component2.raycastTarget ? "a graphic that catches" : "a graphic")) + ", its children drawn in order: " + string.Join(", ", list) + "."));
		}

		private static float ButtonWidth(InventoryGui gui)
		{
			//IL_000b: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			Rect rect = ((RectTransform)((Component)gui.m_craftButton).transform).rect;
			float width = ((Rect)(ref rect)).width;
			if (!(width < 1f))
			{
				return width;
			}
			return 300f;
		}

		private GameObject Group(RectTransform actions, MenuTabs.Section section, List<KeyValuePair<TMP_Text, Func<string>>> words, List<KeyValuePair<TMP_InputField, MenuTabs.Entry>> filled, List<KeyValuePair<TMP_InputField, MenuTabs.Entry>> shared, UnityAction back)
		{
			RectTransform content;
			ScrollRect val = ScrollColumn(actions, section.Title + " actions", stretch: true, out content);
			if (back != null)
			{
				AddButton((Transform)(object)content, "Назад", back, val);
			}
			List<MenuTabs.Entry> list = new List<MenuTabs.Entry>();
			List<MenuTabs.Entry> list2 = new List<MenuTabs.Entry>();
			foreach (MenuTabs.Entry entry in section.Entries)
			{
				if (entry.Kind == MenuTabs.Entry.Kinds.Mark)
				{
					continue;
				}
				if (entry.Kind == MenuTabs.Entry.Kinds.Tile)
				{
					FieldBlock((Transform)(object)content, list, val, filled, shared);
					list2.Add(entry);
					continue;
				}
				if (entry.IsField && !entry.IsHeaded)
				{
					TileRows((Transform)(object)content, list2, val, words);
					list.Add(entry);
					continue;
				}
				FieldBlock((Transform)(object)content, list, val, filled, shared);
				TileRows((Transform)(object)content, list2, val, words);
				if (entry.Kind == MenuTabs.Entry.Kinds.Heading)
				{
					ColumnHeading((Transform)(object)content, entry.Caption);
				}
				else if (entry.Kind == MenuTabs.Entry.Kinds.Strip)
				{
					StripBlock((Transform)(object)content, entry, words);
				}
				else if (entry.IsHeaded)
				{
					if (entry.IsField)
					{
						ValueBlock((Transform)(object)content, entry, val, words);
					}
					else if (entry.OnClick == null)
					{
						WordsBlock((Transform)(object)content, entry, words);
					}
					else
					{
						SettingBlock((Transform)(object)content, entry, val, words);
					}
				}
				else if (entry.Says != null)
				{
					words.Add(new KeyValuePair<TMP_Text, Func<string>>(Words((Transform)(object)content, _look, 0f), entry.Says));
				}
				else if (entry.CaptionOf != null)
				{
					TMP_Text componentInChildren = ((Component)AddButton((Transform)(object)content, entry.CaptionOf(), entry.OnClick, val)).GetComponentInChildren<TMP_Text>(true);
					if ((Object)(object)componentInChildren != (Object)null)
					{
						words.Add(new KeyValuePair<TMP_Text, Func<string>>(componentInChildren, entry.CaptionOf));
					}
				}
				else
				{
					AddButton((Transform)(object)content, entry.Caption, entry.OnClick, val);
				}
			}
			FieldBlock((Transform)(object)content, list, val, filled, shared);
			TileRows((Transform)(object)content, list2, val, words);
			((Component)val).gameObject.SetActive(false);
			return ((Component)val).gameObject;
		}

		private void ColumnHeading(Transform column, string title)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_0052: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: 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_00a5: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Astvard heading " + title, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(column, false);
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).color = StripInk;
			((Graphic)obj).raycastTarget = false;
			Widgets.Height(val, 28f);
			Edge(val.transform);
			TMP_Text obj2 = NewText(val.transform, "Astvard name", _look, _look.Size * 0.65f, HeadingInk, (TextAlignmentOptions)513, (TextWrappingModes)0, (TextOverflowModes)1);
			RectTransform val2 = (RectTransform)obj2.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = new Vector2(12f, 0f);
			val2.offsetMax = new Vector2(-8f, 0f);
			obj2.text = title;
		}

		private void StripBlock(Transform column, MenuTabs.Entry entry, List<KeyValuePair<TMP_Text, Func<string>>> words)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Transform val = Frame(column, "Astvard strip");
			TMP_Text key = NewText(val, "Astvard strip words", _look, _look.Size * 0.65f, HeadingInk, (TextAlignmentOptions)257, (TextWrappingModes)1, (TextOverflowModes)0);
			words.Add(new KeyValuePair<TMP_Text, Func<string>>(key, entry.Says));
			_blockOf[key] = ((Component)val).gameObject;
		}

		private void TileRows(Transform column, List<MenuTabs.Entry> tiles, ScrollRect scroll, List<KeyValuePair<TMP_Text, Func<string>>> words)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			for (int i = 0; i < tiles.Count; i += 2)
			{
				GameObject val = new GameObject("Astvard tiles", new Type[1] { typeof(RectTransform) });
				val.transform.SetParent(column, false);
				HorizontalLayoutGroup obj = val.AddComponent<HorizontalLayoutGroup>();
				((HorizontalOrVerticalLayoutGroup)obj).spacing = 6f;
				((LayoutGroup)obj).childAlignment = (TextAnchor)3;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
				Widgets.Height(val, 36f);
				for (int j = i; j < i + 2; j++)
				{
					if (j < tiles.Count)
					{
						ColumnTile(val.transform, tiles[j], scroll, words);
					}
					else
					{
						Spacer(val.transform);
					}
				}
			}
			tiles.Clear();
		}

		private void ColumnTile(Transform row, MenuTabs.Entry entry, ScrollRect scroll, List<KeyValuePair<TMP_Text, Func<string>>> words)
		{
			string caption = ((entry.CaptionOf != null) ? entry.CaptionOf() : entry.Caption);
			Button obj = GameLook.Copy(_gui.m_craftButton, row, caption, entry.OnClick);
			Widgets.Height(((Component)obj).gameObject, 36f);
			LayoutElement component = ((Component)obj).GetComponent<LayoutElement>();
			component.preferredWidth = 0f;
			component.flexibleWidth = 1f;
			TMP_Text componentInChildren = ((Component)obj).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.enableAutoSizing = true;
				componentInChildren.fontSizeMin = 12f;
				componentInChildren.fontSizeMax = _look.Size * 0.65f;
				componentInChildren.textWrappingMode = (TextWrappingModes)1;
				componentInChildren.overflowMode = (TextOverflowModes)1;
				if (entry.CaptionOf != null)
				{
					words.Add(new KeyValuePair<TMP_Text, Func<string>>(componentInChildren, entry.CaptionOf));
				}
			}
			PassScrollOn(((Component)obj).gameObject, scroll);
		}

		private static void Spacer(Transform row)
		{
			//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)
			GameObject val = new GameObject("Astvard spacer", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(row, false);
			LayoutElement obj = val.AddComponent<LayoutElement>();
			obj.preferredWidth = 0f;
			obj.flexibleWidth = 1f;
			obj.preferredHeight = 36f;
		}

		private Button AddButton(Transform column, string caption, UnityAction onClick, ScrollRect scroll)
		{
			Button obj = GameLook.Copy(_gui.m_craftButton, column, caption, onClick);
			Widgets.Height(((Component)obj).gameObject, 44f);
			PassScrollOn(((Component)obj).gameObject, scroll);
			return obj;
		}

		private void FieldBlock(Transform column, List<MenuTabs.Entry> run, ScrollRect scroll, List<KeyValuePair<TMP_InputField, MenuTabs.Entry>> filled, List<KeyValuePair<TMP_InputField, MenuTabs.Entry>> shared)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			if (run.Count == 0)
			{
				return;
			}
			Transform val = Frame(column, "Astvard fields");
			List<MenuTabs.Entry> list = new List<MenuTabs.Entry>();
			foreach (MenuTabs.Entry item in run)
			{
				if (item.Label != null)
				{
					Label(val, item.Label);
				}
				TMP_InputField val2 = Fields.Make(val, item, _look);
				Widgets.Height(((Component)val2).gameObject, 38f);
				if (item.Fill != null)
				{
					filled.Add(new KeyValuePair<TMP_InputField, MenuTabs.Entry>(val2, item));
				}
				if (item.Same != null)
				{
					shared.Add(new KeyValuePair<TMP_InputField, MenuTabs.Entry>(val2, item));
				}
				if (!item.IsBare)
				{
					list.Add(item);
				}
			}
			if (list.Count > 0)
			{
				GameObject val3 = new GameObject("Astvard field buttons", new Type[1] { typeof(RectTransform) });
				val3.transform.SetParent(val, false);
				HorizontalLayoutGroup obj = val3.AddComponent<HorizontalLayoutGroup>();
				((HorizontalOrVerticalLayoutGroup)obj).spacing = 4f;
				((LayoutGroup)obj).childAlignment = (TextAnchor)4;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
				Widgets.Height(val3, 38f);
				foreach (MenuTabs.Entry entry in list)
				{
					Button obj2 = GameLook.Copy(_gui.m_craftButton, val3.transform, entry.Caption, (UnityAction)delegate
					{
						entry.Apply(entry.Text);
					});
					Widgets.Height(((Component)obj2).gameObject, 38f);
					LayoutElement component = ((Component)obj2).GetComponent<LayoutElement>();
					component.preferredWidth = 0f;
					component.flexibleWidth = 1f;
					Fit(obj2);
					PassScrollOn(((Component)obj2).gameObject, scroll);
				}
			}
			run.Clear();
		}

		private static void Fit(Button button)
		{
			TMP_Text componentInChildren = ((Component)button).GetComponentInChildren<TMP_Text>(true);
			if (!((Object)(object)componentInChildren == (Object)null))
			{
				float fontSize = componentInChildren.fontSize;
				componentInChildren.enableAutoSizing = true;
				componentInChildren.fontSizeMax = fontSize;
				componentInChildren.fontSizeMin = 12f;
				componentInChildren.textWrappingMode = (TextWrappingModes)0;
				componentInChildren.overflowMode = (TextOverflowModes)1;
			}
		}

		private void SettingBlock(Transform column, MenuTabs.Entry entry, ScrollRect scroll, List<KeyValuePair<TMP_Text, Func<string>>> words)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			Transform parent = Frame(column, "Astvard setting");
			NewText(parent, "Astvard setting name", _look, _look.Size * 0.7f, HeadingInk, (TextAlignmentOptions)513, (TextWrappingModes)0, (TextOverflowModes)1).text = entry.Heading;
			TMP_Text key = NewText(parent, "Astvard setting words", _look, _look.Size * 0.65f, _look.Ink, (TextAlignmentOptions)257, (TextWrappingModes)1, (TextOverflowModes)0);
			words.Add(new KeyValuePair<TMP_Text, Func<string>>(key, entry.Says));
			Button obj = GameLook.Copy(_gui.m_craftButton, parent, entry.Heading, entry.OnClick);
			Widgets.Height(((Component)obj).gameObject, 38f);
			Fit(obj);
			PassScrollOn(((Component)obj).gameObject, scroll);
			TMP_Text val = GameLook.Caption(obj, entry.CaptionOf());
			if ((Object)(object)val != (Object)null)
			{
				words.Add(new KeyValuePair<TMP_Text, Func<string>>(val, entry.CaptionOf));
			}
		}

		private void WordsBlock(Transform column, MenuTabs.Entry entry, List<KeyValuePair<TMP_Text, Func<string>>> words)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			Transform val = Frame(column, "Astvard words block");
			NewText(val, "Astvard block name", _look, _look.Size * 0.7f, HeadingInk, (TextAlignmentOptions)513, (TextWrappingModes)0, (TextOverflowModes)1).text = entry.Heading;
			TMP_Text key = NewText(val, "Astvard block words", _look, _look.Size * 0.65f, _look.Ink, (TextAlignmentOptions)257, (TextWrappingModes)1, (TextOverflowModes)0);
			words.Add(new KeyValuePair<TMP_Text, Func<string>>(key, entry.Says));
			_blockOf[key] = ((Component)val).gameObject;
		}

		private void ValueBlock(Transform column, MenuTabs.Entry entry, ScrollRect scroll, List<KeyValuePair<TMP_Text, Func<string>>> words)
		{
			//IL_0036: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			Transform parent = Frame(column, "Astvard value");
			NewText(parent, "Astvard value name", _look, _look.Size * 0.7f, HeadingInk, (TextAlignmentOptions)513, (TextWrappingModes)0, (TextOverflowModes)1).text = entry.Heading;
			TMP_Text key = NewText(parent, "Astvard value words", _look, _look.Size * 0.65f, _look.Ink, (TextAlignmentOptions)257, (TextWrappingModes)1, (TextOverflowModes)0);
			words.Add(new KeyValuePair<TMP_Text, Func<string>>(key, entry.Says));
			Widgets.Height(((Component)Fields.Make(parent, entry, _look)).gameObject, 38f);
			Button obj = GameLook.Copy(_gui.m_craftButton, parent, entry.Heading, (UnityAction)delegate
			{
				entry.Apply(entry.Text);
			});
			GameLook.Caption(obj, entry.Caption);
			Widgets.Height(((Component)obj).gameObject, 38f);
			Fit(obj);
			PassScrollOn(((Component)obj).gameObject, scroll);
		}

		private static Transform Frame(Transform column, string name)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(column, false);
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).color = StripInk;
			((Graphic)obj).raycastTarget = false;
			VerticalLayoutGroup obj2 = val.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(10, 6, 6, 6);
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 4f;
			((LayoutGroup)obj2).childAlignment = (TextAnchor)0;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			Edge(val.transform);
			return val.transform;
		}

		private void Label(Transform block, string text)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			NewText(block, "Astvard label", _look, _look.Size * 0.6f, HeadingInk, (TextAlignmentOptions)1025, (TextWrappingModes)0, (TextOverflowModes)1).text = text;
		}

		private void FillFields(int index)
		{
			foreach (KeyValuePair<TMP_InputField, MenuTabs.Entry> item in _filled[index])
			{
				string textWithoutNotify;
				try
				{
					textWithoutNotify = item.Value.Fill() ?? "";
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("A field of «" + _tab.Sections[index].Title + "» could not be filled: " + ex));
					textWithoutNotify = "";
				}
				item.Key.SetTextWithoutNotify(textWithoutNotify);
				item.Value.Text = item.Key.text;
			}
		}

		private void ShowShared(int index)
		{
			foreach (KeyValuePair<TMP_InputField, MenuTabs.Entry> item in _shared[index])
			{
				if ((Object)(object)item.Key != (Object)null && !item.Key.isFocused && item.Key.text != item.Value.Text)
				{
					item.Key.SetTextWithoutNotify(item.Value.Text);
				}
			}
		}

		private static TMP_Text Words(Transform parent, Fields.Look look, float width)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text val = NewText(parent, "Astvard words", look, look.Size * 0.8f, look.Ink, (TextAlignmentOptions)257, (TextWrappingModes)1, (TextOverflowModes)0);
			if (width > 0f)
			{
				Widgets.Width(((Component)val).gameObject, width);
			}
			return val;
		}

		private static TMP_Text NewText(Transform parent, string name, Fields.Look look, float size, Color ink, TextAlignmentOptions alignment, TextWrappingModes wrapping, TextOverflowModes overflow = (TextOverflowModes)1)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.SetActive(false);
			val.transform.SetParent(parent, false);
			TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)look.Font != (Object)null)
			{
				((TMP_Text)val2).font = look.Font;
			}
			if ((Object)(object)look.Material != (Object)null)
			{
				((TMP_Text)val2).fontSharedMaterial = look.Material;
			}
			((TMP_Text)val2).fontSize = size;
			((Graphic)val2).color = ink;
			((TMP_Text)val2).alignment = alignment;
			((TMP_Text)val2).textWrappingMode = wrapping;
			((TMP_Text)val2).overflowMode = overflow;
			((Graphic)val2).raycastTarget = false;
			val.SetActive(true);
			return (TMP_Text)(object)val2;
		}

		private static RectTransform ActionsArea(RectTransform details)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Astvard actions", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent((Transform)(object)details, false);
			Rect rect = details.rect;
			float num = ((Rect)(ref rect)).yMax - 80f;
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			string[] keptInDetails = KeptInDetails;
			foreach (string text in keptInDetails)
			{
				Transform obj = ((Transform)details).Find(text);
				RectTransform val2 = (RectTransform)(object)((obj is RectTransform) ? obj : null);
				if (!((Object)(object)val2 == (Object)null))
				{
					val2.GetWorldCorners(array);
					num = Mathf.Min(num, ((Transform)details).InverseTransformPoint(array[0]).y);
				}
			}
			RectTransform val3 = (RectTransform)val.transform;
			val3.anchorMin = Vector2.zero;
			val3.anchorMax = Vector2.one;
			val3.offsetMin = new Vector2(10f, 10f);
			float num2 = num - 12f;
			rect = details.rect;
			val3.offsetMax = new Vector2(-10f, num2 - ((Rect)(ref rect)).yMax);
			return val3;
		}

		private static void Paint(Image image, MenuTabs.Section section)
		{
			//IL_002e: 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 (!((Object)(object)image == (Object)null))
			{
				Sprite val = (image.sprite = IconOf(section.IconPrefab));
				((Graphic)image).color = (((Object)(object)val != (Object)null) ? Color.white : Color.clear);
			}
		}

		private static Sprite IconOf(string prefab)
		{
			if (string.IsNullOrEmpty(prefab))
			{
				return null;
			}
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefab) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val2 != (Object)null)
			{
				return val2.m_itemData.GetIcon();
			}
			GameObject val3 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null);
			Piece val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent<Piece>() : null);
			if ((Object)(object)val4 != (Object)null)
			{
				return val4.m_icon;
			}
			StatusEffect val5 = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(prefab)) : null);
			if (!((Object)(object)val5 != (Object)null))
			{
				return null;
			}
			return val5.m_icon;
		}

		private static string PathBetween(Transform top, Transform bottom)
		{
			List<string> list = new List<string>();
			Transform val = bottom;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)top)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}
	}
	internal static class MenuTabs
	{
		internal sealed class Tab
		{
			internal readonly string Caption;

			internal readonly List<Section> Sections = new List<Section>();

			internal int Chosen;

			internal Tab(string caption)
			{
				Caption = caption;
			}

			internal Section Add(string title, string iconPrefab)
			{
				Section section = new Section(title, iconPrefab);
				Sections.Add(section);
				return section;
			}
		}

		internal sealed class Section
		{
			internal readonly string Title;

			internal readonly string IconPrefab;

			internal readonly List<Entry> Entries = new List<Entry>();

			internal Func<IReadOnlyList<Row>> Rows;

			internal Func<string> Empty;

			internal string ChosenKey;

			internal string Id;

			internal bool IsLive;

			internal Action Opened;

			internal bool RefillAsked;

			internal Section(string title, string iconPrefab)
			{
				Title = title;
				IconPrefab = iconPrefab;
			}

			internal Section Action(string caption, UnityAction onClick)
			{
				Entries.Add(new Entry(caption, onClick));
				return this;
			}

			internal Section Field(string label, string hint, string caption, Action<string> apply, string accepts = null, int limit = 32, Func<string> fill = null)
			{
				Entries.Add(new Entry(label, hint, caption, apply, accepts, limit, fill));
				return this;
			}

			internal Section Field(string label, string hint, string caption, Action<string> apply, string accepts, int limit, out Entry field)
			{
				field = new Entry(label, hint, caption, apply, accepts, limit, null);
				Entries.Add(field);
				return this;
			}

			internal Section Input(string label, string hint, string accepts, int limit, out Entry field)
			{
				field = new Entry(label, hint, null, null, accepts, limit, null);
				Entries.Add(field);
				return this;
			}

			internal Section Input(string label, string hint, string accepts, int limit, Func<string> fill, out Entry field)
			{
				field = new Entry(label, hint, null, null, accepts, limit, fill);
				Entries.Add(field);
				return this;
			}

			internal Section Input(string label, string hint, string accepts, int limit, Shared same)
			{
				Entries.Add(new Entry(label, hint, null, null, accepts, limit, null, same));
				return this;
			}

			internal Section Setting(string title, Func<string> says, Func<string> caption, UnityAction onClick)
			{
				Entries.Add(new Entry(title, says, caption, onClick));
				return this;
			}

			internal Section Value(string title, Func<string> says, string hint, string caption, Action<string> apply, string accepts, int limit)
			{
				Entries.Add(new Entry(title, says, hint, caption, apply, accepts, limit));
				return this;
			}

			internal Section Toggle(Func<string> caption, UnityAction onClick)
			{
				Entries.Add(Entry.TileOf(caption, onClick));
				return this;
			}

			internal Section Tile(string caption, UnityAction onClick)
			{
				Entries.Add(Entry.TileOf(caption, onClick));
				return this;
			}

			internal Section Button(Func<string> caption, UnityAction onClick)
			{
				Entries.Add(new Entry(caption, onClick));
				return this;
			}

			internal Section Heading(string title)
			{
				Entries.Add(Entry.HeadingOf(title));
				return this;
			}

			internal Section Strip(Func<string> says)
			{
				Entries.Add(Entry.StripOf(says));
				return this;
			}

			internal Section Mark(string name)
			{
				Entries.Add(Entry.MarkOf(name));
				return this;
			}

			internal Section Insert(string mark, params Entry[] entries)
			{
				int index = Entries.Count;
				for (int i = 0; i < Entries.Count; i++)
				{
					if (Entries[i].Kind == Entry.Kinds.Mark && Entries[i].Caption == mark)
					{
						index = i;
						break;
					}
				}
				Entries.InsertRange(index, entries);
				return this;
			}

			internal Section Text(Func<string> says)
			{
				Entries.Add(new Entry(says));
				return this;
			}

			internal Section Block(string title, Func<string> says)
			{
				Entries.Add(new Entry(title, says));
				return this;
			}

			internal Section Called(string id)
			{
				Id = id;
				return this;
			}

			internal Section Live()
			{
				IsLive = true;
				return this;
			}

			internal Section OnOpen(Action opened)
			{
				Opened = opened;
				return this;
			}

			internal void Refill()
			{
				RefillAsked = true;
			}

			internal Section List(Func<IReadOnlyList<Row>> rows, string empty)
			{
				return List(rows, () => empty);
			}

			internal Section List(Func<IReadOnlyList<Row>> rows, Func<string> empty)
			{
				Rows = rows;
				Empty = empty;
				return this;
			}
		}

		internal sealed class Row
		{
			internal readonly string Title;

			internal readonly string Key;

			internal readonly string Aside;

			internal readonly UnityAction Act;

			internal readonly string Words;

			internal bool IsNote => Words != null;

			internal bool IsHeading
			{
				get
				{
					if (Key == null)
					{
						return Words == null;
					}
					return false;
				}
			}

			internal Row(string title, string key, string aside = null)
				: this(title, key, aside, null, null)
			{
			}

			internal Row(string title, string key, UnityAction act)
				: this(title, key, null, act, null)
			{
			}

			private Row(string title, string key, string aside, UnityAction act, string words)
			{
				Title = title;
				Key = key;
				Aside = aside;
				Act = act;
				Words = words;
			}

			internal static Row Note(string title, string words)
			{
				return new Row(title, null, null, null, words ?? "");
			}
		}

		internal sealed class Shared
		{
			internal string Text = "";
		}

		internal sealed class Entry
		{
			internal enum Kinds
			{
				Plain,
				Heading,
				Strip,
				Tile,
				Mark
			}

			internal readonly Kinds Kind;

			internal readonly string Caption;

			internal readonly UnityAction OnClick;

			internal readonly string Hint;

			internal readonly string Label;

			internal readonly Action<string> Apply;

			internal readonly string Accepts;

			internal readonly int 

BepInEx/plugins/Astvard.Player.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using Astvard.Core;
using Astvard.Core.Builds;
using Astvard.Core.Net;
using Astvard.Core.Placement;
using Astvard.Core.Sorting;
using Astvard.Core.Terrain;
using Astvard.Core.Ui;
using Astvard.Players.Building;
using Astvard.Players.Functions;
using Astvard.Players.Hunting;
using Astvard.Players.Sorting;
using Astvard.Players.Terrain;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Astvard.Player")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d8721dbc184a53c5b52947c0c2c8980b874b1ebb")]
[assembly: AssemblyProduct("Astvard.Player")]
[assembly: AssemblyTitle("Astvard.Player")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Astvard.Players
{
	internal static class CharacterStats
	{
		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		private static class EitrOnTop
		{
			private static void Postfix(Player __instance, ref float eitr)
			{
				Sheet now = Now;
				if (now != null && now.Eitr > 0 && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					eitr += now.Eitr;
				}
			}
		}

		private const float AskEvery = 10f;

		private static ZRoutedRpc _listening;

		private static ZNet _heardOn;

		private static float _askedAt = float.MinValue;

		private static Sheet _sheet = new Sheet();

		private static Player _laidOn;

		private static bool _toldBadPacket;

		private static bool Heard
		{
			get
			{
				if ((Object)(object)_heardOn != (Object)null)
				{
					return _heardOn == ZNet.instance;
				}
				return false;
			}
		}

		private static Sheet Now
		{
			get
			{
				if (!Heard)
				{
					return null;
				}
				return _sheet;
			}
		}

		private static Player Prefab
		{
			get
			{
				ZNetScene instance = ZNetScene.instance;
				GameObject val = (((Object)(object)instance != (Object)null) ? instance.GetPrefab("Player") : null);
				if (!((Object)(object)val != (Object)null))
				{
					return null;
				}
				return val.GetComponent<Player>();
			}
		}

		internal static void Tick()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			ZNet instance2 = ZNet.instance;
			if (instance == null || (Object)(object)instance2 == (Object)null || instance2.IsServer())
			{
				return;
			}
			if (_listening != instance)
			{
				instance.Register<ZPackage>("Astvard_MyStats", (Action<long, ZPackage>)OnSheet);
				_listening = instance;
				_askedAt = float.MinValue;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			if (!Heard)
			{
				if (!(Time.time - _askedAt < 10f))
				{
					_askedAt = Time.time;
					instance.InvokeRoutedRPC("Astvard_AskMyStats", Array.Empty<object>());
				}
			}
			else if ((Object)(object)localPlayer != (Object)(object)_laidOn && _sheet.Any)
			{
				Lay(localPlayer);
			}
		}

		private static void OnSheet(long sender, ZPackage pkg)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			Sheet val = StatSheets.UnpackSheet(pkg);
			if (val == null)
			{
				if (!_toldBadPacket)
				{
					Plugin.Log.LogWarning((object)"The server sent the character's numbers in a format this mod does not read.");
				}
				_toldBadPacket = true;
				return;
			}
			bool flag = !Heard;
			Sheet val2 = (Sheet)((!flag) ? ((object)_sheet) : ((object)new Sheet()));
			_sheet = val;
			_heardOn = ZNet.instance;
			if (!val.Same(val2))
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					Lay(localPlayer);
				}
				Plugin.Log.LogInfo((object)("Character stats from the server: " + StatSheets.Describe(val) + "."));
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)1, flag ? ("Твоему персонажу здесь задано: " + StatSheets.Say(val)) : (val.Any ? ("Админ задал твоему персонажу: " + StatSheets.Say(val)) : "Админ вернул твоему персонажу числа игры"), 0, (Sprite)null, false);
				}
				MenuTabs.Refresh();
			}
		}

		private static void Lay(Player player)
		{
			Player prefab = Prefab;
			if (!((Object)(object)prefab == (Object)null))
			{
				player.m_baseHP = ((_sheet.Hp != -1) ? ((float)_sheet.Hp) : prefab.m_baseHP);
				player.m_baseStamina = ((_sheet.Stamina != -1) ? ((float)_sheet.Stamina) : prefab.m_baseStamina);
				player.m_maxCarryWeight = ((_sheet.Carry != -1) ? ((float)_sheet.Carry) : prefab.m_maxCarryWeight);
				_laidOn = player;
			}
		}

		internal static string About()
		{
			Sheet now = Now;
			if (now == null || !now.Any)
			{
				return "";
			}
			Player prefab = Prefab;
			StringBuilder stringBuilder = new StringBuilder("Админ задал твоему персонажу на этом сервере:");
			if (now.Hp != -1)
			{
				stringBuilder.Append("\nздоровье без еды ").Append(now.Hp).Append(((Object)(object)prefab != (Object)null) ? (" (у игры " + Round(prefab.m_baseHP) + ")") : "");
			}
			if (now.Stamina != -1)
			{
				stringBuilder.Append("\nвыносливость без еды ").Append(now.Stamina).Append(((Object)(object)prefab != (Object)null) ? (" (у игры " + Round(prefab.m_baseStamina) + ")") : "");
			}
			if (now.Eitr != -1)
			{
				stringBuilder.Append("\nэйтр сверх еды +").Append(now.Eitr);
			}
			if (now.Carry != -1)
			{
				stringBuilder.Append("\nпереносимый вес без пояса ").Append(now.Carry).Append(((Object)(object)prefab != (Object)null) ? (" (у игры " + Round(prefab.m_maxCarryWeight) + ")") : "");
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				stringBuilder.Append("\nСейчас, с едой и поясом: здоровье ").Append(Round(((Character)localPlayer).GetMaxHealth())).Append(", выносливость ")
					.Append(Round(((Character)localPlayer).GetMaxStamina()))
					.Append(", эйтр ")
					.Append(Round(((Character)localPlayer).GetMaxEitr()))
					.Append(", вес ")
					.Append(Round(localPlayer.GetMaxCarryWeight()));
			}
			return stringBuilder.ToString();
		}

		private static string Round(float value)
		{
			return Mathf.RoundToInt(value).ToString(CultureInfo.InvariantCulture);
		}
	}
	internal sealed class Charge
	{
		private sealed class Item
		{
			internal string Key;

			internal int Units;

			internal long Estimate;
		}

		internal readonly string Label;

		private readonly List<Item> _items = new List<Item>();

		internal int Cost
		{
			get
			{
				long num = 0L;
				foreach (Item item in _items)
				{
					num += item.Estimate;
				}
				return (int)Math.Min(num, 2147483647L);
			}
		}

		internal Charge(string label)
		{
			Label = label;
		}

		internal Charge Add(Limit price, int units)
		{
			if (units > 0)
			{
				_items.Add(new Item
				{
					Key = price.Key,
					Units = units,
					Estimate = (long)price.Value * (long)units
				});
			}
			return this;
		}

		internal Charge AddServerBuild(string name, int pieces, int own)
		{
			long estimate = ((own >= 0) ? own : (Limits.PriceBuild.Value + (long)Limits.PriceBuild100.Value * (long)((pieces + 99) / 100)));
			_items.Add(new Item
			{
				Key = "build:" + name,
				Units = pieces,
				Estimate = estimate
			});
			return this;
		}

		internal List<KeyValuePair<string, int>> Keys()
		{
			List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(_items.Count);
			foreach (Item item in _items)
			{
				list.Add(new KeyValuePair<string, int>(item.Key, item.Units));
			}
			return list;
		}
	}
	internal static class Charges
	{
		private const float Patience = 10f;

		private static int _counter;

		private static int _asked;

		private static float _askedAt;

		private static Charge _charge;

		private static Action<int> _go;

		private static Action<bool> _refused;

		private static ZRoutedRpc _listening;

		internal static bool Waiting
		{
			get
			{
				if (_asked != 0)
				{
					return Time.time - _askedAt < 10f;
				}
				return false;
			}
		}

		internal static void Pay(Charge charge, Action<int> go, Action<bool> refused = null, bool asPlayer = false)
		{
			if ((Access.IsAdmin && !asPlayer) || charge.Cost <= 0)
			{
				go(0);
				return;
			}
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance == null)
			{
				refused?.Invoke(obj: false);
				return;
			}
			if (Waiting)
			{
				Notice.Say("Жду, что скажет сервер про руны");
				refused?.Invoke(obj: false);
				return;
			}
			if (instance != _listening)
			{
				instance.Register<int, bool, int, int>("Astvard_PayAnswer", (Method<int, bool, int, int>)OnAnswer);
				_listening = instance;
			}
			_asked = ++_counter;
			_askedAt = Time.time;
			_charge = charge;
			_go = go;
			_refused = refused;
			instance.InvokeRoutedRPC("Astvard_Pay", new object[1] { Payments.PackPay(_asked, charge.Label, (IList<KeyValuePair<string, int>>)charge.Keys()) });
			((MonoBehaviour)Plugin.Instance).StartCoroutine(GiveUp(_asked));
		}

		internal static void Refund(int receipt, string label)
		{
			if (receipt != 0)
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC("Astvard_Refund", new object[1] { receipt });
				}
				Plugin.Log.LogInfo((object)("Asked the runes back for «" + label + "» (ask " + receipt + "): it did not happen."));
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)1, "Руны за «" + label + "» возвращаются: не вышло", 0, (Sprite)null, false);
				}
			}
		}

		internal static string List()
		{
			List<string> list = new List<string>();
			Limit[] prices = Limits.Prices;
			foreach (Limit val in prices)
			{
				if (val.Value > 0)
				{
					list.Add(val.Title + " — " + val.Say(val.Value));
				}
			}
			if (list.Count != 0)
			{
				return string.Join("\n", list.ToArray());
			}
			return null;
		}

		private static void OnAnswer(long sender, int ask, bool yes, int cost, int balance)
		{
			if (ask != _asked)
			{
				if (yes && cost > 0)
				{
					ZRoutedRpc instance = ZRoutedRpc.instance;
					if (instance != null)
					{
						instance.InvokeRoutedRPC("Astvard_Refund", new object[1] { ask });
					}
					Plugin.Log.LogInfo((object)("The server took " + cost + " runes for ask " + ask + " after it was given up — asked them back."));
				}
				return;
			}
			Charge charge = _charge;
			Action<int> go = _go;
			Action<bool> refused = _refused;
			_asked = 0;
			_charge = null;
			_go = null;
			_refused = null;
			if (yes)
			{
				if (cost > 0)
				{
					Plugin.Log.LogInfo((object)("Paid " + cost + " runes for «" + charge.Label + "», " + balance + " left."));
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)1, "−" + Words.Runes(cost) + " за «" + charge.Label + "». Осталось " + balance, 0, (Sprite)null, false);
					}
				}
				go((cost > 0) ? ask : 0);
			}
			else
			{
				Plugin.Log.LogInfo((object)("Not enough runes for «" + charge.Label + "»: " + cost + " asked, " + balance + " held."));
				Notice.Say("Не хватает рун: «" + charge.Label + "» стоит " + Words.Runes(cost) + ", а у тебя " + balance);
				refused?.Invoke(obj: true);
			}
		}

		private static IEnumerator GiveUp(int ask)
		{
			yield return (object)new WaitForSeconds(10f);
			if (ask == _asked)
			{
				Action<bool> refused = _refused;
				_asked = 0;
				_charge = null;
				_go = null;
				_refused = null;
				Notice.Say("Сервер не ответил про руны — нажми ещё раз");
				refused?.Invoke(obj: false);
			}
		}
	}
	internal static class Favorites
	{
		[HarmonyPatch(typeof(FejdStartup), "Start")]
		private static class AtTheMenu
		{
			private static void Postfix()
			{
				try
				{
					Take();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Favourites: " + ex));
				}
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static PopupButtonCallback <0>__Pop;
		}

		private const string Argument = "+astvard_favorite";

		private static readonly Regex Address = new Regex("^([A-Za-z0-9.-]{1,253}):(\\d{1,5})$");

		private static readonly FieldInfo WindowsList = AccessTools.Field(typeof(ServerListGui), "m_favoriteServersList");

		private static bool _taken;

		private static string _toSay;

		internal static void Tick()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			if (_toSay == null)
			{
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				_toSay = null;
				return;
			}
			FejdStartup instance = FejdStartup.instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_mainMenu == (Object)null) && instance.m_mainMenu.activeInHierarchy && UnifiedPopup.IsAvailable() && !UnifiedPopup.IsVisible())
			{
				string toSay = _toSay;
				_toSay = null;
				object obj = <>O.<0>__Pop;
				if (obj == null)
				{
					PopupButtonCallback val = UnifiedPopup.Pop;
					<>O.<0>__Pop = val;
					obj = (object)val;
				}
				UnifiedPopup.Push((PopupBase)new WarningPopup("Избранное", toSay, (PopupButtonCallback)obj, false));
			}
		}

		private static void Take()
		{
			if (_taken)
			{
				return;
			}
			_taken = true;
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			List<string> list = new List<string>();
			for (int i = 0; i < commandLineArgs.Length; i++)
			{
				if (commandLineArgs[i] != "+astvard_favorite")
				{
					continue;
				}
				if (i + 1 >= commandLineArgs.Length)
				{
					Plugin.Log.LogWarning((object)"Favourites: +astvard_favorite came last, with no address after it.");
					continue;
				}
				string text = Put(commandLineArgs[i + 1]);
				if (text != null)
				{
					list.Add(text);
				}
			}
			if (list.Count > 0)
			{
				_toSay = string.Join("\n", list);
			}
		}

		private unsafe static string Put(string given)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			Match match = Address.Match((given ?? "").Trim().TrimEnd(new char[1] { '/' }));
			if (!match.Success || !int.TryParse(match.Groups[2].Value, out var result) || result < 1 || result > 65535)
			{
				Plugin.Log.LogWarning((object)("Favourites: «" + given + "» after +astvard_favorite is not a host and a port, left alone."));
				return null;
			}
			string text = match.Groups[1].Value.ToLowerInvariant();
			string text2 = text + ":" + result;
			ServerJoinDataDedicated val = default(ServerJoinDataDedicated);
			((ServerJoinDataDedicated)(ref val))..ctor(text, (ushort)result);
			LocalServerList val2 = ListOfTheWindow();
			bool flag = val2 == null;
			if (flag)
			{
				val2 = new LocalServerList((string)null, ServerListGui.GetServerListLocations("favorite"));
			}
			try
			{
				if (Holds(val2, val))
				{
					Plugin.Log.LogInfo((object)("Favourites: " + text2 + " is there already."));
					return "Сервер " + text2 + " уже в избранном.";
				}
				val2.Add(new ServerJoinData(val));
				SaveStatusCode val3 = val2.Save(false);
				if ((int)val3 != 0)
				{
					Plugin.Log.LogError((object)("Favourites: " + text2 + " was not saved: " + ((object)(*(SaveStatusCode*)(&val3))/*cast due to .constrained prefix*/).ToString() + "."));
					return "Не вышло добавить " + text2 + " в избранное: игра не записала список (" + ((object)(*(SaveStatusCode*)(&val3))/*cast due to .constrained prefix*/).ToString() + ").";
				}
				Plugin.Log.LogInfo((object)("Favourites: " + text2 + " added, into " + (flag ? "the list's file" : "the server window's list") + "."));
				return "Сервер " + text2 + " добавлен в избранное — он в «Присоединиться» → «Избранное».";
			}
			finally
			{
				if (flag)
				{
					val2.Dispose();
				}
			}
		}

		private static LocalServerList ListOfTheWindow()
		{
			ServerListGui val = Object.FindFirstObjectByType<ServerListGui>((FindObjectsInactive)1);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if (WindowsList == null)
			{
				Plugin.Log.LogWarning((object)"Favourites: the server window keeps its favourites elsewhere in this game; the file is written, and the window, made already, shows the server from the next launch.");
				return null;
			}
			object? value = WindowsList.GetValue(val);
			return (LocalServerList)((value is LocalServerList) ? value : null);
		}

		private static bool Holds(LocalServerList list, ServerJoinDataDedicated wanted)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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_0013: Invalid comparison between Unknown and I4
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < list.Count; i++)
			{
				ServerJoinData val = list[i];
				if ((int)val.m_type == 3)
				{
					ServerJoinDataDedicated dedicated = ((ServerJoinData)(ref val)).Dedicated;
					if (dedicated.m_port == wanted.m_port && string.Equals(((ServerJoinDataDedicated)(ref dedicated)).m_host, ((ServerJoinDataDedicated)(ref wanted)).m_host, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	internal static class FrameTurn
	{
		private sealed class Slow
		{
			internal int Count;

			internal double Longest;

			internal float ToldAt = float.MinValue;
		}

		private const double SlowMs = 8.0;

		private const float TellEvery = 60f;

		private static readonly Dictionary<string, Slow> Slows = new Dictionary<string, Slow>();

		private static int _taken = -1;

		internal static bool Take()
		{
			int frameCount = Time.frameCount;
			if (_taken == frameCount)
			{
				return false;
			}
			_taken = frameCount;
			return true;
		}

		internal static long Started()
		{
			return Stopwatch.GetTimestamp();
		}

		internal static void Done(string what, long started)
		{
			double num = (double)(Stopwatch.GetTimestamp() - started) * 1000.0 / (double)Stopwatch.Frequency;
			if (!(num < 8.0))
			{
				if (!Slows.TryGetValue(what, out var value))
				{
					value = new Slow();
					Slows[what] = value;
				}
				value.Count++;
				if (num > value.Longest)
				{
					value.Longest = num;
				}
				if (!(Time.time - value.ToldAt < 60f))
				{
					Plugin.Log.LogInfo((object)("Slow: " + what + " took " + num.ToString("0") + " ms" + ((value.Count > 1) ? (" — " + value.Count + " passes over " + 8.0.ToString("0") + " ms since the last word, the longest " + value.Longest.ToString("0") + " ms") : "") + "."));
					value.ToldAt = Time.time;
					value.Count = 0;
					value.Longest = 0.0;
				}
			}
		}
	}
	internal static class InfoPage
	{
		private const float OffGround = 1f;

		private const float Near = 5f;

		private const int MostNames = 10;

		private static readonly string[] Sides = new string[8] { "север", "северо-восток", "восток", "юго-восток", "юг", "юго-запад", "запад", "северо-запад" };

		internal static string Where()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return "";
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			StringBuilder stringBuilder = new StringBuilder();
			StringBuilder stringBuilder2 = stringBuilder.Append("Позиция: x ").Append(Mathf.RoundToInt(position.x)).Append(", z ")
				.Append(Mathf.RoundToInt(position.z))
				.Append(" — ");
			Vector2 val = new Vector2(position.x, position.z);
			stringBuilder2.Append(Distance(((Vector2)(ref val)).magnitude)).Append(" от центра мира\n");
			stringBuilder.Append(Height(position)).Append('\n');
			stringBuilder.Append("Биом: ").Append(Biome(localPlayer)).Append('\n');
			stringBuilder.Append("Смотришь на ").Append(Facing(localPlayer)).Append('\n');
			stringBuilder.Append(Bed(position)).Append('\n');
			stringBuilder.Append(Building(position));
			return stringBuilder.ToString();
		}

		internal static string World()
		{
			StringBuilder stringBuilder = new StringBuilder();
			World world = ZNet.World;
			if (world != null)
			{
				stringBuilder.Append("Мир «").Append(world.m_name).Append("», сид ")
					.Append(world.m_seedName)
					.Append('\n');
			}
			int num = Limits.WorldResourceRate();
			stringBuilder.Append("Ресурсы: ").Append((num == Limits.ResourceRate.Default) ? "как в игре" : (num + "% от обычного")).Append('\n');
			string text = Clock();
			if (text != null)
			{
				stringBuilder.Append(text).Append('\n');
			}
			stringBuilder.Append(Players()).Append('\n');
			stringBuilder.Append("Мод у тебя: ").Append(Build.Say(Build.Here)).Append('\n');
			stringBuilder.Append("Мод на сервере: ").Append(ServerBuild());
			string text2 = ((Rules.ServerBuild != null) ? Build.Differs(Plain(Rules.ServerBuild)) : "");
			if (text2.Length > 0)
			{
				stringBuilder.Append('\n').Append(text2);
			}
			return stringBuilder.ToString();
		}

		internal static void CopySeed()
		{
			World world = ZNet.World;
			if (world == null || string.IsNullOrEmpty(world.m_seedName))
			{
				Notice.Say("Сида нет — зайди в мир");
				return;
			}
			GUIUtility.systemCopyBuffer = world.m_seedName;
			Notice.Say("Сид скопирован: " + world.m_seedName);
		}

		internal static string Allowed()
		{
			StringBuilder stringBuilder = new StringBuilder();
			if (!Rules.Heard)
			{
				stringBuilder.Append("Сервер правил не сказал, пока так:\n");
			}
			else if (Access.IsAdmin)
			{
				stringBuilder.Append(Access.AdminPays ? ("Ты сейчас админ: эти правила тебя не держат, а постройки — за ресурсы: " + Access.AdminPaysWhy + ".\n") : "Ты сейчас админ: эти правила тебя не держат.\n");
			}
			stringBuilder.Append("Постройки: ");
			if (Rules.PlayersPay)
			{
				stringBuilder.Append("платно, ").Append(Rules.PlayersChests ? ("из сумки и сундуков " + Said(Limits.Chests)) : "только из сумки");
			}
			else
			{
				stringBuilder.Append("даром, ").Append((Limits.BuildPause.Value > 0) ? ("не чаще раза в " + Limits.BuildPause.Value + " мин") : "без паузы");
			}
			stringBuilder.Append("\nКопирование ").Append(Said(Limits.Copy)).Append(", выравнивание ")
				.Append(Rules.PlayersLevel ? Said(Limits.Level) : "только админу");
			stringBuilder.Append("\nДорожки ").Append(Rules.PlayersRoad ? (Said(Limits.RoadLength) + ", мощение " + Said(Limits.PavingRadius)) : "только админу");
			stringBuilder.Append("\nСнос деревьев и камней: ").Append(Rules.PlayersClear ? "можно" : "только админу, игрокам — подлесок");
			stringBuilder.Append("\nМосты ").Append((Limits.BridgeLength.Value > 0) ? Said(Limits.BridgeLength) : "только админу");
			stringBuilder.Append("\nПочинка ").Append(Said(Limits.Repair)).Append(", топливо ")
				.Append(Rules.PlayersRepairPay ? "из сумки" : "даром");
			stringBuilder.Append("\nОтметки на карте ").Append(Said(Limits.MarksRadius)).Append(" — у всех, и у админа");
			stringBuilder.Append("\nВыкладывать постройки на сервер: ").Append(Rules.PlayersShare ? "можно" : "только админу");
			string text = Charges.List();
			stringBuilder.Append("\nЦены в рунах: ").Append((text == null) ? "всё без рун" : ("\n" + text));
			return stringBuilder.ToString();
		}

		private static string Height(Vector3 at)
		{
			//IL_0000: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (Character.InInterior(at))
			{
				return "Высота: в подземелье не считается";
			}
			string text = "Высота: " + Levelling.AboveWater(at.y);
			float num = default(float);
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(at, ref num) && Mathf.Abs(at.y - num) > 1f)
			{
				text = text + "\nЗемля под тобой: " + Levelling.AboveWater(num);
			}
			return text;
		}

		private static string Biome(Player player)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			BiomeSector currentBiomeData = player.GetCurrentBiomeData();
			if ((int)currentBiomeData.Biome != 0)
			{
				return currentBiomeData.GetName(false);
			}
			return "ещё не ясно";
		}

		private static string Facing(Player player)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 lookDir = ((Character)player).GetLookDir();
			Vector2 way = default(Vector2);
			((Vector2)(ref way))..ctor(lookDir.x, lookDir.z);
			if (((Vector2)(ref way)).sqrMagnitude < 0.0001f)
			{
				Vector3 forward = ((Component)player).transform.forward;
				((Vector2)(ref way))..ctor(forward.x, forward.z);
			}
			float num = Bearing(way);
			return Side(num) + " (" + Mathf.RoundToInt(num) % 360 + "°)";
		}

		private static string Bed(Vector3 at)
		{
			//IL_002b: 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_0074: 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)
			PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
			if (val != null && val.HaveCustomSpawnPoint())
			{
				return "Кровать: " + Way(at, val.GetCustomSpawnPoint());
			}
			Vector3 to = default(Vector3);
			if ((Object)(object)Game.instance != (Object)null && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetLocationIcon(Game.instance.m_StartLocation, ref to))
			{
				return "Кровати нет — возродишься у жертвенных камней: " + Way(at, to);
			}
			return "Кровати нет";
		}

		private static string Building(Vector3 at)
		{
			//IL_0000: 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)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (Location.IsInsideNoBuildLocation(at))
			{
				return "Строить: нельзя — здесь запрет самой игры";
			}
			if (!PrivateArea.CheckAccess(at, 0f, false, false))
			{
				return "Строить: нельзя — чужой оберег";
			}
			if (PrivateArea.InsideFactionArea(at, (Faction)0))
			{
				return "Строить: можно — оберег тебя пускает";
			}
			return "Строить: можно — оберегов нет";
		}

		private static string Clock()
		{
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			int num = Mathf.FloorToInt(instance.GetDayFraction() * 24f * 60f) % 1440;
			string text = "Время: день " + instance.GetDay() + ", " + (num / 60).ToString("00") + ":" + (num % 60).ToString("00");
			List<string> list = new List<string>();
			if (EnvMan.IsNight())
			{
				list.Add("ночь");
			}
			if (EnvMan.CanSleep())
			{
				list.Add("можно спать");
			}
			if (list.Count <= 0)
			{
				return text;
			}
			return text + " — " + string.Join(", ", list);
		}

		private static string Players()
		{
			//IL_003d: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerInfo> list = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetPlayerList() : null);
			if (list == null || list.Count == 0)
			{
				return "В мире: сервер ещё не прислал, кто здесь";
			}
			List<string> list2 = new List<string>();
			foreach (PlayerInfo item in list)
			{
				if (list2.Count == 10)
				{
					break;
				}
				string text = Plain(item.m_name);
				list2.Add((text.Length > 0) ? text : "без персонажа");
			}
			string text2 = "В мире " + list.Count + " " + Words.Plural(list.Count, "игрок", "игрока", "игроков") + ": " + string.Join(", ", list2);
			if (list.Count <= list2.Count)
			{
				return text2;
			}
			return text2 + " и ещё " + (list.Count - list2.Count);
		}

		private static string ServerBuild()
		{
			string serverBuild = Rules.ServerBuild;
			if (serverBuild != null)
			{
				return Build.Say(Plain(serverBuild));
			}
			if (!Rules.Heard)
			{
				return "не ответил: на нём нет Astvard.Server, или это одиночная игра";
			}
			return "не говорит — сборка старше этой";
		}

		private static string Said(Limit limit)
		{
			return limit.Say(limit.Value);
		}

		private static string Way(Vector3 from, Vector3 to)
		{
			//IL_0002: 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_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Vector2 way = default(Vector2);
			((Vector2)(ref way))..ctor(to.x - from.x, to.z - from.z);
			if (((Vector2)(ref way)).magnitude < 5f)
			{
				return "рядом";
			}
			return Distance(((Vector2)(ref way)).magnitude) + " на " + Side(Bearing(way));
		}

		private static float Bearing(Vector2 way)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Repeat(Mathf.Atan2(way.x, way.y) * 57.29578f, 360f);
		}

		private static string Side(float degrees)
		{
			return Sides[Mathf.RoundToInt(degrees / 45f) % Sides.Length];
		}

		private static string Distance(float metres)
		{
			if (metres < 1000f)
			{
				return Mathf.RoundToInt(metres) + " м";
			}
			return (metres / 1000f).ToString("0.#", CultureInfo.InvariantCulture).Replace('.', ',') + " км";
		}

		private static string Plain(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			foreach (char c in text)
			{
				if (c != '<' && c != '>' && !char.IsControl(c))
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString().Trim();
		}
	}
	[BepInPlugin("astvard.player", "Astvard Player", "2.0.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("valheim.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Func<string, string, string, string, Copy> <0>__TextAround;

			public static Func<Vector3, float, float, bool, bool, string, bool> <1>__PaveAt;

			public static Func<Vector3, float, bool, bool, bool, string, bool> <2>__StartAround;

			public static Func<string> <3>__About;

			public static Func<string> <4>__About;

			public static Func<string> <5>__Where;

			public static Func<string> <6>__World;

			public static Func<string> <7>__Allowed;

			public static UnityAction <8>__CopySeed;

			public static Func<string> <9>__Now;

			public static Action<string> <10>__CopyAndHold;

			public static Action<string> <11>__CopyAndSave;

			public static UnityAction <12>__CopyForRunes;

			public static Func<string> <13>__Caption;

			public static UnityAction <14>__Next;

			public static Func<string> <15>__NeighboursCaption;

			public static UnityAction <16>__ToggleNeighbours;

			public static Func<string> <17>__SnapCaption;

			public static UnityAction <18>__ToggleSnap;

			public static Func<string> <19>__ClearCaption;

			public static UnityAction <20>__ToggleClear;

			public static Func<string> <21>__About;

			public static Func<string> <22>__About;

			public static Func<string> <23>__Keys;

			public static UnityAction <24>__Undo;

			public static Func<IReadOnlyList<Row>> <25>__Rows;

			public static Func<string> <26>__Info;

			public static UnityAction <27>__Place;

			public static UnityAction <28>__PlaceForRunes;

			public static Action<string> <29>__Rename;

			public static Func<string> <30>__ChosenName;

			public static Action<string> <31>__Move;

			public static Func<string> <32>__ChosenCategory;

			public static UnityAction <33>__Put;

			public static UnityAction <34>__Delete;

			public static Func<IReadOnlyList<Row>> <35>__Rows;

			public static Func<string> <36>__Empty;

			public static Func<string> <37>__Info;

			public static UnityAction <38>__Place;

			public static UnityAction <39>__PlaceForRunes;

			public static Func<string> <40>__KindCaption;

			public static UnityAction <41>__NextKind;

			public static Func<string> <42>__ModeCaption;

			public static UnityAction <43>__ToggleMode;

			public static Func<string> <44>__PlaceCaption;

			public static UnityAction <45>__Place;

			public static UnityAction <46>__PlaceForRunes;

			public static Func<string> <47>__About;

			public static Func<string> <48>__WalkwayCaption;

			public static UnityAction <49>__ToggleWalkway;

			public static Func<string> <50>__RoofCaption;

			public static UnityAction <51>__ToggleRoof;

			public static Func<string> <52>__LevelCaption;

			public static UnityAction <53>__ToggleLevel;

			public static UnityAction <54>__Start;

			public static UnityAction <55>__StartForRunes;

			public static Func<string> <56>__About;

			public static Func<string> <57>__RoofCaption;

			public static UnityAction <58>__ToggleRoof;

			public static UnityAction <59>__Start;

			public static UnityAction <60>__StartForRunes;

			public static Func<string> <61>__About;

			public static Func<IReadOnlyList<Row>> <62>__Rows;

			public static Func<string> <63>__Empty;

			public static Func<string> <64>__Info;

			public static Func<string> <65>__HighlightCaption;

			public static UnityAction <66>__Highlight;

			public static Func<string> <67>__ShapeCaption;

			public static UnityAction <68>__ToggleShape;

			public static UnityAction <69>__Move;

			public static Action<string> <70>__Rename;

			public static Func<string> <71>__ChosenName;

			public static Action<string> <72>__Resize;

			public static Func<string> <73>__ChosenRadius;

			public static Action<string> <74>__Invite;

			public static Action<string> <75>__Strike;

			public static Func<string> <76>__DropCaption;

			public static UnityAction <77>__Drop;

			public static Func<IReadOnlyList<Row>> <78>__Rows;

			public static Func<string> <79>__Empty;

			public static Func<string> <80>__ZoneNow;

			public static Action<string> <81>__SetCoal;

			public static Func<string> <82>__ChosenCoal;

			public static Func<string> <83>__Stations;

			public static Func<string> <84>__Coal;

			public static Func<string> <85>__Lacking;

			public static Func<string> <86>__NoRoom;

			public static Func<string> <87>__Hives;

			public static Func<string> <88>__Meads;

			public static Func<string> <89>__Tames;

			public static Func<string> <90>__Info;

			public static Func<IReadOnlyList<Row>> <91>__Rows;

			public static Func<string> <92>__Empty;

			public static Func<string> <93>__ZoneNow;

			public static Func<string> <94>__ChosenKeep;

			public static Func<string> <95>__ChosenSeeds;

			public static Action<string> <96>__SetLimits;

			public static Func<string> <97>__ChosenBeds;

			public static Func<string> <98>__About;

			public static Func<IReadOnlyList<Row>> <99>__Rows;

			public static Func<string> <100>__Empty;

			public static Action <101>__Opened;

			public static Func<string> <102>__Info;

			public static Action<string> <103>__SetKeep;

			public static Func<string> <104>__ChosenKeep;

			public static Func<IReadOnlyList<Row>> <105>__ShelfRows;

			public static Func<string> <106>__Now;

			public static Func<string> <107>__SupplyTile;

			public static UnityAction <108>__ArmSupply;

			public static Func<string> <109>__CollectTile;

			public static UnityAction <110>__ArmCollect;

			public static Func<string> <111>__HighlightCaption;

			public static UnityAction <112>__ToggleHighlight;

			public static Func<string> <113>__About;

			public static Func<string> <114>__FillMeans;

			public static Func<string> <115>__FillCaption;

			public static UnityAction <116>__ToggleFill;

			public static Func<string> <117>__CollectMeans;

			public static Func<string> <118>__CollectCaption;

			public static UnityAction <119>__ToggleCollect;

			public static Func<string> <120>__ReachSays;

			public static Action<string> <121>__SetReach;

			public static Func<string> <122>__ShapeMeans;

			public static Func<string> <123>__ShapeCaption;

			public static UnityAction <124>__ToggleShape;

			public static Func<string> <125>__CoalSays;

			public static Action<string> <126>__SetCoal;

			public static Func<string> <127>__AtChests;

			public static Func<string> <128>__Coal;

			public static Func<string> <129>__Lacks;

			public static Func<string> <130>__NoRoom;

			public static Func<string> <131>__Hives;

			public static Func<string> <132>__Barrels;

			public static Func<string> <133>__Tames;

			public static Func<string> <134>__Now;

			public static UnityAction <135>__Place;

			public static Func<string> <136>__TrapTile;

			public static UnityAction <137>__ArmTrap;

			public static Func<string> <138>__ReleaseTile;

			public static UnityAction <139>__ArmRelease;

			public static Func<string> <140>__FeedTile;

			public static UnityAction <141>__ToggleFeed;

			public static Func<string> <142>__Near;

			public static Func<string> <143>__Diets;

			public static Func<string> <144>__About;

			public static UnityAction <145>__Circle;

			public static UnityAction <146>__Square;

			public static Func<string> <147>__About;

			public static UnityAction <148>__TakeBack;

			public static Action<Vector3> <149>__RememberEnd;

			public static Action<Vector3> <150>__ForgetEnd;

			public static Action <151>__Redraw;

			public static Func<string> <152>__Now;

			public static UnityAction <153>__Start;

			public static UnityAction <154>__Continue;

			public static Action<string> <155>__Pave;

			public static Func<string> <156>__SideCaption;

			public static UnityAction <157>__ToggleSide;

			public static Func<string> <158>__About;

			public static UnityAction <159>__TakeBack;

			public static Action<string> <160>__Run;

			public static Func<string> <161>__About;

			public static Func<string> <162>__Caption;

			public static UnityAction <163>__Toggle;

			public static Func<string> <164>__About;

			public static Func<string> <165>__CornerSays;
		}

		internal const string Guid = "astvard.player";

		internal const string Title = "Astvard Player";

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		internal static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Expected O, but got Unknown
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Expected O, but got Unknown
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Expected O, but got Unknown
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Expected O, but got Unknown
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Expected O, but got Unknown
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_034d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Expected O, but got Unknown
			//IL_0400: Unknown result type (might be due to invalid IL or missing references)
			//IL_0405: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Expected O, but got Unknown
			//IL_0480: Unknown result type (might be due to invalid IL or missing references)
			//IL_0485: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Expected O, but got Unknown
			//IL_04a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b0: Expected O, but got Unknown
			//IL_0578: Unknown result type (might be due to invalid IL or missing references)
			//IL_057d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0583: Expected O, but got Unknown
			//IL_059d: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a8: Expected O, but got Unknown
			//IL_0641: Unknown result type (might be due to invalid IL or missing references)
			//IL_0646: Unknown result type (might be due to invalid IL or missing references)
			//IL_064c: Expected O, but got Unknown
			//IL_0666: Unknown result type (might be due to invalid IL or missing references)
			//IL_066b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0671: Expected O, but got Unknown
			//IL_06e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f0: Expected O, but got Unknown
			//IL_0720: Unknown result type (might be due to invalid IL or missing references)
			//IL_0725: Unknown result type (might be due to invalid IL or missing references)
			//IL_072b: Expected O, but got Unknown
			//IL_075b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0760: Unknown result type (might be due to invalid IL or missing references)
			//IL_0766: Expected O, but got Unknown
			//IL_0780: Unknown result type (might be due to invalid IL or missing references)
			//IL_0785: Unknown result type (might be due to invalid IL or missing references)
			//IL_078b: Expected O, but got Unknown
			//IL_0863: Unknown result type (might be due to invalid IL or missing references)
			//IL_0868: Unknown result type (might be due to invalid IL or missing references)
			//IL_086e: Expected O, but got Unknown
			//IL_089e: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a9: Expected O, but got Unknown
			//IL_08d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_08de: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e4: Expected O, but got Unknown
			//IL_08fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0903: Unknown result type (might be due to invalid IL or missing references)
			//IL_0909: Expected O, but got Unknown
			//IL_0923: Unknown result type (might be due to invalid IL or missing references)
			//IL_0928: Unknown result type (might be due to invalid IL or missing references)
			//IL_092e: Expected O, but got Unknown
			//IL_096d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0972: Unknown result type (might be due to invalid IL or missing references)
			//IL_0978: Expected O, but got Unknown
			//IL_0a3a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a3f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a45: Expected O, but got Unknown
			//IL_0a5f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a64: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a6a: Expected O, but got Unknown
			//IL_0a84: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a89: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a8f: Expected O, but got Unknown
			//IL_0ace: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ad3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ad9: Expected O, but got Unknown
			//IL_0b7f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b84: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b8a: Expected O, but got Unknown
			//IL_0bba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bbf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bc5: Expected O, but got Unknown
			//IL_0bdf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0be4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bea: Expected O, but got Unknown
			//IL_0d31: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d36: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d3c: Expected O, but got Unknown
			//IL_0dfc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e06: Expected O, but got Unknown
			//IL_1051: Unknown result type (might be due to invalid IL or missing references)
			//IL_105b: Expected O, but got Unknown
			//IL_12ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_12ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_12f5: Expected O, but got Unknown
			//IL_1325: Unknown result type (might be due to invalid IL or missing references)
			//IL_132a: Unknown result type (might be due to invalid IL or missing references)
			//IL_1330: Expected O, but got Unknown
			//IL_1360: Unknown result type (might be due to invalid IL or missing references)
			//IL_1365: Unknown result type (might be due to invalid IL or missing references)
			//IL_136b: Expected O, but got Unknown
			//IL_13ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_13ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_13f5: Expected O, but got Unknown
			//IL_1445: Unknown result type (might be due to invalid IL or missing references)
			//IL_144a: Unknown result type (might be due to invalid IL or missing references)
			//IL_1450: Expected O, but got Unknown
			//IL_14f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_14f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_14fb: Expected O, but got Unknown
			//IL_16bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_16c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_16c8: Expected O, but got Unknown
			//IL_16f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_16fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_1703: Expected O, but got Unknown
			//IL_173d: Unknown result type (might be due to invalid IL or missing references)
			//IL_1742: Unknown result type (might be due to invalid IL or missing references)
			//IL_1748: Expected O, but got Unknown
			//IL_1778: Unknown result type (might be due to invalid IL or missing references)
			//IL_177d: Unknown result type (might be due to invalid IL or missing references)
			//IL_1783: Expected O, but got Unknown
			//IL_1851: Unknown result type (might be due to invalid IL or missing references)
			//IL_1856: Unknown result type (might be due to invalid IL or missing references)
			//IL_185c: Expected O, but got Unknown
			//IL_1876: Unknown result type (might be due to invalid IL or missing references)
			//IL_187b: Unknown result type (might be due to invalid IL or missing references)
			//IL_1881: Expected O, but got Unknown
			//IL_18c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_18c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_18cb: Expected O, but got Unknown
			//IL_1985: Unknown result type (might be due to invalid IL or missing references)
			//IL_198a: Unknown result type (might be due to invalid IL or missing references)
			//IL_1990: Expected O, but got Unknown
			//IL_19aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_19af: Unknown result type (might be due to invalid IL or missing references)
			//IL_19b5: Expected O, but got Unknown
			//IL_1a55: Unknown result type (might be due to invalid IL or missing references)
			//IL_1a5a: Unknown result type (might be due to invalid IL or missing references)
			//IL_1a60: Expected O, but got Unknown
			//IL_1b1b: Unknown result type (might be due to invalid IL or missing references)
			//IL_1b20: Unknown result type (might be due to invalid IL or missing references)
			//IL_1b26: Expected O, but got Unknown
			//IL_1b77: Unknown result type (might be due to invalid IL or missing references)
			//IL_1b81: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			Tab val = MenuTabs.Add("ПАНЕЛЬ ИГРОКА");
			Copies.Around = Commands.TextAround;
			Paving.Circle = Roads.PaveAt;
			Paving.Fence = Fence.StartAround;
			Section obj = val.Add("Информация", "piece_cartographytable").Live().Block("Руны", (Func<string>)Runes.About)
				.Block("Персонаж", (Func<string>)CharacterStats.About)
				.Block("Где ты", (Func<string>)InfoPage.Where)
				.Block("Мир и сервер", (Func<string>)InfoPage.World)
				.Block("Правила сервера", (Func<string>)InfoPage.Allowed);
			object obj2 = <>O.<8>__CopySeed;
			if (obj2 == null)
			{
				UnityAction val2 = InfoPage.CopySeed;
				<>O.<8>__CopySeed = val2;
				obj2 = (object)val2;
			}
			obj.Action("Скопировать сид", (UnityAction)obj2);
			Section obj3 = val.Add("Постройки", "Hammer").Called("build-settings").Live()
				.Strip((Func<string>)Builder.Now)
				.Heading("Копия")
				.Field("Радиус, м", "10, если пусто", "Копировать", (Action<string>)Commands.CopyAndHold, "0123456789", 2, ref Commands.Radius)
				.Field("Имя постройки", "напр. Дом у реки", "Скопировать", (Action<string>)Commands.CopyAndSave, (string)null, 60, (Func<string>)null)
				.Input("Категория", "Разное, если пусто", (string)null, 40, ref Commands.Category);
			object obj4 = <>O.<12>__CopyForRunes;
			if (obj4 == null)
			{
				UnityAction val3 = Commands.CopyForRunes;
				<>O.<12>__CopyForRunes = val3;
				obj4 = (object)val3;
			}
			Section obj5 = obj3.Tile("Копировать за руны", (UnityAction)obj4).Heading("Как ставится");
			Func<string> func = Groundwork.Caption;
			object obj6 = <>O.<14>__Next;
			if (obj6 == null)
			{
				UnityAction val4 = Groundwork.Next;
				<>O.<14>__Next = val4;
				obj6 = (object)val4;
			}
			Section obj7 = obj5.Toggle(func, (UnityAction)obj6);
			Func<string> func2 = BuildSettings.NeighboursCaption;
			object obj8 = <>O.<16>__ToggleNeighbours;
			if (obj8 == null)
			{
				UnityAction val5 = BuildSettings.ToggleNeighbours;
				<>O.<16>__ToggleNeighbours = val5;
				obj8 = (object)val5;
			}
			Section obj9 = obj7.Toggle(func2, (UnityAction)obj8);
			Func<string> func3 = BuildSettings.SnapCaption;
			object obj10 = <>O.<18>__ToggleSnap;
			if (obj10 == null)
			{
				UnityAction val6 = BuildSettings.ToggleSnap;
				<>O.<18>__ToggleSnap = val6;
				obj10 = (object)val6;
			}
			Section obj11 = obj9.Toggle(func3, (UnityAction)obj10);
			Func<string> func4 = BuildSettings.ClearCaption;
			object obj12 = <>O.<20>__ToggleClear;
			if (obj12 == null)
			{
				UnityAction val7 = BuildSettings.ToggleClear;
				<>O.<20>__ToggleClear = val7;
				obj12 = (object)val7;
			}
			Section obj13 = obj11.Toggle(func4, (UnityAction)obj12).Mark("admin-build-tiles").Input("Дистанция проекции, м", "11, если пусто; от 2 до 40", "0123456789.,", 4, ref BuildSettings.Distance)
				.Block("Что копируется", (Func<string>)Commands.About)
				.Block("Что это значит", (Func<string>)BuildSettings.About)
				.Block("Клавиши", (Func<string>)BuildSettings.Keys);
			object obj14 = <>O.<24>__Undo;
			if (obj14 == null)
			{
				UnityAction val8 = Commands.Undo;
				<>O.<24>__Undo = val8;
				obj14 = (object)val8;
			}
			obj13.Action("Отменить постройку", (UnityAction)obj14);
			Section obj15 = val.Add("Мои постройки", "Wood").List((Func<IReadOnlyList<Row>>)Library.Rows, "Пока пусто: «Скопировать»\nв «Постройках» сохраняет сюда").Block("Постройка", (Func<string>)Library.Info);
			object obj16 = <>O.<27>__Place;
			if (obj16 == null)
			{
				UnityAction val9 = Library.Place;
				<>O.<27>__Place = val9;
				obj16 = (object)val9;
			}
			Section obj17 = obj15.Tile("Поставить", (UnityAction)obj16);
			object obj18 = <>O.<28>__PlaceForRunes;
			if (obj18 == null)
			{
				UnityAction val10 = Library.PlaceForRunes;
				<>O.<28>__PlaceForRunes = val10;
				obj18 = (object)val10;
			}
			Section obj19 = obj17.Tile("За руны", (UnityAction)obj18).Heading("Имя и категория").Field("Имя", "новое имя", "Переназвать", (Action<string>)Library.Rename, (string)null, 60, (Func<string>)Library.ChosenName)
				.Field("Категория", "напр. Дома", "Перенести", (Action<string>)Library.Move, (string)null, 40, (Func<string>)Library.ChosenCategory)
				.Heading("Ещё");
			object obj20 = <>O.<33>__Put;
			if (obj20 == null)
			{
				UnityAction val11 = PutOnServer.Put;
				<>O.<33>__Put = val11;
				obj20 = (object)val11;
			}
			Section obj21 = obj19.Tile("Выложить на сервер", (UnityAction)obj20);
			object obj22 = <>O.<34>__Delete;
			if (obj22 == null)
			{
				UnityAction val12 = Library.Delete;
				<>O.<34>__Delete = val12;
				obj22 = (object)val12;
			}
			Library.Section = obj21.Tile("Удалить", (UnityAction)obj22);
			Section obj23 = val.Add("Постройки сервера", "piece_workbench").Called("server-builds").List((Func<IReadOnlyList<Row>>)ServerLibrary.Rows, (Func<string>)ServerLibrary.Empty)
				.Block("Постройка", (Func<string>)ServerLibrary.Info);
			object obj24 = <>O.<38>__Place;
			if (obj24 == null)
			{
				UnityAction val13 = ServerLibrary.Place;
				<>O.<38>__Place = val13;
				obj24 = (object)val13;
			}
			Section obj25 = obj23.Tile("Поставить", (UnityAction)obj24);
			object obj26 = <>O.<39>__PlaceForRunes;
			if (obj26 == null)
			{
				UnityAction val14 = ServerLibrary.PlaceForRunes;
				<>O.<39>__PlaceForRunes = val14;
				obj26 = (object)val14;
			}
			ServerLibrary.Section = obj25.Tile("За руны", (UnityAction)obj26);
			Section obj27 = val.Add("Сундуки", "piece_chest_wood").Live().Strip((Func<string>)Builder.Now)
				.Heading("Какой и как");
			Func<string> func5 = Chests.KindCaption;
			object obj28 = <>O.<41>__NextKind;
			if (obj28 == null)
			{
				UnityAction val15 = Chests.NextKind;
				<>O.<41>__NextKind = val15;
				obj28 = (object)val15;
			}
			Section obj29 = obj27.Toggle(func5, (UnityAction)obj28);
			Func<string> func6 = Chests.ModeCaption;
			object obj30 = <>O.<43>__ToggleMode;
			if (obj30 == null)
			{
				UnityAction val16 = Chests.ToggleMode;
				<>O.<43>__ToggleMode = val16;
				obj30 = (object)val16;
			}
			Section obj31 = obj29.Toggle(func6, (UnityAction)obj30);
			Func<string> func7 = Chests.PlaceCaption;
			object obj32 = <>O.<45>__Place;
			if (obj32 == null)
			{
				UnityAction val17 = Chests.Place;
				<>O.<45>__Place = val17;
				obj32 = (object)val17;
			}
			Section obj33 = obj31.Toggle(func7, (UnityAction)obj32);
			object obj34 = <>O.<46>__PlaceForRunes;
			if (obj34 == null)
			{
				UnityAction val18 = Chests.PlaceForRunes;
				<>O.<46>__PlaceForRunes = val18;
				obj34 = (object)val18;
			}
			obj33.Tile("За руны", (UnityAction)obj34).Block("Как встаёт", (Func<string>)Chests.About).Block("Клавиши", (Func<string>)BuildSettings.Keys);
			Section obj35 = val.Add("Забор", "stake_wall").Live().Strip((Func<string>)(() => Both(Fence.Now(), Builder.Now())))
				.Heading("Кольцо")
				.Input("Радиус, м", "20, если пусто", "0123456789.,", 4, ref Fence.Radius);
			Func<string> func8 = Fence.WalkwayCaption;
			object obj36 = <>O.<49>__ToggleWalkway;
			if (obj36 == null)
			{
				UnityAction val19 = Fence.ToggleWalkway;
				<>O.<49>__ToggleWalkway = val19;
				obj36 = (object)val19;
			}
			Section obj37 = obj35.Toggle(func8, (UnityAction)obj36);
			Func<string> func9 = Fence.RoofCaption;
			object obj38 = <>O.<51>__ToggleRoof;
			if (obj38 == null)
			{
				UnityAction val20 = Fence.ToggleRoof;
				<>O.<51>__ToggleRoof = val20;
				obj38 = (object)val20;
			}
			Section obj39 = obj37.Toggle(func9, (UnityAction)obj38);
			Func<string> func10 = Fence.LevelCaption;
			object obj40 = <>O.<53>__ToggleLevel;
			if (obj40 == null)
			{
				UnityAction val21 = Fence.ToggleLevel;
				<>O.<53>__ToggleLevel = val21;
				obj40 = (object)val21;
			}
			Section obj41 = obj39.Toggle(func10, (UnityAction)obj40);
			object obj42 = <>O.<54>__Start;
			if (obj42 == null)
			{
				UnityAction val22 = Fence.Start;
				<>O.<54>__Start = val22;
				obj42 = (object)val22;
			}
			Section obj43 = obj41.Action("Обнести забором", (UnityAction)obj42);
			object obj44 = <>O.<55>__StartForRunes;
			if (obj44 == null)
			{
				UnityAction val23 = Fence.StartForRunes;
				<>O.<55>__StartForRunes = val23;
				obj44 = (object)val23;
			}
			Section obj45 = obj43.Action("Обнести забором за руны", (UnityAction)obj44).Block("Как встаёт", (Func<string>)Fence.About);
			object obj46 = <>O.<24>__Undo;
			if (obj46 == null)
			{
				UnityAction val24 = Commands.Undo;
				<>O.<24>__Undo = val24;
				obj46 = (object)val24;
			}
			obj45.Action("Отменить постройку", (UnityAction)obj46);
			Section obj47 = val.Add("Мост", "wood_floor").Live().Strip((Func<string>)(() => Both(Bridge.Now(), Builder.Now())))
				.Heading("Размеры")
				.Input("Ширина, секций по 2 м", "2, если пусто; до 8", "0123456789", 1, ref Bridge.Width)
				.Input("Подъём настила, м", "0, если пусто", "0123456789.,", 4, ref Bridge.Lift)
				.Input("Отступ конца, м", "4, если пусто", "0123456789.,", 4, ref Bridge.Gap);
			Func<string> func11 = Bridge.RoofCaption;
			object obj48 = <>O.<58>__ToggleRoof;
			if (obj48 == null)
			{
				UnityAction val25 = Bridge.ToggleRoof;
				<>O.<58>__ToggleRoof = val25;
				obj48 = (object)val25;
			}
			Section obj49 = obj47.Toggle(func11, (UnityAction)obj48);
			object obj50 = <>O.<59>__Start;
			if (obj50 == null)
			{
				UnityAction val26 = Bridge.Start;
				<>O.<59>__Start = val26;
				obj50 = (object)val26;
			}
			Section obj51 = obj49.Action("Начать мост", (UnityAction)obj50);
			object obj52 = <>O.<60>__StartForRunes;
			if (obj52 == null)
			{
				UnityAction val27 = Bridge.StartForRunes;
				<>O.<60>__StartForRunes = val27;
				obj52 = (object)val27;
			}
			Section obj53 = obj51.Action("Начать мост за руны", (UnityAction)obj52).Block("Как класть", (Func<string>)Bridge.About);
			object obj54 = <>O.<24>__Undo;
			if (obj54 == null)
			{
				UnityAction val28 = Commands.Undo;
				<>O.<24>__Undo = val28;
				obj54 = (object)val28;
			}
			obj53.Action("Отменить постройку", (UnityAction)obj54);
			Section obj55 = val.Add("Зоны сортировки", "Cart").List((Func<IReadOnlyList<Row>>)SortPage.Rows, (Func<string>)SortPage.Empty).Live()
				.Block("Зона", (Func<string>)SortPage.Info);
			Func<string> func12 = SortPage.HighlightCaption;
			object obj56 = <>O.<66>__Highlight;
			if (obj56 == null)
			{
				UnityAction val29 = SortPage.Highlight;
				<>O.<66>__Highlight = val29;
				obj56 = (object)val29;
			}
			Section obj57 = obj55.Toggle(func12, (UnityAction)obj56);
			Func<string> func13 = SortPage.ShapeCaption;
			object obj58 = <>O.<68>__ToggleShape;
			if (obj58 == null)
			{
				UnityAction val30 = SortPage.ToggleShape;
				<>O.<68>__ToggleShape = val30;
				obj58 = (object)val30;
			}
			Section obj59 = obj57.Toggle(func13, (UnityAction)obj58);
			object obj60 = <>O.<69>__Move;
			if (obj60 == null)
			{
				UnityAction val31 = SortPage.Move;
				<>O.<69>__Move = val31;
				obj60 = (object)val31;
			}
			Section obj61 = obj59.Tile("Перенести", (UnityAction)obj60).Heading("Имя и размер").Field("Имя зоны", "напр. Склад", "Назвать", (Action<string>)SortPage.Rename, (string)null, 24, (Func<string>)SortPage.ChosenName)
				.Field("Радиус, м", "у квадрата — от середины до края", "Задать", (Action<string>)SortPage.Resize, "0123456789", 2, (Func<string>)SortPage.ChosenRadius)
				.Heading("Кто в зоне")
				.Field("Вписать игрока", "имя в игре", "Вписать", (Action<string>)SortPage.Invite, (string)null, 32, (Func<string>)null)
				.Field("Выписать игрока", "имя в игре", "Выписать", (Action<string>)SortPage.Strike, (string)null, 32, (Func<string>)null);
			Func<string> func14 = SortPage.DropCaption;
			object obj62 = <>O.<77>__Drop;
			if (obj62 == null)
			{
				UnityAction val32 = SortPage.Drop;
				<>O.<77>__Drop = val32;
				obj62 = (object)val32;
			}
			SortPage.Zones = obj61.Button(func14, (UnityAction)obj62);
			FillPage.Section = val.Add("Настройка зоны", "smelter").List((Func<IReadOnlyList<Row>>)FillPage.Rows, (Func<string>)FillPage.Empty).Live()
				.Strip((Func<string>)FillPage.ZoneNow)
				.Heading("Что наполнять");
			int[] order = FillKinds.Order;
			foreach (int num2 in order)
			{
				int chosen = num2;
				FillPage.Section.Toggle((Func<string>)(() => FillPage.Caption(chosen)), (UnityAction)delegate
				{
					FillPage.Toggle(chosen);
				});
			}
			FillPage.Section.Field("Угля на складе", "0 — без предела", "Задать", (Action<string>)FillPage.SetCoal, "0123456789", 6, (Func<string>)FillPage.ChosenCoal).Heading("Последний проход").Block("Станции", (Func<string>)FillPage.Stations)
				.Block("Уголь", (Func<string>)FillPage.Coal)
				.Block("Нечем наполнить", (Func<string>)FillPage.Lacking)
				.Block("Некуда класть", (Func<string>)FillPage.NoRoom)
				.Block("Ульи", (Func<string>)FillPage.Hives)
				.Block("Медовухи", (Func<string>)FillPage.Meads)
				.Block("Звери", (Func<string>)FillPage.Tames)
				.Block("Что это значит", (Func<string>)FillPage.Info);
			GardenPage.Section = val.Add("Огород", "Cultivator").List((Func<IReadOnlyList<Row>>)GardenPage.Rows, (Func<string>)GardenPage.Empty).Live()
				.Strip((Func<string>)GardenPage.ZoneNow)
				.Heading("Что делать");
			order = GardenKinds.Order;
			foreach (int num3 in order)
			{
				int chosen2 = num3;
				GardenPage.Section.Toggle((Func<string>)(() => GardenPage.Caption(chosen2)), (UnityAction)delegate
				{
					GardenPage.Toggle(chosen2);
				});
			}
			GardenPage.Section.Heading("Пределы").Input("Урожая на складе", "каждой культуры, 0 — без предела", "0123456789", 6, (Func<string>)GardenPage.ChosenKeep, ref GardenPage.Keep).Input("Семян на складе", "каждого вида, 0 — без предела", "0123456789", 6, (Func<string>)GardenPage.ChosenSeeds, ref GardenPage.Seeds)
				.Field("Количество грядок", "всего в зоне, 0 — без предела", "Задать", (Action<string>)GardenPage.SetLimits, "0123456789", 6, (Func<string>)GardenPage.ChosenBeds)
				.Block("Как это устроено", (Func<string>)GardenPage.About);
			MeadPage.Section = val.Add("Медовухи", "MeadHealthMinor").List((Func<IReadOnlyList<Row>>)MeadPage.Rows, (Func<string>)MeadPage.Empty).OnOpen((Action)MeadPage.Opened)
				.Live()
				.Block("Сейчас", (Func<string>)MeadPage.Info)
				.Field("Держать на складе", "бутылок, 0 — не варить", "Задать", (Action<string>)MeadPage.SetKeep, "0123456789", 4, (Func<string>)MeadPage.ChosenKeep);
			val.Add("Полки", "sign").List((Func<IReadOnlyList<Row>>)SortPage.ShelfRows, "Полок нет");
			StationChests.Bind(((BaseUnityPlugin)this).Config);
			Section obj63 = val.Add("Зона станций", "piece_chest").Live().Strip((Func<string>)StationChests.Now)
				.Heading("Назначить сундук");
			Func<string> func15 = StationPage.SupplyTile;
			object obj64 = <>O.<108>__ArmSupply;
			if (obj64 == null)
			{
				UnityAction val33 = StationPage.ArmSupply;
				<>O.<108>__ArmSupply = val33;
				obj64 = (object)val33;
			}
			Section obj65 = obj63.Toggle(func15, (UnityAction)obj64);
			Func<string> func16 = StationPage.CollectTile;
			object obj66 = <>O.<110>__ArmCollect;
			if (obj66 == null)
			{
				UnityAction val34 = StationPage.ArmCollect;
				<>O.<110>__ArmCollect = val34;
				obj66 = (object)val34;
			}
			Section obj67 = obj65.Toggle(func16, (UnityAction)obj66);
			Func<string> func17 = StationPage.HighlightCaption;
			object obj68 = <>O.<112>__ToggleHighlight;
			if (obj68 == null)
			{
				UnityAction val35 = StationPage.ToggleHighlight;
				<>O.<112>__ToggleHighlight = val35;
				obj68 = (object)val35;
			}
			Section obj69 = obj67.Toggle(func17, (UnityAction)obj68).Block("Как", (Func<string>)StationPage.About).Heading("Настройки");
			Func<string> func18 = StationPage.FillMeans;
			Func<string> func19 = StationPage.FillCaption;
			object obj70 = <>O.<116>__ToggleFill;
			if (obj70 == null)
			{
				UnityAction val36 = StationPage.ToggleFill;
				<>O.<116>__ToggleFill = val36;
				obj70 = (object)val36;
			}
			Section obj71 = obj69.Setting("Наполнение", func18, func19, (UnityAction)obj70);
			Func<string> func20 = StationPage.CollectMeans;
			Func<string> func21 = StationPage.CollectCaption;
			object obj72 = <>O.<119>__ToggleCollect;
			if (obj72 == null)
			{
				UnityAction val37 = StationPage.ToggleCollect;
				<>O.<119>__ToggleCollect = val37;
				obj72 = (object)val37;
			}
			Section obj73 = obj71.Setting("Сбор", func20, func21, (UnityAction)obj72).Value("Зона станций", (Func<string>)StationPage.ReachSays, "от 8 до 64 м", "Задать", (Action<string>)StationPage.SetReach, "0123456789", 2);
			Func<string> func22 = StationPage.ShapeMeans;
			Func<string> func23 = StationPage.ShapeCaption;
			object obj74 = <>O.<124>__ToggleShape;
			if (obj74 == null)
			{
				UnityAction val38 = StationPage.ToggleShape;
				<>O.<124>__ToggleShape = val38;
				obj74 = (object)val38;
			}
			obj73.Setting("Форма зоны", func22, func23, (UnityAction)obj74).Value("Угля на складе", (Func<string>)StationPage.CoalSays, "0 — без предела", "Задать", (Action<string>)StationPage.SetCoal, "0123456789", 6).Heading("Последний проход")
				.Block("У сундуков", (Func<string>)StationChests.AtChests)
				.Block("Уголь", (Func<string>)StationChests.Coal)
				.Block("Нечем наполнить", (Func<string>)StationChests.Lacks)
				.Block("Некуда класть", (Func<string>)StationChests.NoRoom)
				.Block("Ульи", (Func<string>)StationChests.Hives)
				.Block("Бочки", (Func<string>)StationChests.Barrels)
				.Block("Звери", (Func<string>)StationChests.Tames);
			Traps.Bind(((BaseUnityPlugin)this).Config);
			Section obj75 = val.Add("Капканы", "TrophyBoar").Live().Strip((Func<string>)TrapPage.Now)
				.Heading("Капкан");
			object obj76 = <>O.<135>__Place;
			if (obj76 == null)
			{
				UnityAction val39 = TrapPage.Place;
				<>O.<135>__Place = val39;
				obj76 = (object)val39;
			}
			Section obj77 = obj75.Tile("Поставить капкан", (UnityAction)obj76);
			Func<string> func24 = TrapPage.TrapTile;
			object obj78 = <>O.<137>__ArmTrap;
			if (obj78 == null)
			{
				UnityAction val40 = TrapPage.ArmTrap;
				<>O.<137>__ArmTrap = val40;
				obj78 = (object)val40;
			}
			Section obj79 = obj77.Toggle(func24, (UnityAction)obj78).Heading("Пойманный зверь");
			Func<string> func25 = TrapPage.ReleaseTile;
			object obj80 = <>O.<139>__ArmRelease;
			if (obj80 == null)
			{
				UnityAction val41 = TrapPage.ArmRelease;
				<>O.<139>__ArmRelease = val41;
				obj80 = (object)val41;
			}
			Section obj81 = obj79.Toggle(func25, (UnityAction)obj80);
			Func<string> func26 = TrapPage.FeedTile;
			object obj82 = <>O.<141>__ToggleFeed;
			if (obj82 == null)
			{
				UnityAction val42 = TrapPage.ToggleFeed;
				<>O.<141>__ToggleFeed = val42;
				obj82 = (object)val42;
			}
			obj81.Toggle(func26, (UnityAction)obj82).Block("Капканы рядом", (Func<string>)TrapPage.Near).Block("Кто что ест", (Func<string>)TrapPage.Diets)
				.Block("Как работает", (Func<string>)TrapPage.About);
			Section obj83 = val.Add("Рельеф", "Hoe").Input("Радиус, м", "8, если пусто", "0123456789", 2, ref Levelling.Radius).Input("Высота над водой, м", "где стоишь, если пусто", "0123456789-.,", 6, ref Levelling.Height);
			object obj84 = <>O.<145>__Circle;
			if (obj84 == null)
			{
				UnityAction val43 = Levelling.Circle;
				<>O.<145>__Circle = val43;
				obj84 = (object)val43;
			}
			Section obj85 = obj83.Tile("Выровнять круг", (UnityAction)obj84);
			object obj86 = <>O.<146>__Square;
			if (obj86 == null)
			{
				UnityAction val44 = Levelling.Square;
				<>O.<146>__Square = val44;
				obj86 = (object)val44;
			}
			Section obj87 = obj85.Tile("Выровнять квадрат", (UnityAction)obj86).Block("Как это", (Func<string>)Levelling.About);
			object obj88 = <>O.<148>__TakeBack;
			if (obj88 == null)
			{
				UnityAction val45 = Levelling.TakeBack;
				<>O.<148>__TakeBack = val45;
				obj88 = (object)val45;
			}
			obj87.Action("Откатить", (UnityAction)obj88);
			LongRoads.Ended += Roads.RememberEnd;
			LongRoads.Undone += Roads.ForgetEnd;
			RoadSettings.Changed += Roads.Redraw;
			Section obj89 = val.Add("Дорожки", "stone_floor_2x2").Live().Strip((Func<string>)Roads.Now)
				.Heading("Проложить");
			object obj90 = <>O.<153>__Start;
			if (obj90 == null)
			{
				UnityAction val46 = Roads.Start;
				<>O.<153>__Start = val46;
				obj90 = (object)val46;
			}
			Section obj91 = obj89.Tile("Начать дорожку", (UnityAction)obj90);
			object obj92 = <>O.<154>__Continue;
			if (obj92 == null)
			{
				UnityAction val47 = Roads.Continue;
				<>O.<154>__Continue = val47;
				obj92 = (object)val47;
			}
			Section obj93 = obj91.Tile("Продолжить от конца", (UnityAction)obj92).Field("Радиус мощения, м", "8, если пусто", "Замостить круг", (Action<string>)Roads.Pave, "0123456789", 2, ref Roads.PavingRadius);
			RoadSettings.AddTo(obj93, (Action<Section>)delegate(Section own)
			{
				//IL_0046: 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_0051: Expected O, but got Unknown
				Section obj98 = own.Input("Изгиб", "0 — прямо, 10 — полукругом", "0123456789,.", 4, ref Roads.Bend);
				Func<string> func28 = Roads.SideCaption;
				object obj99 = <>O.<157>__ToggleSide;
				if (obj99 == null)
				{
					UnityAction val50 = Roads.ToggleSide;
					<>O.<157>__ToggleSide = val50;
					obj99 = (object)val50;
				}
				obj98.Toggle(func28, (UnityAction)obj99);
			}, false);
			Section obj94 = obj93.Block("Как это", (Func<string>)Roads.About);
			object obj95 = <>O.<159>__TakeBack;
			if (obj95 == null)
			{
				UnityAction val48 = Roads.TakeBack;
				<>O.<159>__TakeBack = val48;
				obj95 = (object)val48;
			}
			obj94.Action("Откатить", (UnityAction)obj95);
			MapMarks.Bind(((BaseUnityPlugin)this).Config);
			Section obj96 = val.Add("Функции", "Torch").Heading("Починить").Field("Радиус, м", "20, если пусто", "Починить", (Action<string>)Repair.Run, "0123456789", 3, (Func<string>)null)
				.Block("Что делает", (Func<string>)Repair.About)
				.Heading("Карта");
			Func<string> func27 = MapMarks.Caption;
			object obj97 = <>O.<163>__Toggle;
			if (obj97 == null)
			{
				UnityAction val49 = MapMarks.Toggle;
				<>O.<163>__Toggle = val49;
				obj97 = (object)val49;
			}
			obj96.Toggle(func27, (UnityAction)obj97).Block("Что отмечается", (Func<string>)MapMarks.About);
			Corner.Add((Func<string>)Builder.CornerSays);
			_harmony = new Harmony("astvard.player");
			_harmony.PatchAll();
			Log.LogInfo((object)"Player tab registered.");
		}

		private static string Both(string first, string second)
		{
			if (string.IsNullOrEmpty(first))
			{
				return second ?? "";
			}
			if (!string.IsNullOrEmpty(second))
			{
				return first + "\n" + second;
			}
			return first;
		}

		private void Update()
		{
			Runes.Tick();
			CharacterStats.Tick();
			ZoneBook.Tick();
			ShelfNews.Tick();
			SortPass.Tick();
			StationChests.Tick();
			ChestLabels.Tick();
			ZoneHighlight.Tick();
			StationHighlight.Tick();
			Traps.Tick();
			MapMarks.Tick();
			Favorites.Tick();
		}

		private void FixedUpdate()
		{
			Traps.Hold();
		}

		private void LateUpdate()
		{
			ChestLabels.Face();
			TrapRope.Draw();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class Runes
	{
		private const float AskEvery = 10f;

		private static ZRoutedRpc _listening;

		private static ZNet _heardOn;

		private static float _askedAt = float.MinValue;

		private static int _balance;

		private static float _nextAt;

		private static bool _away;

		private static int _played;

		private static float _playedAt;

		private static ZNet _playedOn;

		private static bool Heard
		{
			get
			{
				if ((Object)(object)_heardOn != (Object)null)
				{
					return _heardOn == ZNet.instance;
				}
				return false;
			}
		}

		private static bool PlayedHeard
		{
			get
			{
				if ((Object)(object)_playedOn != (Object)null)
				{
					return _playedOn == ZNet.instance;
				}
				return false;
			}
		}

		internal static void Tick()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			ZNet instance2 = ZNet.instance;
			if (instance != null && !((Object)(object)instance2 == (Object)null) && !instance2.IsServer())
			{
				if (_listening != instance)
				{
					instance.Register<int, int, bool, bool>("Astvard_Runes", (Method<int, int, bool, bool>)OnRunes);
					instance.Register<int>("Astvard_Played", (Action<long, int>)OnPlayed);
					instance.Register<ZPackage>("Astvard_RunesGranted", (Action<long, ZPackage>)OnGranted);
					_listening = instance;
					_askedAt = float.MinValue;
				}
				if (!Heard && !((Object)(object)Player.m_localPlayer == (Object)null) && !(Time.time - _askedAt < 10f))
				{
					_askedAt = Time.time;
					instance.InvokeRoutedRPC("Astvard_AskRunes", Array.Empty<object>());
				}
			}
		}

		private static void OnRunes(long sender, int balance, int toNext, bool earned, bool away)
		{
			bool num = !Heard || balance != _balance || away != _away;
			_heardOn = ZNet.instance;
			_balance = Mathf.Max(0, balance);
			_nextAt = Time.realtimeSinceStartup + (float)Mathf.Max(0, toNext);
			_away = away;
			if (earned)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)1, "+1 руна за " + Rate(Limits.RuneMinutes.Value) + " в мире. Всего: " + _balance, 0, (Sprite)null, false);
				}
			}
			if (num)
			{
				MenuTabs.Refresh();
			}
		}

		private static void OnGranted(long sender, ZPackage pkg)
		{
			int num = default(int);
			int num2 = default(int);
			if (RuneLedger.UnpackGranted(pkg, ref num, ref num2) && num != 0)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)1, (num > 0) ? ("Админ выдал тебе " + Words.Runes(num) + ". Всего: " + num2) : ("Админ забрал у тебя " + Words.Runes(-num) + ". Осталось: " + num2), 0, (Sprite)null, false);
				}
			}
		}

		private static void OnPlayed(long sender, int seconds)
		{
			_played = Mathf.Max(0, seconds);
			_playedAt = Time.realtimeSinceStartup;
			_playedOn = ZNet.instance;
		}

		internal static string About()
		{
			if (!Heard)
			{
				return "Сервер про руны не сказал: на нём нет Astvard.Server, или это одиночная игра.";
			}
			int value = Limits.RuneIdle.Value;
			int num = Mathf.CeilToInt((_nextAt - Time.realtimeSinceStartup) / 60f);
			string text = "У тебя " + _balance + " " + Words.Plural(_balance, "руна", "руны", "рун");
			string text2 = (_away ? (text + ". Сейчас они стоят: ты " + value + " мин ничего не делал. Двинься или займись делом — счёт пойдёт снова.") : (text + ((num > 0) ? (" — следующая примерно через " + num + " мин игры.") : " — следующая вот-вот.")));
			return text2 + (PlayedHeard ? ("\nНаиграно здесь: " + Duration(Played()) + ".") : "") + "\nРуна — за " + Each(Limits.RuneMinutes.Value) + " в мире, остаток не пропадает." + ((value > 0) ? (" " + value + " мин без дела — рун нет, пока не займёшься делом.") : "");
		}

		private static double Played()
		{
			return (double)_played + (_away ? 0.0 : ((double)(Time.realtimeSinceStartup - _playedAt)));
		}

		private static string Duration(double seconds)
		{
			long num = (long)(seconds / 60.0);
			if (num < 1)
			{
				return "меньше минуты";
			}
			if (num < 60)
			{
				return num + " мин";
			}
			long num2 = num % 60;
			return num / 60 + " ч" + ((num2 > 0) ? (" " + num2 + " мин") : "");
		}

		private static string Rate(int minutes)
		{
			if (minutes == 60)
			{
				return "час";
			}
			if (minutes % 60 == 0)
			{
				return minutes / 60 + " ч";
			}
			return minutes + " мин";
		}

		private static string Each(int minutes)
		{
			return minutes switch
			{
				60 => "каждый час", 
				1 => "каждую минуту", 
				_ => "каждые " + Rate(minutes), 
			};
		}
	}
}
namespace Astvard.Players.Terrain
{
	internal static class GroundUndo
	{
		internal sealed class Step
		{
			internal readonly string Label;

			internal readonly Vector3 Centre;

			internal readonly float Reach;

			internal readonly ZNet World = ZNet.instance;

			internal readonly List<Vector2s> Zones = new List<Vector2s>();

			internal readonly List<Before> Befores = new List<Before>();

			internal readonly List<Before> Paints = new List<Before>();

			internal readonly List<ZDOID> Pieces = new List<ZDOID>();

			internal string PiecesPrefab;

			internal bool PiecesPaid;

			internal Receipt PiecesReceipt;

			internal Action Undone;

			internal Step(string label, Vector3 centre, float reach)
			{
				//IL_0045: 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)
				Label = label;
				Centre = centre;
				Reach = reach;
			}

			internal void Add(Vector2s zone, Before before)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				Add(zone, before, null);
			}

			internal void Add(Vector2s zone, Before heights, Before paint)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				Zones.Add(zone);
				Befores.Add((Before)(((object)heights) ?? ((object)new Before())));
				Paints.Add((paint != null && paint.Count > 0) ? paint : null);
			}
		}

		private const int Depth = 5;

		private static readonly List<Step> Steps = new List<Step>();

		internal static void Push(Step step)
		{
			if (step.Zones.Count != 0)
			{
				Steps.Add(step);
				while (Steps.Count > 5)
				{
					Steps.RemoveAt(0);
				}
			}
		}

		internal static void TakeBack()
		{
			Steps.RemoveAll((Step step2) => step2.World != ZNet.instance);
			if (Steps.Count == 0)
			{
				Notice.Say("Откатывать нечего");
				return;
			}
			Step step = Steps[Steps.Count - 1];
			Notice.Say(Restore(step, out var said) ?? ("Откат: " + step.Label + said));
		}

		internal static string TakeBack(Step step)
		{
			Steps.RemoveAll((Step each) => each.World != ZNet.instance);
			if (Steps.Count == 0 || Steps[Steps.Count - 1] != step)
			{
				return "Землю после неё уже меняли — откати её в «Рельефе»";
			}
			string said;
			return Restore(step, out said);
		}

		private static string Restore(Step step, out string said)
		{
			//IL_0038: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			said = "";
			if (!Ground.Ready)
			{
				return "Откат не сработает с этой версией игры — подробности в логе";
			}
			List<TerrainComp> list = new List<TerrainComp>(step.Zones.Count);
			foreach (Vector2s zone in step.Zones)
			{
				Vector3 zonePos = ZoneSystem.GetZonePos(zone);
				TerrainComp val = TerrainComp.FindTerrainCompiler(zonePos);
				if ((Object)(object)val == (Object)null)
				{
					return ((Object)(object)Heightmap.FindHeightmap(zonePos) == (Object)null) ? ("Подойди ближе к тому месту: " + step.Label + " — в " + Mathf.RoundToInt(Away(step)) + " м") : "Земля там ещё прогружается — нажми через пару секунд";
				}
				list.Add(val);
			}
			for (int i = 0; i < list.Count; i++)
			{
				if (step.Befores[i].Count > 0)
				{
					Ground.Restore(list[i], step.Befores[i], step.Centre, step.Reach);
				}
				else
				{
					Ground.Claim(list[i]);
					Ground.Note(list[i], step.Centre, step.Reach);
				}
				if (step.Paints[i] != null)
				{
					RoadPaint.Restore(list[i], step.Paints[i]);
				}
			}
			foreach (TerrainComp item in list)
			{
				Ground.Save(item);
			}
			Ground.Refresh(step.Centre, step.Reach);
			said = TakeDown(step);
			Steps.Remove(step);
			step.Undone?.Invoke();
			Plugin.Log.LogInfo((object)("Took back " + step.Label + " at " + ((object)step.Centre/*cast due to .constrained prefix*/).ToString() + " in " + list.Count + " zones" + ((step.Pieces.Count > 0) ? (", " + step.Pieces.Count + " pieces with it") : "") + "; " + Steps.Count + " steps left."));
			return null;
		}

		private static string TakeDown(Step step)
		{
			//IL_0033: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			if (step.Pieces.Count == 0 || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return "";
			}
			int num = 0;
			foreach (ZDOID piece in step.Pieces)
			{
				GameObject val = ZNetScene.instance.FindInstance(piece);
				ZNetView val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ZNetView>() : null);
				if (!((Object)(object)val2 == (Object)null) && val2.IsValid())
				{
					val2.ClaimOwnership();
					ZNetScene.instance.Destroy(val);
					num++;
				}
			}
			string text = ", факелов убрано: " + num;
			Player localPlayer = Player.m_localPlayer;
			if (!step.PiecesPaid || num == 0 || (Object)(object)localPlayer == (Object)null)
			{
				return text;
			}
			Bill bill = new Bill();
			bill.Add(step.PiecesPrefab, num);
			bill.Refund(localPlayer, step.PiecesReceipt, out var toChests, out var toFeet);
			return text + ((toChests && toFeet) ? ", материалы — в сундуки и под ноги" : (toChests ? ", материалы — в сундуки" : (toFeet ? ", материалы под ногами" : "")));
		}

		private static float Away(Step step)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_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 localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return 0f;
			}
			Vector3 position = ((Component)localPlayer).transform.position;
			Vector2 val = new Vector2(position.x - step.Centre.x, position.z - step.Centre.z);
			return ((Vector2)(ref val)).magnitude;
		}
	}
	internal static class Levelling
	{
		internal const string Digits = "0123456789";

		internal const string HeightCharacters = "0123456789-.,";

		private const float DefaultRadius = 8f;

		internal const float MaxRadius = 64f;

		private const float WardCell = 4f;

		internal const string Closed = "Ровнять землю игрокам нельзя — так решил админ";

		internal static Entry Radius;

		internal static Entry Height;

		private static Projection _hand;

		private static PadMarks _marks;

		private static bool _square;

		private static float _toldLift;

		private static string _toldHeight;

		private static int _standMask;

		internal static bool Allowed
		{
			get
			{
				if (!Access.IsAdmin)
				{
					return Rules.PlayersLevel;
				}
				return true;
			}
		}

		private static float Sea
		{
			get
			{
				if (!((Object)(object)ZoneSystem.instance != (Object)null))
				{
					return 30f;
				}
				return ZoneSystem.instance.m_waterLevel;
			}
		}

		internal static string About()
		{
			if (!Allowed)
			{
				return "Ровнять землю игрокам нельзя — так решил админ. «Откатить» возвращает своё и так.";
			}
			int value = Limits.PriceLevel.Value;
			return "Площадка встаёт в руки: земля под ней ляжет ровно на высоту поля — или на высоту, где стоишь, если оно пусто, — а вокруг сойдёт склоном.\nЛКМ — выровнять, Shift+ЛКМ — и оставить в руках, Esc — убрать, P — закрепить, стрелки — сдвинуть, Q и E — повернуть квадрат, Shift+Q и Shift+E — выше и ниже.\nИгра даёт сдвинуть землю не дальше " + 8 + " м от той, что дал мир: где не дотянется, край площадки в руках красный." + ((!Access.IsAdmin) ? ("\nРадиус игрокам — до " + Limits.Level.Value + " м") : "") + ((value > 0 && !Access.IsAdmin) ? (", цена — " + Limits.PriceLevel.Say(value)) : "");
		}

		internal static void Circle()
		{
			Hold(square: false);
		}

		internal static void Square()
		{
			Hold(square: true);
		}

		internal static void TakeBack()
		{
			GroundUndo.TakeBack();
		}

		private static void Hold(bool square)
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			if (!Allowed)
			{
				Notice.Say("Ровнять землю игрокам нельзя — так решил админ");
				return;
			}
			if (!Ground.Ready)
			{
				Notice.Say("Выравнивание не сработает с этой версией игры — подробности в логе");
				return;
			}
			_square = square;
			if (_hand == null || Projection.Current != _hand)
			{
				Projection val = Projection.Start(square ? "выравнивание квадратом" : "выравнивание кругом", 11f);
				PadMarks padMarks = PadMarks.On(val.Root);
				if (padMarks == null)
				{
					val.Drop();
					Notice.Say("Проекцию нечем нарисовать — подробности в логе");
					return;
				}
				_hand = val;
				_marks = padMarks;
				_toldLift = val.Lift;
				Entry height = Height;
				_toldHeight = ((height != null) ? height.Text : null);
				val.Place = Place;
				val.Redraw = Redraw;
			}
			_hand.NoTurning = (square ? null : "Круг вращать незачем");
			_marks.Invalidate();
			int value = Limits.PriceLevel.Value;
			Notice.Say((square ? "Выравнивание квадратом" : "Выравнивание кругом") + "\nЛКМ — выровнять, Esc — отмена, P — закрепить" + (square ? ", Q/E — поворот" : "") + "\nShift+Q/E — выше и ниже" + ((value > 0 && !Access.IsAdmin) ? ("\nЦена: " + Limits.PriceLevel.Say(value)) : ""));
		}

		private static void Redraw(Projection hand)
		{
			//IL_0038: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			Entry radius = Radius;
			float num = RadiusOf((radius != null) ? radius.Text : null);
			Entry height = Height;
			if (!TryHeight((height != null) ? height.Text : null, out var metres))
			{
				metres = 0f;
			}
			Vector3 centre = CentreOf(hand);
			float num2 = TargetOf(hand, centre, metres);
			bool flag = _marks.Draw(centre, num2, num, Pad.BlendFor(num), _square, YawOf(hand));
			if (hand.Lift == _toldLift)
			{
				Entry height2 = Height;
				if (!(((height2 != null) ? height2.Text : null) != _toldHeight))
				{
					return;
				}
			}
			_toldLift = hand.Lift;
			Entry height3 = Height;
			_toldHeight = ((height3 != null) ? height3.Text : null);
			Notice.Say("Высота площадки: " + AboveWater(num2) + (flag ? "\nМестами земля дальше 8 м от неё — ровно не выйдет" : ""));
		}

		private static void Place(Projection hand, bool keep)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if (!Allowed)
			{
				Notice.Say("Ровнять землю игрокам нельзя — так решил админ");
				return;
			}
			if (Charges.Waiting)
			{
				Notice.Say("Жду, что скажет сервер про руны");
				return;
			}
			Entry height = Height;
			if (!TryHeight((height != null) ? height.Text : null, out var metres))
			{
				Notice.Say("Высота — метры над водой, например 5\nПусто — где стоишь");
				return;
			}
			Entry radius = Radius;
			float radius2 = RadiusOf((radius != null) ? radius.Text : null);
			Vector3 centre = CentreOf(hand);
			centre.y = TargetOf(hand, centre, metres);
			bool square = _square;
			float yaw = YawOf(hand);
			string label = (square ? "площадка квадратом" : "площадка кругом");
			Charges.Pay(new Charge(label).Add(Limits.PriceLevel, 1), delegate(int receipt)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				if (Projection.Current != hand || !Level(centre, radius2, square, yaw, "выравнивание", quiet: false, out var _))
				{
					Charges.Refund(receipt, label);
				}
				else if (keep)
				{
					_marks?.Invalidate();
				}
				else
				{
					hand.Finish();
					_hand = null;
					_marks = null;
				}
			});
		}

		internal unsafe static bool Level(Vector3 target, float radius, bool square, float yaw, string label, bool quiet, out GroundUndo.Step step)
		{
			//IL_0040: 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_0071: 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_0098: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: 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)
			step = null;
			if (!Ground.Ready)
			{
				Notice.Say("Выравнивание не сработает с этой версией игры — подробности в логе");
				return false;
			}
			float num = Pad.BlendFor(radius);
			float num2 = radius + num;
			if (!Access.IsAdmin && !WardsAllow(target, num2, square, yaw))
			{
				Notice.Say("Выравнивание задевает чужой оберег — уменьши радиус или отойди");
				return false;
			}
			bool flag = default(bool);
			List<TerrainComp> list = Ground.Compilers(target, num2, square, yaw, ref flag);
			if (list == null)
			{
				Notice.Say(flag ? "Выравнивание выходит за прогруженную землю — уменьши радиус или подойди ближе" : "Земля там ещё прогружается — подожди пару секунд и нажми снова");
				return false;
			}
			float num3 = Pad.Extent(num2, square, yaw);
			GroundUndo.Step step2 = new GroundUndo.Step(label, target, num3);
			int num4 = 0;
			foreach (TerrainComp item in list)
			{
				Before val = new Before();
				num4 += Ground.Level(item, target, radius, num, square, yaw, val);
				if (val.Count > 0)
				{
					step2.Add(Ground.ZoneOf(item), val);
				}
			}
			foreach (TerrainComp item2 in list)
			{
				Ground.Save(item2);
			}
			Ground.Refresh(target, num3);
			GroundUndo.Push(step2);
			if (step2.Zones.Count > 0)
			{
				step = step2;
			}
			int num5 = Mathf.RoundToInt(radius);
			if (!quiet)
			{
				Notice.Say((square ? ("Выровнено квадратом " + num5 * 2 + "×" + num5 * 2 + " м") : ("Выровнено кругом радиусом " + num5 + " м")) + ", на " + AboveWater(target.y) + ((num4 > 0) ? "\nЗемля двигается не дальше 8 м — местами не дотянулась" : ""));
			}
			ManualLogSource log = Plugin.Log;
			string[] obj = new string[16]
			{
				"Levelled ",
				label,
				", a ",
				square ? ("square turned " + Mathf.RoundToInt(yaw) + "°") : "circle",
				": r=",
				radius.ToString(),
				", slope ",
				num.ToString("0.#", CultureInfo.InvariantCulture),
				" m, y=",
				target.y.ToString("0.##", CultureInfo.InvariantCulture),
				", ",
				step2.Zones.Count.ToString(),
				" zones at ",
				null,
				null,
				null

BepInEx/plugins/Astvard.Server.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Astvard.Core;
using Astvard.Core.Builds;
using Astvard.Core.Net;
using Astvard.Core.Placement;
using Astvard.Core.Sorting;
using Astvard.Core.Terrain;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using Steamworks;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Astvard.Server")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d8721dbc184a53c5b52947c0c2c8980b874b1ebb")]
[assembly: AssemblyProduct("Astvard.Server")]
[assembly: AssemblyTitle("Astvard.Server")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Astvard.Server
{
	internal sealed class AccessLists
	{
		[Serializable]
		private sealed class Answer
		{
			public int format;

			public string[] permitted;

			public string[] admins;
		}

		private const int Format = 1;

		private static readonly UTF8Encoding NoMark = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

		private readonly ConfigEntry<string> _url;

		private readonly ConfigEntry<string> _token;

		private readonly ConfigEntry<int> _seconds;

		private string _etag;

		private string _trouble;

		private float Interval => Mathf.Clamp(_seconds.Value, 5, 600);

		private static string Folder => Utils.GetSaveDataPath((FileSource)2);

		internal AccessLists(ConfigEntry<string> url, ConfigEntry<string> token, ConfigEntry<int> seconds)
		{
			_url = url;
			_token = token;
			_seconds = seconds;
		}

		internal IEnumerator Run()
		{
			WaitForSecondsRealtime second = new WaitForSecondsRealtime(1f);
			while ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				yield return second;
			}
			Plugin.Log.LogInfo((object)("Access lists: from " + _url.Value + " every " + Interval + " s, into " + Folder + "."));
			while (true)
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					yield return Fetch();
				}
				yield return (object)new WaitForSecondsRealtime(Interval);
			}
		}

		private IEnumerator Fetch()
		{
			string text = (_token.Value ?? "").Trim();
			if (text.Length == 0)
			{
				Trouble("no token: [Сайт] Токен in astvard.server.cfg is empty, so the lists are left as they are.");
				yield break;
			}
			UnityWebRequest request = UnityWebRequest.Get(_url.Value);
			try
			{
				request.timeout = 10;
				request.SetRequestHeader("Authorization", "Bearer " + text);
				if (_etag != null)
				{
					request.SetRequestHeader("If-None-Match", _etag);
				}
				yield return request.SendWebRequest();
				if ((int)request.result == 2)
				{
					Trouble("the site would not answer (" + request.error + "); the lists are left as they are.");
					yield break;
				}
				long responseCode = request.responseCode;
				switch (responseCode)
				{
				case 304L:
					Settled();
					yield break;
				case 401L:
					Trouble("the site turned the token down (401): [Сайт] Токен is not the site's GAME_TOKEN.");
					yield break;
				case 404L:
					Trouble("404: the address is wrong, or the site has no GAME_TOKEN of its own and so no route.");
					yield break;
				default:
					Trouble("the site answered " + responseCode + "; the lists are left as they are.");
					yield break;
				case 200L:
				{
					Answer answer;
					try
					{
						answer = JsonUtility.FromJson<Answer>(request.downloadHandler.text);
					}
					catch (Exception ex)
					{
						Trouble("the site's answer is not JSON: " + ex.Message);
						yield break;
					}
					if (answer == null || answer.format != 1 || answer.permitted == null || answer.admins == null)
					{
						Trouble("the site's answer has a form this mod does not know (format " + ((answer != null) ? answer.format.ToString() : "none") + ", this mod knows " + 1 + ").");
						yield break;
					}
					if (Apply(answer))
					{
						_etag = request.GetResponseHeader("ETag");
					}
					break;
				}
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private bool Apply(Answer answer)
		{
			int kept;
			int skipped;
			string text = AccessText.Permitted(answer.permitted, out kept, out skipped);
			int kept2;
			int skipped2;
			string text2 = AccessText.Admins(answer.admins, out kept2, out skipped2);
			bool flag;
			bool flag2;
			try
			{
				flag = Write("permittedlist.txt", text);
				flag2 = Write("adminlist.txt", text2);
			}
			catch (Exception ex)
			{
				Trouble("could not write the lists into " + Folder + ": " + ex.Message);
				return false;
			}
			Settled();
			if (skipped + skipped2 > 0)
			{
				Plugin.Log.LogWarning((object)("Access lists: " + (skipped + skipped2) + " ids from the site are not SteamID64s and were left out."));
			}
			if (!flag && !flag2)
			{
				return true;
			}
			Plugin.Log.LogInfo((object)("Access lists: the site lets in " + ((kept == 0) ? "nobody" : kept.ToString()) + " and makes " + kept2 + " admin" + ((kept2 == 1) ? "" : "s") + " — wrote " + (flag ? "permittedlist.txt" : "") + ((flag && flag2) ? " and " : "") + (flag2 ? "adminlist.txt" : "") + "." + ((kept == 0) ? " The server is closed to everyone: a list with no ids would open it." : "")));
			return true;
		}

		private static bool Write(string name, string text)
		{
			string text2 = Folder + "/" + name;
			if (File.Exists(text2) && File.ReadAllText(text2) == text)
			{
				return false;
			}
			string text3 = text2 + ".astvard-new";
			File.WriteAllText(text3, text, NoMark);
			if (File.Exists(text2))
			{
				File.Replace(text3, text2, text2 + ".bak");
			}
			else
			{
				File.Move(text3, text2);
			}
			File.SetLastWriteTimeUtc(text2, DateTime.UtcNow);
			return true;
		}

		private void Trouble(string what)
		{
			if (!(what == _trouble))
			{
				_trouble = what;
				Plugin.Log.LogWarning((object)("Access lists: " + what));
			}
		}

		private void Settled()
		{
			if (_trouble != null)
			{
				_trouble = null;
				Plugin.Log.LogInfo((object)"Access lists: the site answers again.");
			}
		}
	}
	internal static class AccessText
	{
		internal const string Nobody = "76561197960265728";

		private const ulong First = 76561197960265728uL;

		private const ulong Last = 76561202255233023uL;

		private const string Header = "// Этот файл пишет Astvard.Server по списку с сайта. Правка руками пропадёт,\n// как только он перепишет файл: при старте сервера или когда список на сайте изменится.\n";

		internal static bool IsSteamId(string id)
		{
			if (id == null || id.Length != 17)
			{
				return false;
			}
			foreach (char c in id)
			{
				if (c < '0' || c > '9')
				{
					return false;
				}
			}
			if (ulong.TryParse(id, NumberStyles.None, CultureInfo.InvariantCulture, out var result) && result > 76561197960265728L)
			{
				return result <= 76561202255233023L;
			}
			return false;
		}

		internal static string Permitted(IEnumerable<string> ids, out int kept, out int skipped)
		{
			StringBuilder stringBuilder = new StringBuilder("// Этот файл пишет Astvard.Server по списку с сайта. Правка руками пропадёт,\n// как только он перепишет файл: при старте сервера или когда список на сайте изменится.\n");
			kept = Lines(stringBuilder, ids, out skipped, "V_");
			if (kept == 0)
			{
				stringBuilder.Append("// На сайте не пущен никто. Файл без номеров игра читает как «пускать всех»,\n");
				stringBuilder.Append("// поэтому здесь номер, которого нет ни у кого: аккаунт 0.\n");
				stringBuilder.Append("V_").Append("76561197960265728").Append('\n');
			}
			return stringBuilder.ToString();
		}

		internal static string Admins(IEnumerable<string> ids, out int kept, out int skipped)
		{
			StringBuilder stringBuilder = new StringBuilder("// Этот файл пишет Astvard.Server по списку с сайта. Правка руками пропадёт,\n// как только он перепишет файл: при старте сервера или когда список на сайте изменится.\n");
			kept = Lines(stringBuilder, ids, out skipped, "V_", "Steam_");
			if (kept == 0)
			{
				stringBuilder.Append("// Админов в игре на сайте нет.\n");
			}
			return stringBuilder.ToString();
		}

		private static int Lines(StringBuilder text, IEnumerable<string> ids, out int skipped, params string[] prefixes)
		{
			HashSet<string> hashSet = new HashSet<string>();
			int num = 0;
			skipped = 0;
			if (ids == null)
			{
				return 0;
			}
			foreach (string id in ids)
			{
				if (!IsSteamId(id))
				{
					skipped++;
				}
				else if (hashSet.Add(id))
				{
					num++;
					foreach (string value in prefixes)
					{
						text.Append(value).Append(id).Append('\n');
					}
				}
			}
			return num;
		}
	}
	internal sealed class BuildPause
	{
		private readonly PlayerRules _rules;

		private readonly Dictionary<string, float> _lastBuild = new Dictionary<string, float>();

		private ZRoutedRpc _listening;

		internal BuildPause(PlayerRules rules)
		{
			_rules = rules;
		}

		internal void Tick()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != _listening)
			{
				_listening = instance;
				instance.Register<int>("Astvard_AskBuild", (Action<long, int>)OnAsk);
			}
		}

		private void OnAsk(long sender, int ask)
		{
			string who;
			bool flag = Senders.FromAdmin(out who);
			string text = Senders.SenderHost();
			float num = (float)_rules.LimitOf(Limits.BuildPause) * 60f;
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			int num2 = 0;
			if (!flag && num > 0f && !string.IsNullOrEmpty(text) && _lastBuild.TryGetValue(text, out var value))
			{
				num2 = Mathf.Max(0, Mathf.CeilToInt(value + num - realtimeSinceStartup));
			}
			bool flag2 = num2 == 0;
			if (flag2 && !flag && !string.IsNullOrEmpty(text))
			{
				_lastBuild[text] = realtimeSinceStartup;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "Astvard_BuildAnswer", new object[3] { ask, flag2, num2 });
			Plugin.Log.LogInfo((object)("Build asked by " + who + (flag ? " (admin)" : "") + ": " + (flag2 ? "yes" : ("no, " + num2 + " s of the pause left")) + "."));
		}
	}
	internal static class Causeway
	{
		internal struct Stretch
		{
			internal int First;

			internal int Last;
		}

		private const float Least = 0.05f;

		internal static float[] Raise(IList<float> ground, float step, float floor)
		{
			int count = ground.Count;
			float[] array = new float[count];
			bool flag = false;
			for (int i = 0; i < count; i++)
			{
				array[i] = float.MinValue;
				if (!(ground[i] >= floor))
				{
					array[i] = floor;
					flag = true;
				}
			}
			if (!flag)
			{
				return null;
			}
			if (step > 0f)
			{
				float num = 0.35f * step;
				for (int j = 1; j < count; j++)
				{
					if (array[j - 1] > float.MinValue)
					{
						array[j] = Math.Max(array[j], array[j - 1] - num);
					}
				}
				for (int num2 = count - 2; num2 >= 0; num2--)
				{
					if (array[num2 + 1] > float.MinValue)
					{
						array[num2] = Math.Max(array[num2], array[num2 + 1] - num);
					}
				}
			}
			float[] array2 = new float[count];
			for (int k = 0; k < count; k++)
			{
				array2[k] = Math.Max(ground[k], array[k]);
			}
			return array2;
		}

		internal static List<Stretch> Spans(IList<float> ground, IList<float> raised)
		{
			List<Stretch> list = new List<Stretch>();
			int num = Math.Min(ground.Count, raised.Count);
			int i = 0;
			while (i < num)
			{
				if (raised[i] - ground[i] < 0.05f)
				{
					i++;
					continue;
				}
				int num2 = i;
				for (; i + 1 < num && raised[i + 1] - ground[i + 1] >= 0.05f; i++)
				{
				}
				int num3 = i;
				i++;
				num2 = Math.Max(0, num2 - 1);
				num3 = Math.Min(num - 1, num3 + 1);
				if (list.Count > 0 && num2 <= list[list.Count - 1].Last + 1)
				{
					Stretch value = list[list.Count - 1];
					value.Last = num3;
					list[list.Count - 1] = value;
				}
				else
				{
					list.Add(new Stretch
					{
						First = num2,
						Last = num3
					});
				}
			}
			return list;
		}
	}
	internal sealed class Charges
	{
		private sealed class Paid
		{
			internal string Name;

			internal string Label;

			internal int Cost;

			internal float At;
		}

		private const float KeepFor = 600f;

		private readonly PlayerRules _rules;

		private readonly Runes _runes;

		private readonly Dictionary<string, Paid> _paid = new Dictionary<string, Paid>();

		private ZRoutedRpc _listening;

		internal Charges(PlayerRules rules, Runes runes)
		{
			_rules = rules;
			_runes = runes;
		}

		internal void Tick()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != _listening)
			{
				_listening = instance;
				instance.Register<ZPackage>("Astvard_Pay", (Action<long, ZPackage>)OnPay);
				instance.Register<int>("Astvard_Refund", (Action<long, int>)OnRefund);
			}
		}

		private void OnPay(long sender, ZPackage pkg)
		{
			long num = Senders.SenderPeer();
			string text = Senders.SenderHost();
			int num2 = default(int);
			string text2 = default(string);
			List<KeyValuePair<string, int>> items = default(List<KeyValuePair<string, int>>);
			if (num != 0L && !string.IsNullOrEmpty(text) && Payments.UnpackPay(pkg, ref num2, ref text2, ref items))
			{
				string text3 = Senders.SenderName();
				int num3 = CostOf(items);
				int balance;
				if (num3 == 0)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(num, "Astvard_PayAnswer", new object[4]
					{
						num2,
						true,
						0,
						_runes.BalanceOf(text, text3)
					});
				}
				else if (!_runes.Spend(num, text, text3, num3, out balance))
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(num, "Astvard_PayAnswer", new object[4] { num2, false, num3, balance });
					Plugin.Log.LogInfo((object)("Runes: «" + text3 + "» (" + text + ") has " + balance + ", «" + text2 + "» costs " + num3 + " — refused."));
				}
				else
				{
					Forget();
					_paid[KeyOf(text, num2)] = new Paid
					{
						Name = text3,
						Label = text2,
						Cost = num3,
						At = Time.realtimeSinceStartup
					};
					ZRoutedRpc.instance.InvokeRoutedRPC(num, "Astvard_PayAnswer", new object[4] { num2, true, num3, balance });
					Plugin.Log.LogInfo((object)("Runes: «" + text3 + "» (" + text + ") paid " + num3 + " for «" + text2 + "», now " + balance + "."));
				}
			}
		}

		private void OnRefund(long sender, int ask)
		{
			long num = Senders.SenderPeer();
			string text = Senders.SenderHost();
			if (num == 0L || string.IsNullOrEmpty(text))
			{
				return;
			}
			string key = KeyOf(text, ask);
			if (_paid.TryGetValue(key, out var value))
			{
				_paid.Remove(key);
				if (!(Time.realtimeSinceStartup - value.At > 600f))
				{
					_runes.Give(num, text, value.Cost);
					Plugin.Log.LogInfo((object)("Runes: " + value.Cost + " back to «" + value.Name + "» (" + text + ") for «" + value.Label + "»: it did not happen."));
				}
			}
		}

		private int CostOf(List<KeyValuePair<string, int>> items)
		{
			long num = 0L;
			foreach (KeyValuePair<string, int> item in items)
			{
				if (item.Key.StartsWith("build:", StringComparison.Ordinal))
				{
					num += ServerBuildCost(item.Key.Substring("build:".Length), item.Value);
					continue;
				}
				Limit val = Limits.PriceOf(item.Key);
				if (val != null)
				{
					num += (long)_rules.LimitOf(val) * (long)item.Value;
				}
			}
			return (int)Math.Min(num, 2147483647L);
		}

		private long ServerBuildCost(string name, int pieces)
		{
			Heading val = SharedFolder.FindByName(SharedBuilds.Folder, name);
			if (val != null)
			{
				int? num = SharedFolder.Price(val);
				if (num.HasValue)
				{
					return num.Value;
				}
				pieces = val.Pieces;
			}
			return _rules.LimitOf(Limits.PriceBuild) + (long)_rules.LimitOf(Limits.PriceBuild100) * (long)((pieces + 99) / 100);
		}

		private void Forget()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			List<string> list = null;
			foreach (KeyValuePair<string, Paid> item in _paid)
			{
				if (realtimeSinceStartup - item.Value.At > 600f)
				{
					(list ?? (list = new List<string>())).Add(item.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (string item2 in list)
			{
				_paid.Remove(item2);
			}
		}

		private static string KeyOf(string id, int ask)
		{
			return id + "/" + ask.ToString(CultureInfo.InvariantCulture);
		}
	}
	internal static class GameWords
	{
		internal static Dictionary<string, string> In(string language)
		{
			LocalizationSettings val = Resources.Load<LocalizationSettings>("LocalizationSettings");
			List<TextAsset> list = (((Object)(object)val != (Object)null) ? val.Localizations : null);
			if (list == null || list.Count == 0)
			{
				return null;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			bool flag = false;
			foreach (TextAsset item in list)
			{
				if ((Object)(object)item != (Object)null && Read(item.text, language, dictionary))
				{
					flag = true;
				}
			}
			if (!flag)
			{
				return null;
			}
			return dictionary;
		}

		internal static bool Read(string text, string language, Dictionary<string, string> words)
		{
			if (string.IsNullOrEmpty(text))
			{
				return false;
			}
			int num = text.IndexOf('\n');
			if (num < 0)
			{
				return false;
			}
			int column = -1;
			string[] array = text.Substring(0, num).TrimEnd(new char[1] { '\r' }).Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				if (Bare(array[i]) == language)
				{
					column = i;
					break;
				}
			}
			if (column < 0)
			{
				return false;
			}
			StringBuilder key = new StringBuilder();
			StringBuilder english = new StringBuilder();
			StringBuilder wanted = new StringBuilder();
			int field = 0;
			bool flag = false;
			for (int j = num + 1; j <= text.Length; j++)
			{
				bool flag2 = j == text.Length;
				char c = (flag2 ? '\n' : text[j]);
				if (!flag2 && c == '"')
				{
					if (flag && j + 1 < text.Length && text[j + 1] == '"')
					{
						Keep('"');
						j++;
					}
					else
					{
						flag = !flag;
					}
				}
				else if (!flag2 && c == ',' && !flag)
				{
					field++;
				}
				else if (flag2 || (c == '\n' && !flag))
				{
					Row();
				}
				else
				{
					Keep(c);
				}
			}
			return true;
			void Keep(char symbol)
			{
				if (field == 0)
				{
					key.Append(symbol);
				}
				else if (field == 1)
				{
					english.Append(symbol);
				}
				if (field == column)
				{
					wanted.Append(symbol);
				}
			}
			void Row()
			{
				string text2 = key.ToString();
				if (text2.Length > 0 && !text2.StartsWith("//", StringComparison.Ordinal) && field >= column)
				{
					string text3 = wanted.ToString().Trim();
					if (text3.Length == 0)
					{
						text3 = english.ToString();
					}
					words[text2] = text3;
				}
				key.Length = 0;
				english.Length = 0;
				wanted.Length = 0;
				field = 0;
			}
		}

		private static string Bare(string name)
		{
			name = name.Trim();
			if (name.Length < 2 || name[0] != '"' || name[name.Length - 1] != '"')
			{
				return name;
			}
			return name.Substring(1, name.Length - 2);
		}
	}
	internal static class Json
	{
		internal sealed class Writer
		{
			private readonly StringBuilder _json = new StringBuilder();

			private readonly Stack<char> _open = new Stack<char>();

			private bool _first = true;

			internal int Length => _json.Length;

			internal Writer Object(string name = null)
			{
				return Open(name, '{', '}');
			}

			internal Writer Array(string name)
			{
				return Open(name, '[', ']');
			}

			internal Writer End()
			{
				_json.Append(_open.Pop());
				_first = false;
				return this;
			}

			internal Writer Field(string name, string value)
			{
				Item(name);
				Quote(_json, value);
				return this;
			}

			internal Writer Field(string name, int value)
			{
				Item(name);
				_json.Append(value.ToString(CultureInfo.InvariantCulture));
				return this;
			}

			internal Writer Field(string name, long value)
			{
				Item(name);
				_json.Append(value.ToString(CultureInfo.InvariantCulture));
				return this;
			}

			internal Writer Field(string name, bool value)
			{
				Item(name);
				_json.Append(value ? "true" : "false");
				return this;
			}

			internal Writer Field(string name, double value)
			{
				Item(name);
				bool flag = !double.IsNaN(value) && !double.IsInfinity(value);
				_json.Append(flag ? value.ToString("0.###", CultureInfo.InvariantCulture) : "0");
				return this;
			}

			internal Writer Element(string value)
			{
				return Field(null, value);
			}

			public override string ToString()
			{
				return _json.ToString();
			}

			private Writer Open(string name, char opens, char closes)
			{
				Item(name);
				_json.Append(opens);
				_open.Push(closes);
				_first = true;
				return this;
			}

			private void Item(string name)
			{
				if (!_first)
				{
					_json.Append(',');
				}
				_first = false;
				if (name != null)
				{
					Quote(_json, name);
					_json.Append(':');
				}
			}
		}

		private sealed class Reader
		{
			private const int Deepest = 32;

			private readonly string _text;

			private int _at;

			internal Reader(string text)
			{
				_text = text;
			}

			internal object Whole()
			{
				object result = Value(0);
				Space();
				if (_at < _text.Length)
				{
					throw Wrong("nothing after the value");
				}
				return result;
			}

			private object Value(int depth)
			{
				Space();
				if (_at >= _text.Length)
				{
					throw Wrong("a value");
				}
				return _text[_at] switch
				{
					'{' => ReadObject(depth + 1), 
					'[' => ReadArray(depth + 1), 
					'"' => ReadString(), 
					't' => Word("true", true), 
					'f' => Word("false", false), 
					'n' => Word("null", null), 
					_ => ReadNumber(), 
				};
			}

			private Dictionary<string, object> ReadObject(int depth)
			{
				if (depth > 32)
				{
					throw Wrong("no more than " + 32 + " levels");
				}
				Dictionary<string, object> dictionary = new Dictionary<string, object>();
				_at++;
				Space();
				if (Take('}'))
				{
					return dictionary;
				}
				do
				{
					Space();
					if (_at >= _text.Length || _text[_at] != '"')
					{
						throw Wrong("a name in quotes");
					}
					string key = ReadString();
					Space();
					if (!Take(':'))
					{
						throw Wrong("':'");
					}
					dictionary[key] = Value(depth);
					Space();
				}
				while (Take(','));
				if (!Take('}'))
				{
					throw Wrong("',' or '}'");
				}
				return dictionary;
			}

			private List<object> ReadArray(int depth)
			{
				if (depth > 32)
				{
					throw Wrong("no more than " + 32 + " levels");
				}
				List<object> list = new List<object>();
				_at++;
				Space();
				if (Take(']'))
				{
					return list;
				}
				do
				{
					list.Add(Value(depth));
					Space();
				}
				while (Take(','));
				if (!Take(']'))
				{
					throw Wrong("',' or ']'");
				}
				return list;
			}

			private string ReadString()
			{
				_at++;
				StringBuilder stringBuilder = new StringBuilder();
				while (true)
				{
					if (_at >= _text.Length)
					{
						throw Wrong("the end of the string");
					}
					char c = _text[_at++];
					if (c == '"')
					{
						return stringBuilder.ToString();
					}
					if (c < ' ')
					{
						throw Wrong("no control characters in a string");
					}
					if (c != '\\')
					{
						stringBuilder.Append(c);
						continue;
					}
					if (_at >= _text.Length)
					{
						break;
					}
					switch (_text[_at++])
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
					{
						if (_at + 4 > _text.Length || !int.TryParse(_text.Substring(_at, 4), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var result))
						{
							throw Wrong("four hex digits after \\u");
						}
						stringBuilder.Append((char)result);
						_at += 4;
						break;
					}
					default:
						_at--;
						throw Wrong("an escape JSON has");
					}
				}
				throw Wrong("an escape");
			}

			private object ReadNumber()
			{
				int at = _at;
				while (_at < _text.Length && "-+.0123456789eE".IndexOf(_text[_at]) >= 0)
				{
					_at++;
				}
				string text = _text.Substring(at, _at - at);
				if (text.Length == 0)
				{
					throw Wrong("a value");
				}
				if (long.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result))
				{
					return result;
				}
				if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
				{
					return result2;
				}
				_at = at;
				throw Wrong("a number");
			}

			private object Word(string word, object value)
			{
				if (_at + word.Length > _text.Length || string.CompareOrdinal(_text, _at, word, 0, word.Length) != 0)
				{
					throw Wrong("a value");
				}
				_at += word.Length;
				return value;
			}

			private bool Take(char c)
			{
				if (_at >= _text.Length || _text[_at] != c)
				{
					return false;
				}
				_at++;
				return true;
			}

			private void Space()
			{
				while (_at < _text.Length && (_text[_at] == ' ' || _text[_at] == '\t' || _text[_at] == '\n' || _text[_at] == '\r'))
				{
					_at++;
				}
			}

			private FormatException Wrong(string wanted)
			{
				return new FormatException("expected " + wanted + " at character " + _at);
			}
		}

		internal static object Read(string text)
		{
			return new Reader(text ?? "").Whole();
		}

		internal static void Quote(StringBuilder json, string text)
		{
			json.Append('"');
			string text2 = text ?? "";
			foreach (char c in text2)
			{
				switch (c)
				{
				case '"':
					json.Append("\\\"");
					continue;
				case '\\':
					json.Append("\\\\");
					continue;
				case '\n':
					json.Append("\\n");
					continue;
				case '\r':
					json.Append("\\r");
					continue;
				case '\t':
					json.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder = json.Append("\\u");
					int num = c;
					stringBuilder.Append(num.ToString("x4", CultureInfo.InvariantCulture));
				}
				else
				{
					json.Append(c);
				}
			}
			json.Append('"');
		}
	}
	internal sealed class NetLights<TKey>
	{
		private struct Paving
		{
			internal int Piece;

			internal float Ax;

			internal float Az;

			internal float Bx;

			internal float Bz;

			internal float Half;
		}

		private sealed class Lamp
		{
			internal int Piece;

			internal bool Road;

			internal float X;

			internal float Z;

			internal TKey Key;

			internal bool Gone;
		}

		private const float Cell = 32f;

		internal const float MostSlack = 1f;

		private readonly List<Paving> _pavings = new List<Paving>();

		private readonly Dictionary<long, List<int>> _pavingCells = new Dictionary<long, List<int>>();

		private readonly List<Lamp> _lamps = new List<Lamp>();

		private readonly Dictionary<long, List<int>> _lampCells = new Dictionary<long, List<int>>();

		internal int Standing { get; private set; }

		internal void AddRoad(int piece, IList<float> xs, IList<float> zs, float half)
		{
			int num = Math.Min(xs.Count, zs.Count);
			switch (num)
			{
			case 0:
				return;
			case 1:
				AddDisc(piece, xs[0], zs[0], half);
				return;
			}
			for (int i = 0; i + 1 < num; i++)
			{
				AddPaving(piece, xs[i], zs[i], xs[i + 1], zs[i + 1], half);
			}
		}

		internal void AddDisc(int piece, float x, float z, float radius)
		{
			AddPaving(piece, x, z, x, z, radius);
		}

		internal void AddLamp(int piece, bool road, float x, float z, TKey key)
		{
			int count = _lamps.Count;
			_lamps.Add(new Lamp
			{
				Piece = piece,
				Road = road,
				X = x,
				Z = z,
				Key = key
			});
			Into(_lampCells, KeyOf(CellOf(x), CellOf(z)), count);
			Standing++;
		}

		internal bool OnPaving(int piece, float x, float z, float slack)
		{
			slack = Math.Max(0f, Math.Min(slack, 1f));
			if (!_pavingCells.TryGetValue(KeyOf(CellOf(x), CellOf(z)), out var value))
			{
				return false;
			}
			foreach (int item in value)
			{
				Paving paving = _pavings[item];
				if (paving.Piece != piece)
				{
					float num = paving.Half + slack;
					if (SegmentSq(x, z, paving.Ax, paving.Az, paving.Bx, paving.Bz) < num * num)
					{
						return true;
					}
				}
			}
			return false;
		}

		internal bool NearLamp(int piece, float x, float z, float reach)
		{
			int num = CellOf(x - reach);
			int num2 = CellOf(x + reach);
			int num3 = CellOf(z - reach);
			int num4 = CellOf(z + reach);
			for (int i = num3; i <= num4; i++)
			{
				for (int j = num; j <= num2; j++)
				{
					if (!_lampCells.TryGetValue(KeyOf(j, i), out var value))
					{
						continue;
					}
					foreach (int item in value)
					{
						Lamp lamp = _lamps[item];
						if (!lamp.Gone && lamp.Piece != piece)
						{
							float num5 = lamp.X - x;
							float num6 = lamp.Z - z;
							if (num5 * num5 + num6 * num6 < reach * reach)
							{
								return true;
							}
						}
					}
				}
			}
			return false;
		}

		internal List<TKey> Take(int piece, IList<float> xs, IList<float> zs, float reach, bool roadsOnly)
		{
			List<TKey> list = new List<TKey>();
			int num = Math.Min(xs.Count, zs.Count);
			for (int i = 0; i < num; i++)
			{
				float num2 = ((i + 1 < num) ? xs[i + 1] : xs[i]);
				float num3 = ((i + 1 < num) ? zs[i + 1] : zs[i]);
				if (i + 1 >= num && num > 1)
				{
					break;
				}
				int num4 = CellOf(Math.Min(xs[i], num2) - reach);
				int num5 = CellOf(Math.Max(xs[i], num2) + reach);
				int num6 = CellOf(Math.Min(zs[i], num3) - reach);
				int num7 = CellOf(Math.Max(zs[i], num3) + reach);
				for (int j = num6; j <= num7; j++)
				{
					for (int k = num4; k <= num5; k++)
					{
						if (!_lampCells.TryGetValue(KeyOf(k, j), out var value))
						{
							continue;
						}
						foreach (int item in value)
						{
							Lamp lamp = _lamps[item];
							if (!lamp.Gone && lamp.Piece != piece && (!roadsOnly || lamp.Road) && !(SegmentSq(lamp.X, lamp.Z, xs[i], zs[i], num2, num3) >= reach * reach))
							{
								lamp.Gone = true;
								Standing--;
								list.Add(lamp.Key);
							}
						}
					}
				}
			}
			return list;
		}

		private void AddPaving(int piece, float ax, float az, float bx, float bz, float half)
		{
			int count = _pavings.Count;
			_pavings.Add(new Paving
			{
				Piece = piece,
				Ax = ax,
				Az = az,
				Bx = bx,
				Bz = bz,
				Half = half
			});
			float num = half + 1f;
			int num2 = CellOf(Math.Min(ax, bx) - num);
			int num3 = CellOf(Math.Max(ax, bx) + num);
			int num4 = CellOf(Math.Min(az, bz) - num);
			int num5 = CellOf(Math.Max(az, bz) + num);
			for (int i = num4; i <= num5; i++)
			{
				for (int j = num2; j <= num3; j++)
				{
					Into(_pavingCells, KeyOf(j, i), count);
				}
			}
		}

		private static void Into(Dictionary<long, List<int>> cells, long key, int index)
		{
			if (!cells.TryGetValue(key, out var value))
			{
				value = (cells[key] = new List<int>());
			}
			value.Add(index);
		}

		private static int CellOf(float value)
		{
			return (int)Math.Floor(value / 32f);
		}

		private static long KeyOf(int cx, int cz)
		{
			return ((long)cx << 32) | (uint)cz;
		}

		private static float SegmentSq(float x, float z, float ax, float az, float bx, float bz)
		{
			float num = bx - ax;
			float num2 = bz - az;
			float num3 = num * num + num2 * num2;
			float num4 = ((num3 > 1E-08f) ? Math.Max(0f, Math.Min(1f, ((x - ax) * num + (z - az) * num2) / num3)) : 0f);
			float num5 = x - (ax + num * num4);
			float num6 = z - (az + num2 * num4);
			return num5 * num5 + num6 * num6;
		}
	}
	internal sealed class PlaceFinder
	{
		private ZRoutedRpc _listening;

		internal void Tick()
		{
			ZNet instance = ZNet.instance;
			ZRoutedRpc instance2 = ZRoutedRpc.instance;
			if (!((Object)(object)instance == (Object)null) && instance2 != null && instance.IsServer() && instance2 != _listening)
			{
				_listening = instance2;
				instance2.Register<ZPackage>("Astvard_AskPlaces", (Action<long, ZPackage>)OnAsk);
			}
		}

		private static void OnAsk(long sender, ZPackage package)
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			long num = Senders.SenderPeer();
			if (num == 0L)
			{
				return;
			}
			string text;
			try
			{
				text = PlaceList.UnpackAsk(package);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Places: an ask from " + Senders.SenderName() + " would not read: " + ex.Message));
				return;
			}
			if (text == null)
			{
				return;
			}
			if (!Senders.FromAdmin(out var who))
			{
				Plugin.Log.LogWarning((object)("Places: " + who + " asked where every " + text + " is and is no admin — refused."));
				ZRoutedRpc.instance.InvokeRoutedRPC(num, "Astvard_Places", new object[1] { PlaceList.PackAnswer((Outcome)1, text, (IList<Vector2>)null) });
				return;
			}
			List<Vector2> list = new List<Vector2>();
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance != (Object)null)
			{
				foreach (LocationInstance value in instance.m_locationInstances.Values)
				{
					ZoneLocation location = value.m_location;
					if (location != null && !(((!string.IsNullOrEmpty(location.m_prefabName)) ? location.m_prefabName : location.m_name) != text))
					{
						list.Add(new Vector2(value.m_position.x, value.m_position.z));
						if (list.Count >= 4000)
						{
							break;
						}
					}
				}
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(num, "Astvard_Places", new object[1] { PlaceList.PackAnswer((Outcome)0, text, (IList<Vector2>)list) });
			Plugin.Log.LogInfo((object)("Places: " + list.Count + " " + text + " in this world, told to " + who + "."));
		}
	}
	internal sealed class PlayerRules
	{
		private enum Outcome
		{
			Changed,
			Same,
			Refused
		}

		private readonly Dictionary<string, ConfigEntry<bool>> _switches = new Dictionary<string, ConfigEntry<bool>>();

		private readonly Dictionary<string, ConfigEntry<int>> _limits = new Dictionary<string, ConfigEntry<int>>();

		private ZRoutedRpc _listening;

		internal bool PlayersShare => _switches["players_share"].Value;

		internal event Action<string, string> ChangedInGame;

		internal event Action Changed;

		internal PlayerRules(ConfigEntry<bool> playersPay, ConfigEntry<bool> playersChests, ConfigEntry<bool> playersShare, ConfigEntry<bool> playersLevel, ConfigEntry<bool> playersRoad, ConfigEntry<bool> playersClear, ConfigEntry<bool> playersRepairPay, ConfigFile config)
		{
			_switches["players_pay"] = playersPay;
			_switches["players_chests"] = playersChests;
			_switches["players_share"] = playersShare;
			_switches["players_level"] = playersLevel;
			_switches["players_road"] = playersRoad;
			_switches["players_clear"] = playersClear;
			_switches["players_repair_pay"] = playersRepairPay;
			Limit[] all = Limits.All;
			foreach (Limit val in all)
			{
				Dictionary<string, ConfigEntry<int>> limits = _limits;
				string key = val.Key;
				string text = val.Group;
				string configKey = val.ConfigKey;
				int num = val.Default;
				string[] obj = new string[13]
				{
					val.Title, ", ", val.Unit, ": от ", null, null, null, null, null, null,
					null, null, null
				};
				int min = val.Min;
				obj[4] = min.ToString();
				obj[5] = " до ";
				min = val.Max;
				obj[6] = min.ToString();
				obj[7] = ((val.Zero != null) ? ("; 0 — " + val.Zero) : "");
				obj[8] = ". ";
				obj[9] = val.About;
				obj[10] = " Меняется и из игры: «АДМИНКА» → «";
				obj[11] = val.Group;
				obj[12] = "», и на сайте.";
				limits[key] = config.Bind<int>(text, configKey, num, string.Concat(obj));
			}
		}

		internal int LimitOf(Limit limit)
		{
			if (!_limits.TryGetValue(limit.Key, out var value))
			{
				return limit.Default;
			}
			return Mathf.Clamp(value.Value, limit.Min, limit.Max);
		}

		internal void Tick()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != _listening)
			{
				_listening = instance;
				instance.Register("Astvard_AskRules", (Action<long>)OnAsk);
				instance.Register<string, string>("Astvard_SetRule", (Action<long, string, string>)OnSet);
				Plugin.Log.LogInfo((object)("Rules for players: " + Describe() + "."));
			}
		}

		internal Dictionary<string, string> Values()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (KeyValuePair<string, ConfigEntry<bool>> @switch in _switches)
			{
				dictionary[@switch.Key] = (@switch.Value.Value ? "1" : "0");
			}
			Limit[] all = Limits.All;
			foreach (Limit val in all)
			{
				dictionary[val.Key] = LimitOf(val).ToString(CultureInfo.InvariantCulture);
			}
			return dictionary;
		}

		internal bool Apply(IEnumerable<KeyValuePair<string, string>> values, string from)
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, string> value in values)
			{
				string said;
				string why;
				switch (Set(value.Key, value.Value, out said, out why))
				{
				case Outcome.Changed:
					list.Add(value.Key + " " + value.Value);
					break;
				case Outcome.Refused:
					if (why != null)
					{
						Plugin.Log.LogWarning((object)("Rule from " + from + ": " + why));
					}
					break;
				}
			}
			if (list.Count == 0)
			{
				return false;
			}
			Plugin.Log.LogInfo((object)("Rules for players changed by " + from + " (" + string.Join(", ", list) + "): " + Describe() + "."));
			Tell();
			return true;
		}

		private string Describe()
		{
			List<string> list = new List<string>();
			Limit[] all = Limits.All;
			foreach (Limit val in all)
			{
				list.Add(val.Key + " " + LimitOf(val));
			}
			return (_switches["players_pay"].Value ? "players pay for their builds" : "players build free") + (_switches["players_chests"].Value ? " (from the bag and the chests nearby)" : " (from the bag only)") + (_switches["players_share"].Value ? ", may put builds on the server" : ", may not put builds on the server") + (_switches["players_level"].Value ? ", may level the ground" : ", may not level the ground") + ((!_switches["players_road"].Value) ? ", may not lay roads" : (_switches["players_clear"].Value ? ", may lay roads clearing their way" : ", may lay roads")) + (_switches["players_repair_pay"].Value ? ", and pay for fuel when mending" : ", and get fuel free when mending") + "; limits: " + string.Join(", ", list);
		}

		private ZPackage Packet()
		{
			Dictionary<string, string> dictionary = Values();
			dictionary["server_build"] = Build.Here;
			return Rules.Pack((IDictionary<string, string>)dictionary);
		}

		private void Tell()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				instance.InvokeRoutedRPC(0L, "Astvard_Rules", new object[1] { Packet() });
			}
			this.Changed?.Invoke();
		}

		private void OnAsk(long sender)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "Astvard_Rules", new object[1] { Packet() });
		}

		private void OnSet(long sender, string key, string value)
		{
			if (!Senders.FromAdmin(out var who))
			{
				Plugin.Log.LogWarning((object)("Rule " + key + " = " + value + " asked by " + who + ", who is not an admin — refused."));
				return;
			}
			string said;
			string why;
			Outcome outcome = Set(key, value, out said, out why);
			if (outcome == Outcome.Refused)
			{
				Plugin.Log.LogWarning((object)("Rule asked by " + who + ": " + why));
				return;
			}
			Plugin.Log.LogInfo((object)("Rules for players changed by " + who + ": " + Describe() + "."));
			Tell();
			if (outcome == Outcome.Changed)
			{
				this.ChangedInGame?.Invoke(key, said);
			}
		}

		private Outcome Set(string key, string value, out string said, out string why)
		{
			said = null;
			why = null;
			if (key == null)
			{
				why = "a rule with no key — refused.";
				return Outcome.Refused;
			}
			if (_switches.TryGetValue(key, out var value2))
			{
				if (value != "1" && value != "0")
				{
					why = key + " = " + value + " is neither 1 nor 0 — refused.";
					return Outcome.Refused;
				}
				said = value;
				bool flag = value == "1";
				if (value2.Value == flag)
				{
					return Outcome.Same;
				}
				value2.Value = flag;
				return Outcome.Changed;
			}
			Limit[] all = Limits.All;
			foreach (Limit val in all)
			{
				if (!(val.Key != key))
				{
					int? num = Rules.ReadLimit(val, value);
					if (!num.HasValue)
					{
						string[] obj = new string[8] { key, " = ", value, " is not a whole number from ", null, null, null, null };
						int min = val.Min;
						obj[4] = min.ToString();
						obj[5] = " to ";
						min = val.Max;
						obj[6] = min.ToString();
						obj[7] = " — refused.";
						why = string.Concat(obj);
						return Outcome.Refused;
					}
					said = num.Value.ToString(CultureInfo.InvariantCulture);
					if (LimitOf(val) == num.Value)
					{
						return Outcome.Same;
					}
					_limits[key].Value = num.Value;
					return Outcome.Changed;
				}
			}
			why = key + " is not a rule this server knows — refused.";
			return Outcome.Refused;
		}
	}
	internal sealed class PlayerStats
	{
		private sealed class Entry
		{
			internal string Id;

			internal string Name = "";

			internal Sheet Sheet = new Sheet();
		}

		private const string FileName = "astvard-stats.txt";

		private const string Header = "# astvard stats 1: id, health, stamina, eitr, carry weight, name; -1 is the game's own";

		private const int LongestName = 64;

		private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture;

		private readonly Dictionary<string, Entry> _entries = new Dictionary<string, Entry>();

		private readonly HashSet<long> _asked = new HashSet<long>();

		private readonly Runes _runes;

		private ZRoutedRpc _listening;

		private bool _loaded;

		private string _troubleWriting;

		private static string FilePath => Path.Combine(Paths.ConfigPath, "astvard-stats.txt");

		internal PlayerStats(Runes runes)
		{
			_runes = runes;
		}

		internal string Describe()
		{
			if (!_loaded)
			{
				Load();
			}
			return "Player stats: " + _entries.Count + " player(s) with numbers set in astvard-stats.txt; an admin sets them in «Персонаж», and a player's own client lays them on the character.";
		}

		internal void Tick()
		{
			ZNet instance = ZNet.instance;
			ZRoutedRpc instance2 = ZRoutedRpc.instance;
			if (!((Object)(object)instance == (Object)null) && instance2 != null && instance.IsServer() && instance2 != _listening)
			{
				_listening = instance2;
				instance2.Register("Astvard_AskMyStats", (Action<long>)OnAskMine);
				instance2.Register("Astvard_AskStatsLedger", (Action<long>)OnAskLedger);
				instance2.Register<ZPackage>("Astvard_SetStats", (Action<long, ZPackage>)OnSet);
				_asked.Clear();
				if (!_loaded)
				{
					Load();
				}
			}
		}

		private void OnAskMine(long sender)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			string text = Senders.SenderHost();
			long num = Senders.SenderPeer();
			if (string.IsNullOrEmpty(text) || num == 0L)
			{
				return;
			}
			_asked.Add(num);
			Entry entry = Find(text);
			if (entry != null)
			{
				string text2 = Clean(Senders.SenderName());
				if (text2.Length > 0 && text2 != entry.Name)
				{
					entry.Name = text2;
					Save();
				}
			}
			Tell(num, (Sheet)((entry != null) ? ((object)entry.Sheet) : ((object)new Sheet())));
		}

		private void OnAskLedger(long sender)
		{
			long num = Senders.SenderPeer();
			if (num == 0L)
			{
				return;
			}
			if (!Senders.FromAdmin(out var who))
			{
				Plugin.Log.LogWarning((object)("Player stats: " + who + " asked for the list of players and is no admin — refused."));
				ZRoutedRpc.instance.InvokeRoutedRPC(num, "Astvard_StatsLedger", new object[1] { StatSheets.PackLedger((Outcome)1, (IList<Row>)null) });
				return;
			}
			List<Row> list = Ledger(num);
			if (list.Count > 500)
			{
				Plugin.Log.LogWarning((object)("Player stats: the list for " + who + " is one package: " + (list.Count - 500) + " players did not fit in it."));
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(num, "Astvard_StatsLedger", new object[1] { StatSheets.PackLedger((Outcome)0, (IList<Row>)list) });
		}

		private List<Row> Ledger(long asker)
		{
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Expected O, but got Unknown
			//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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Expected O, but got Unknown
			if (!_loaded)
			{
				Load();
			}
			List<Row> list = new List<Row>();
			HashSet<string> hashSet = new HashSet<string>();
			HashSet<long> hashSet2 = new HashSet<long>();
			foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers())
			{
				if (connectedPeer != null)
				{
					hashSet2.Add(connectedPeer.m_uid);
					ISocket socket = connectedPeer.m_socket;
					string text = ((socket != null) ? socket.GetHostName() : null);
					if (!string.IsNullOrEmpty(text) && !((ZDOID)(ref connectedPeer.m_characterID)).IsNone() && hashSet.Add(text))
					{
						Entry entry = Find(text);
						string text2 = Clean(connectedPeer.m_playerName);
						list.Add(new Row
						{
							Id = text,
							Name = ((text2.Length > 0) ? text2 : (entry?.Name ?? "")),
							Online = true,
							Modded = _asked.Contains(connectedPeer.m_uid),
							You = (connectedPeer.m_uid == asker),
							Sheet = (Sheet)((entry != null) ? ((object)entry.Sheet.Copy()) : ((object)new Sheet()))
						});
					}
				}
			}
			_asked.IntersectWith(hashSet2);
			foreach (Entry value in _entries.Values)
			{
				if (!hashSet.Contains(value.Id))
				{
					list.Add(new Row
					{
						Id = value.Id,
						Name = value.Name,
						Sheet = value.Sheet.Copy()
					});
				}
			}
			list.Sort(delegate(Row a, Row b)
			{
				if (a.Online != b.Online)
				{
					if (!a.Online)
					{
						return 1;
					}
					return -1;
				}
				int num = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase);
				return (num == 0) ? string.CompareOrdinal(a.Id, b.Id) : num;
			});
			return list;
		}

		private void OnSet(long sender, ZPackage pkg)
		{
			long num = Senders.SenderPeer();
			string text = default(string);
			Sheet val = default(Sheet);
			if (num == 0L || !StatSheets.UnpackSet(pkg, ref text, ref val))
			{
				return;
			}
			if (!Senders.FromAdmin(out var who))
			{
				Plugin.Log.LogWarning((object)("Player stats: " + who + " tried to set the numbers of " + text + " and is no admin — refused."));
				Answer(num, (Outcome)1, text, null, null, told: false);
				return;
			}
			if (string.IsNullOrEmpty(text) || text.Length > 32 || !StatSheets.Valid(val))
			{
				Plugin.Log.LogWarning((object)("Player stats: " + who + " sent numbers out of bounds for " + text + " — refused."));
				Answer(num, (Outcome)2, text, null, null, told: false);
				return;
			}
			if (!_loaded)
			{
				Load();
			}
			ZNetPeer val2 = PeerOf(text);
			Entry entry = Find(text);
			string text2 = Clean(val2?.m_playerName);
			if (text2.Length == 0)
			{
				text2 = entry?.Name ?? _runes.NameOf(text) ?? "";
			}
			if (val.Any)
			{
				if (entry == null)
				{
					entry = new Entry
					{
						Id = text
					};
					_entries[text] = entry;
				}
				entry.Sheet = val.Copy();
				if (text2.Length > 0)
				{
					entry.Name = text2;
				}
			}
			else
			{
				_entries.Remove(text);
			}
			Save();
			if (val2 != null)
			{
				Tell(val2.m_uid, val);
			}
			bool flag = val2 != null && _asked.Contains(val2.m_uid);
			Plugin.Log.LogInfo((object)("Player stats: " + who + " set «" + text2 + "» (" + text + ") to " + StatSheets.Describe(val) + ((val2 == null) ? ", not on now." : (flag ? ", told." : ", on without the part that lays them."))));
			Answer(num, (Outcome)0, text, text2, val, flag);
		}

		private static void Answer(long to, Outcome outcome, string id, string name, Sheet sheet, bool told)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc.instance.InvokeRoutedRPC(to, "Astvard_SetStatsAnswer", new object[1] { StatSheets.PackAnswer(outcome, id, name, sheet, told) });
		}

		private static void Tell(long peer, Sheet sheet)
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				instance.InvokeRoutedRPC(peer, "Astvard_MyStats", new object[1] { StatSheets.PackSheet(sheet) });
			}
		}

		private Entry Find(string id)
		{
			if (!_loaded)
			{
				Load();
			}
			if (id == null || !_entries.TryGetValue(id, out var value))
			{
				return null;
			}
			return value;
		}

		private static ZNetPeer PeerOf(string id)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			foreach (ZNetPeer connectedPeer in instance.GetConnectedPeers())
			{
				if (connectedPeer?.m_socket != null && connectedPeer.m_socket.GetHostName() == id)
				{
					return connectedPeer;
				}
			}
			return null;
		}

		private static string Clean(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return "";
			}
			string text = name.Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ')
				.Trim();
			if (text.Length <= 64)
			{
				return text;
			}
			return text.Substring(0, 64);
		}

		private void Load()
		{
			//IL_007d: 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)
			//IL_0091: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			_loaded = true;
			_entries.Clear();
			try
			{
				if (!File.Exists(FilePath))
				{
					return;
				}
				int num = 0;
				string[] array = File.ReadAllLines(FilePath, Encoding.UTF8);
				foreach (string text in array)
				{
					if (!text.StartsWith("#") && text.Trim().Length != 0)
					{
						string[] array2 = text.Split(new char[1] { '\t' });
						Sheet val = ((array2.Length < 5) ? ((Sheet)null) : new Sheet
						{
							Hp = Number(array2[1]),
							Stamina = Number(array2[2]),
							Eitr = Number(array2[3]),
							Carry = Number(array2[4])
						});
						string text2 = array2[0].Trim();
						if (text2.Length == 0 || val == null || !StatSheets.Valid(val) || !val.Any)
						{
							num++;
							continue;
						}
						_entries[text2] = new Entry
						{
							Id = text2,
							Name = ((array2.Length > 5) ? Clean(array2[5]) : ""),
							Sheet = val
						};
					}
				}
				if (num > 0)
				{
					Plugin.Log.LogWarning((object)("Player stats: " + num + " line(s) of astvard-stats.txt are not a player's numbers — left out."));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Player stats: could not read astvard-stats.txt: " + ex.Message));
			}
		}

		private static int Number(string text)
		{
			if (!int.TryParse(text.Trim(), NumberStyles.Integer, Invariant, out var result))
			{
				return int.MinValue;
			}
			return result;
		}

		private void Save()
		{
			try
			{
				List<string> list = new List<string> { "# astvard stats 1: id, health, stamina, eitr, carry weight, name; -1 is the game's own" };
				foreach (Entry value in _entries.Values)
				{
					Sheet sheet = value.Sheet;
					list.Add(value.Id + "\t" + sheet.Hp.ToString(Invariant) + "\t" + sheet.Stamina.ToString(Invariant) + "\t" + sheet.Eitr.ToString(Invariant) + "\t" + sheet.Carry.ToString(Invariant) + "\t" + value.Name);
				}
				string text = FilePath + ".tmp";
				File.WriteAllLines(text, list.ToArray(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				if (File.Exists(FilePath))
				{
					File.Replace(text, FilePath, null);
				}
				else
				{
					File.Move(text, FilePath);
				}
				_troubleWriting = null;
			}
			catch (Exception ex)
			{
				if (!(_troubleWriting == ex.Message))
				{
					_troubleWriting = ex.Message;
					Plugin.Log.LogError((object)("Player stats: could not write astvard-stats.txt: " + ex.Message));
				}
			}
		}
	}
	[BepInPlugin("astvard.server", "Astvard Server", "2.0.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("valheim_server.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal const string Guid = "astvard.server";

		internal const string Title = "Astvard Server";

		private Harmony _harmony;

		private PlayerRules _rules;

		private SharedBuilds _builds;

		private BuildPause _pause;

		private Runes _runes;

		private Charges _charges;

		private RuneGrants _grants;

		private PlayerStats _stats;

		private readonly PlaceFinder _places = new PlaceFinder();

		private SharedWeather _weather;

		private SharedWind _wind;

		private SharedTime _time;

		private SortZones _zones;

		private SortShelves _shelves;

		private WorldRates _worldRates;

		private RoadJobs _roads;

		private RoadNet _roadNet;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_rules = new PlayerRules(((BaseUnityPlugin)this).Config.Bind<bool>("Постройки игроков", "Платно", true, "Игроки ставят постройки мода за ресурсы из сумки (true) или даром (false). Админ меняет это и из игры: «АДМИНКА» → «Настройки»."), ((BaseUnityPlugin)this).Config.Bind<bool>("Постройки игроков", "Из сундуков", true, "Чего нет в сумке, платная постройка игрока добирает из сундуков рядом, которые он открыл бы сам (true), или берёт только из сумки (false). Как далеко — [Пределы игроков] Сундуки рядом. Меняется и из игры: «АДМИНКА» → «Настройки», и на сайте."), ((BaseUnityPlugin)this).Config.Bind<bool>("Постройки игроков", "Выкладывать", false, "Игроки могут выкладывать свои постройки на сервер (true): они приходят скрытыми, и открывает их игрокам админ. Меняется и из игры: «АДМИНКА» → «Настройки»."), ((BaseUnityPlugin)this).Config.Bind<bool>("Рельеф игроков", "Выравнивание", true, "Игроки могут ровнять землю «Рельефом» и под своими постройками (true) или ровняет только админ (false). Как далеко — [Пределы игроков] Выравнивание. Меняется и из игры: «АДМИНКА» → «Настройки», и на сайте."), ((BaseUnityPlugin)this).Config.Bind<bool>("Рельеф игроков", "Дорожки", true, "Игроки могут класть дорожки и мостить круги в «Дорожках» (true) или кладёт только админ (false). Как далеко — [Пределы игроков] Дорожка и Мощение. Меняется и из игры: «АДМИНКА» → «Настройки», и на сайте."), ((BaseUnityPlugin)this).Config.Bind<bool>("Рельеф игроков", "Снос", false, "Дорожка игрока сносит деревья, пни, брёвна и голые камни на пути (true) или убирает только подлесок (false). Снесённое откат не вернёт. Меняется и из игры: «АДМИНКА» → «Настройки», и на сайте."), ((BaseUnityPlugin)this).Config.Bind<bool>("Функции игроков", "Починка платно", true, "«Починить» у игроков берёт топливо для факелов, костров и печей из сумки (true) или заправляет даром (false). Чинит всегда даром, как молот. Меняется и из игры: «АДМИНКА» → «Настройки»."), ((BaseUnityPlugin)this).Config);
			_builds = new SharedBuilds(() => _rules.PlayersShare);
			_pause = new BuildPause(_rules);
			_runes = new Runes(_rules);
			_charges = new Charges(_rules, _runes);
			_grants = new RuneGrants(_runes);
			_stats = new PlayerStats(_runes);
			_weather = new SharedWeather();
			_wind = new SharedWind();
			_time = new SharedTime();
			_zones = new SortZones(_rules, _runes);
			_shelves = new SortShelves();
			_worldRates = new WorldRates(_rules);
			WorldSeed.Bind(((BaseUnityPlugin)this).Config);
			SteamListing.Bind(((BaseUnityPlugin)this).Config);
			_roads = new RoadJobs((MonoBehaviour)(object)this);
			_roadNet = new RoadNet((MonoBehaviour)(object)this, _roads);
			_harmony = new Harmony("astvard.server");
			_harmony.PatchAll();
			Log.LogInfo((object)Throttle.Describe());
			Log.LogInfo((object)RuneGrants.Describe());
			Log.LogInfo((object)_stats.Describe());
			Log.LogInfo((object)SharedWeather.Describe());
			Log.LogInfo((object)SharedWind.Describe());
			Log.LogInfo((object)SharedTime.Describe());
			Log.LogInfo((object)_zones.Describe());
			Log.LogInfo((object)_shelves.Describe());
			Log.LogInfo((object)WorldRates.Describe());
			Log.LogInfo((object)WorldSeed.Describe());
			Log.LogInfo((object)SteamListing.Describe());
			Log.LogInfo((object)RoadJobs.Describe());
			Log.LogInfo((object)RoadNet.Describe());
			ConfigEntry<string> val = ((BaseUnityPlugin)this).Config.Bind<string>("Сайт", "Адрес", "http://127.0.0.1:3002/api/game/access", "Где сайт отдаёт списки допуска и админов (GET, договор — в AstvardTwo/site/CLAUDE.md). Правила игроков ходят рядом: …/access, замененный на …/rules.");
			ConfigEntry<string> token = ((BaseUnityPlugin)this).Config.Bind<string>("Сайт", "Токен", "", "GAME_TOKEN из .env сайта. Пусто — списки и правила на сервере с сайтом не сверяются.");
			ConfigEntry<int> seconds = ((BaseUnityPlugin)this).Config.Bind<int>("Сайт", "Секунд", 15, "Как часто спрашивать сайт, от 5 до 600. Игра сама перечитывает списки раз в 10 секунд.");
			((MonoBehaviour)this).StartCoroutine(new AccessLists(val, token, seconds).Run());
			((MonoBehaviour)this).StartCoroutine(new SiteRules(_rules, val, token, seconds).Run());
			((MonoBehaviour)this).StartCoroutine(new SiteRunes(_runes, val, token, seconds).Run());
			((MonoBehaviour)this).StartCoroutine(new SiteBuilds(val, token, seconds).Run());
			((MonoBehaviour)this).StartCoroutine(new SiteZones(_zones, val, token, seconds).Run());
			((MonoBehaviour)this).StartCoroutine(new SiteShelves(_shelves, val, token, seconds).Run());
			((MonoBehaviour)this).StartCoroutine(new SiteStatus(val, token, seconds).Run());
			Log.LogInfo((object)"Astvard Server 2.0.2 loaded.");
		}

		private void Update()
		{
			_rules.Tick();
			_builds.Tick();
			_pause.Tick();
			_runes.Tick();
			_charges.Tick();
			_grants.Tick();
			_stats.Tick();
			_places.Tick();
			_weather.Tick();
			_wind.Tick();
			_time.Tick();
			_zones.Tick();
			_shelves.Tick();
			_worldRates.Tick();
			_roads.Tick();
			_roadNet.Tick();
		}

		private void OnApplicationQuit()
		{
			_runes?.Save();
		}

		private void OnDestroy()
		{
			RoadJobs.Shutdown();
			_runes?.Save();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal sealed class RoadJobs
	{
		internal sealed class NetPiece
		{
			internal string Label = "Road net";

			internal readonly List<float> X = new List<float>();

			internal readonly List<float> Z = new List<float>();

			internal bool Circle;

			internal float Radius;

			internal Order Order;

			internal NetLights<ZDOID> Lights;

			internal int Index;

			internal bool Over;

			internal string Failure;

			internal bool Stopped;

			internal float Laid;

			internal int Doused;

			internal int Spared;

			internal int Cleared;

			internal int Placed;

			internal int Skipped;

			internal int Bends;

			internal int Unbent;

			internal NetPiece FromRing;

			internal NetPiece ToRing;

			internal bool Trimmed;

			internal float WetMetres;

			internal float MostFill;

			internal float Raised;

			internal int Bridges;

			internal float BridgeMetres;
		}

		private sealed class Job
		{
			internal int Id;

			internal string Label;

			internal NetPiece Net;

			internal bool Circle;

			internal long Asker;

			internal string Who;

			internal Order Order;

			internal readonly List<float> X = new List<float>();

			internal readonly List<float> Z = new List<float>();

			internal float Half;

			internal float Blend;

			internal Color Paint;

			internal bool Stop;

			internal Record Record;

			internal readonly HashSet<Vector2s> Zones = new HashSet<Vector2s>();

			internal float NextPost;

			internal float Laid;

			internal int Cleared;

			internal int Painted;

			internal int ShortOf;

			internal int Placed;

			internal int Skipped;

			internal int Spared;

			internal int Bends;

			internal int Unbent;

			internal int Incomplete;

			internal float Raised;

			internal int Bridges;

			internal float BridgeMetres;

			internal int BridgeParts;

			internal int Islands;

			internal List<Causeway.Stretch> Spanned;

			internal float Reach => Half + Blend;
		}

		private sealed class Record
		{
			internal int Id;

			internal bool Running = true;

			internal readonly Dictionary<ZDOID, byte[]> Before = new Dictionary<ZDOID, byte[]>();

			internal readonly List<ZDOID> Torches = new List<ZDOID>();

			internal Vector3 End;
		}

		private sealed class Span
		{
			internal Vector3 Start;

			internal Quaternion Facing;

			internal readonly List<Piece> Parts = new List<Piece>();
		}

		private sealed class Crossing
		{
			internal readonly List<Span> Bridges = new List<Span>();

			internal readonly List<Causeway.Stretch> Islands = new List<Causeway.Stretch>();
		}

		private const int Piece = 100;

		private const float BridgeLeast = 4f;

		private const float BridgeMost = 64f;

		private const int MostSpans = 4;

		private const float Margin = 64f;

		private const float Patience = 60f;

		private const int Kept = 4;

		private const int NewZonesARound = 2;

		internal const float RingMost = 32f;

		internal const float PlaceRoom = 4f;

		private const float ApartShare = 0.55f;

		private const float PavingSlack = 0.3f;

		private static readonly WaitForSeconds Quarter = new WaitForSeconds(0.25f);

		private static readonly MethodInfo PokeMethod = AccessTools.Method(typeof(ZoneSystem), "PokeLocalZone", new Type[1] { typeof(Vector2s) }, (Type[])null);

		private static readonly MethodInfo CheckLoadMethod = AccessTools.Method(typeof(TerrainComp), "CheckLoad", (Type[])null, (Type[])null);

		private static Job _job;

		private static Func<Vector2s, bool> _poke;

		private static ZoneSystem _pokeFor;

		private static float _pokedAt = float.MinValue;

		private static readonly List<ZDO> Records = new List<ZDO>();

		private static readonly HashSet<ZDO> Near = new HashSet<ZDO>();

		private readonly MonoBehaviour _host;

		private readonly List<Record> _kept = new List<Record>();

		private ZRoutedRpc _listening;

		internal static bool Busy => _job != null;

		internal RoadJobs(MonoBehaviour host)
		{
			_host = host;
		}

		internal static string Describe()
		{
			return "Long roads: an admin's road past " + 100 + " m is laid here, " + 100 + " m at a time, up to " + 10000 + " m" + ((PokeMethod != null) ? ", its ground loaded by the server." : "; ZoneSystem.PokeLocalZone is gone, so they are not laid.");
		}

		internal void Tick()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != _listening)
			{
				_listening = instance;
				instance.Register<ZPackage>("Astvard_LongRoad", (Action<long, ZPackage>)OnOrder);
				instance.Register<int>("Astvard_LongRoadStop", (Action<long, int>)OnStop);
				instance.Register<int>("Astvard_LongRoadUndo", (Action<long, int>)OnUndo);
			}
		}

		internal static void Shutdown()
		{
			if (_job != null)
			{
				_job.Stop = true;
			}
		}

		internal static void StopNet()
		{
			if (_job?.Net != null)
			{
				_job.Stop = true;
			}
		}

		internal bool Take(NetPiece piece)
		{
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			if (_job != null || piece == null || piece.Order == null || piece.X.Count == 0 || piece.X.Count != piece.Z.Count)
			{
				return false;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if (PokeFor(instance) == null || !instance.LocationsGenerated || !Ground.Ready)
			{
				return false;
			}
			Order order = piece.Order;
			float num = (piece.Circle ? Mathf.Clamp(piece.Radius, 1f, 32f) : Mathf.Max(order.Width * 0.5f, 0.75f));
			Job job = new Job
			{
				Id = Random.Range(1, int.MaxValue),
				Label = piece.Label,
				Net = piece,
				Circle = piece.Circle,
				Asker = 0L,
				Who = "the road net",
				Order = order,
				Half = num,
				Blend = (order.Smooth ? RoadLevel.BlendFor(num) : 0f),
				Paint = (order.Paved ? Heightmap.m_paintMaskPaved : Heightmap.m_paintMaskDirt),
				NextPost = order.Spacing * 0.5f
			};
			job.X.AddRange(piece.X);
			job.Z.AddRange(piece.Z);
			job.Record = new Record
			{
				Id = job.Id
			};
			_job = job;
			_host.StartCoroutine(Run(job));
			return true;
		}

		private void OnOrder(long sender, ZPackage package)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0392: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0397: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ca: Unknown result type (might be due to invalid IL or missing references)
			long num = Senders.SenderPeer();
			Order val;
			try
			{
				val = Order.Read(package);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Long road: an order from " + Senders.SenderName() + " would not read: " + ex.Message));
				return;
			}
			if (!Senders.FromAdmin(out var who))
			{
				Plugin.Log.LogWarning((object)("Long road asked by " + who + ", who is not an admin — refused."));
				Tell(num, val?.Id ?? 0, (News)5, "Длинные дорожки сервер кладёт только админу");
				return;
			}
			if (val == null)
			{
				Tell(num, 0, (News)5, "У тебя мод другой версии: сервер твою дорожку не прочтёт");
				return;
			}
			if (_job != null || RoadNet.Laying)
			{
				Tell(num, val.Id, (News)5, RoadNet.Laying ? "Сервер кладёт сеть дорог — дождись её или останови в «АДМИНКЕ» → «Сеть дорог»" : "Сервер уже кладёт дорожку — дождись её или останови");
				return;
			}
			ZoneSystem instance = ZoneSystem.instance;
			if (PokeFor(instance) == null || !instance.LocationsGenerated || !Ground.Ready)
			{
				Tell(num, val.Id, (News)5, "Сервер сейчас не может подгружать землю — см. его лог");
				return;
			}
			if (!val.Finite())
			{
				Plugin.Log.LogWarning((object)("Long road " + val.Id + " from " + who + " carried a non-number."));
				Tell(num, val.Id, (News)5, "Дорожка пришла битой — отметь её заново");
				return;
			}
			float num2 = Flat(val.From, val.To);
			if (num2 < 1f || num2 > 10000f)
			{
				Tell(num, val.Id, (News)5, "Дорожка " + Metres(num2) + " м — сервер кладёт от 1 до " + Metres(10000f) + " м");
				return;
			}
			val.Bend = Mathf.Clamp(val.Bend, -10f, 10f);
			val.Width = Mathf.Clamp(val.Width, 1f, 8f);
			val.Spacing = Mathf.Clamp(val.Spacing, 4f, 50f);
			if (val.Torch < 0 || val.Torch >= Torches.Prefabs.Length)
			{
				val.Torch = 0;
			}
			RoadLine val2 = RoadLine.Curve(val.From.x, val.From.z, val.To.x, val.To.z, val.Bend, 1f);
			if (val2.Count < 2)
			{
				Tell(num, val.Id, (News)5, "Точки слишком близко");
				return;
			}
			float num3 = Mathf.Max(val.Width * 0.5f, 0.75f);
			Job job = new Job
			{
				Id = val.Id,
				Label = "Long road " + val.Id,
				Asker = num,
				Who = who,
				Order = val,
				Half = num3,
				Blend = (val.Smooth ? RoadLevel.BlendFor(num3) : 0f),
				Paint = (val.Paved ? Heightmap.m_paintMaskPaved : Heightmap.m_paintMaskDirt),
				NextPost = val.Spacing * 0.5f
			};
			for (int i = 0; i < val2.Count; i++)
			{
				job.X.Add(val2.XAt(i));
				job.Z.Add(val2.ZAt(i));
			}
			job.Record = new Record
			{
				Id = val.Id
			};
			_kept.Add(job.Record);
			int num4 = 0;
			while (num4 < _kept.Count && _kept.Count > 4)
			{
				if (_kept[num4].Running)
				{
					num4++;
				}
				else
				{
					_kept.RemoveAt(num4);
				}
			}
			_job = job;
			_host.StartCoroutine(Run(job));
			Tell(num, job.Id, (News)0, "Сервер взялся за дорожку " + Metres(val2.Length) + " м: кладёт по " + 100 + " м, землю подгружает сам. Можно заниматься своим", val.From, 0f, val2.Length);
			Plugin.Log.LogInfo((object)(job.Label + " from " + who + ": " + val2.Length.ToString("0.0", CultureInfo.InvariantCulture) + " m from " + ((Vector3)(ref val.From)).ToString("F1") + " to " + ((Vector3)(ref val.To)).ToString("F1") + ", width " + val.Width.ToString("0.#", CultureInfo.InvariantCulture) + ", bend " + val.Bend.ToString("0.#", CultureInfo.InvariantCulture) + (val.Paved ? ", stone" : ", dirt") + (val.Smooth ? ", smoothed" : "") + (val.Clear ? ", clearing" : ", undergrowth only") + (val.Round ? ", going round" : "") + ((val.Torch > 0) ? (", torches " + Torches.Prefabs[val.Torch] + " every " + Metres(val.Spacing) + " m" + (val.Forever ? ", everlasting" : "")) : "") + ", " + val2.Count + " points."));
		}

		private static void OnStop(long sender, int id)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			long num = Senders.SenderPeer();
			if (!Senders.FromAdmin(out var who))
			{
				Plugin.Log.LogWarning((object)("Long road " + id + ": a stop asked by " + who + ", who is not an admin — refused."));
			}
			else if (_job != null && _job.Id == id)
			{
				_job.Asker = num;
				_job.Stop = true;
				Plugin.Log.LogInfo((object)("Long road " + id + ": stop asked by " + who + "."));
			}
			else
			{
				Tell(num, id, (News)3, "Эта дорожка уже не кладётся");
			}
		}

		private void OnUndo(long sender, int id)
		{
			//IL_006a: 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_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			long num = Senders.SenderPeer();
			if (!Senders.FromAdmin(out var who))
			{
				Plugin.Log.LogWarning((object)("Long road " + id + ": taking back asked by " + who + ", who is not an admin — refused."));
				Tell(num, id, (News)7, "Откатывать дорожки сервера может только админ");
				return;
			}
			if (_job != null)
			{
				if (_job.Id == id)
				{
					_job.Asker = num;
					Tell(num, id, (News)8, "Эта дорожка ещё кладётся — «Остановить укладку», потом откатывай");
				}
				else
				{
					Tell(num, id, (News)7, "Сервер сейчас кладёт другую дорожку — откати, когда он закончит");
				}
				return;
			}
			Record record = _kept.Find((Record r) => r.Id == id);
			if (record == null)
			{
				Tell(num, id, (News)6, "Сервер уже не помнит эту дорожку — откатить нечего");
				return;
			}
			_kept.Remove(record);
			long sessionID = ZDOMan.GetSessionID();
			int num2 = 0;
			int num3 = 0;
			foreach (KeyValuePair<ZDOID, byte[]> item in record.Before)
			{
				ZDO zDO = ZDOMan.instance.GetZDO(item.Key);
				if (zDO == null)
				{
					num3++;
					continue;
				}
				zDO.SetOwner(sessionID);
				zDO.Set(ZDOVars.s_TCData, item.Value);
				num2++;
			}
			int num4 = 0;
			foreach (ZDOID torch in record.Torches)
			{
				ZDO zDO2 = ZDOMan.instance.GetZDO(torch);
				if (zDO2 == null)
				{
					continue;
				}
				GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(torch) : null);
				if ((Object)(object)val != (Object)null)
				{
					ZNetView component = val.GetComponent<ZNetView>();
					if (component != null)
					{
						component.ClaimOwnership();
					}
					ZNetScene.instance.Destroy(val);
				}
				else
				{
					zDO2.SetOwner(sessionID);
					ZDOMan.instance.DestroyZDO(zDO2);
				}
				num4++;
			}
			Tell(num, id, (News)6, "Откат: дорожка сервера — " + num2 + " зон" + ((num4 > 0) ? (", факелов убрано: " + num4) : "") + ((num3 > 0) ? (", " + num3 + " зон уже нет") : ""), record.End);
			Plugin.Log.LogInfo((object)("Long road " + id + " taken back by " + who + ": " + num2 + " zones written back, " + num3 + " gone, " + num4 + " torches taken down."));
		}

		private static void Tell(long peer, int id, News news, string text, Vector3 end = default(Vector3), float laid = 0f, float total = 0f)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (peer != 0L && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(peer, "Astvard_LongRoadNews", new object[1] { LongRoads.Say(id, news, text, end, laid, total) });
			}
		}

		private IEnumerator Run(Job job)
		{
			float began = Time.realtimeSinceStartup;
			string failure = null;
			int laidTo = 0;
			int pieces = 0;
			int lastStart = Math.Max(1, job.X.Count - 1);
			try
			{
				if (job.Order.Round && !job.Circle)
				{
					RoundPlaces(job);
				}
				int first = 0;
				while (first < lastStart && !job.Stop && !((Object)(object)ZNet.instance == (Object)null))
				{
					int last = Math.Min(first + 100, job.X.Count - 1);
					int ahead = Math.Min(first + 200, job.X.Count - 1);
					if (job.Order.Bridge && !job.Circle)
					{
						last = PastWater(job, last, ahead);
					}
					float pieceBegan = Time.realtimeSinceStartup;
					SetZones(job, first, ahead);
					float deadline = Time.time + 60f;
					while (!HasGround(job) && !job.Stop && Time.time < deadline && (Object)(object)ZNet.instance != (Object)null)
					{
						yield return Quarter;
					}
					if (job.Stop || (Object)(object)ZNet.instance == (Object)null)
					{
						break;
					}
					if (!HasGround(job))
					{
						failure = "земля не подгрузилась за минуту";
						break;
					}
					deadline = Time.time + 60f;
					while ((Pending(job) > 0 || !Settled(job)) && !job.Stop && Time.time < deadline && (Object)(object)ZNet.instance != (Object)null)
					{
						yield return Quarter;
					}
					if (job.Stop || (Object)(object)ZNet.instance == (Object)null)
					{
						break;
					}
					bool complete = Pending(job) == 0 && Settled(job);
					if (!complete)
					{
						job.Incomplete++;
						Plugin.Log.LogWarning((object)(job.Label + ": " + Pending(job) + " objects still not built after a minute; the piece goes without clearing, going round and torches."));
					}
					yield return null;
					yield return null;
					if (job.Stop || (Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null)
					{
						break;
					}
					if (complete && job.Order.Round && !job.Circle)
					{
						RoundAhead(job, first, ahead, last);
					}
					if (complete && job.Circle)
					{
						Grow(job);
					}
					RoadLine piece = LineOf(job, first, last);
					failure = Lay(job, piece, complete);
					if (failure != null)
					{
						break;
					}
					laidTo = last;
					pieces++;
					job.Laid += piece.Length;
					NetLights<ZDOID> lights = job.Net?.Lights;
					if (lights != null)
					{
						if (job.Circle)
						{
							lights.AddDisc(job.Net.Index, job.X[0], job.Z[0], job.Half);
						}
						else
						{
							lights.AddRoad(job.Net.Index, XsOf(piece), ZsOf(piece), job.Half);
						}
					}
					if (job.Order.Torch > 0)
					{
						yield return null;
						if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
						{
							break;
						}
						if (job.Circle)
						{
							if (lights != null)
							{
								float[] xs = new float[1] { job.X[0] };
								float[] zs = new float[1] { job.Z[0] };
								job.Net.Doused += Douse(lights.Take(job.Net.Index, xs, zs, job.Half + 0.6f + Apart(job), roadsOnly: true));
								job.Net.Doused += Douse(lights.Take(job.Net.Index, xs, zs, job.Half + 0.3f, roadsOnly: false));
							}
							List<Post> list = RoadLine.RingPosts(job.X[0], job.Z[0], job.Half + 0.6f, job.Order.Spacing);
							if (complete)
							{
								Light(job, list);
							}
							else
							{
								job.Skipped += list.Count;
							}
						}
						else
						{
							if (lights != null)
							{
								job.Net.Doused += Douse(lights.Take(job.Net.Index, XsOf(piece), ZsOf(piece), job.Half + 0.3f, roadsOnly: false));
							}
							List<Post> list2 = piece.EdgePostsFrom(job.NextPost, job.Order.Spacing, job.Half + 0.6f, ref job.NextPost);
							if (job.Spanned != null && job.Spanned.Count > 0)
							{
								list2 = OffBridges(piece, list2, job.Spanned);
							}
							if (complete)
							{
								Light(job, list2);
							}
							else
							{
								job.Skipped += list2.Count;
							}
						}
					}
					float num = LengthOf(job);
					Plugin.Log.LogInfo((object)(job.Label + ": " + (job.Circle ? ("circle r=" + job.Half.ToString("0.#", CultureInfo.InvariantCulture) + " m laid, ") : (Metres(job.Laid) + " of " + Metres(num) + " m laid, ")) + job.Zones.Count + " zones, " + (Time.realtimeSinceStartup - pieceBegan).ToString("0.0", CultureInfo.InvariantCulture) + " s."));
					Tell(job.Asker, job.Id, (News)1, "Сервер кладёт дорожку: " + Metres(job.Laid) + " из " + Metres(num) + " м", PointAt(job, last), job.Laid, num);
					if (last <= first)
					{
						break;
					}
					first = last;
				}
			}
			finally
			{
				job.Zones.Clear();
				job.Record.Running = false;
				job.Record.End = PointAt(job, laidTo);
				if (_job == job)
				{
					_job = null;
				}
				if (job.Net != null)
				{
					Hand(job, failure, pieces, laidTo);
				}
			}
			float num2 = LengthOf(job);
			string text = ((job.Order.Smooth && job.Laid > 0f) ? ", сглажена" : "") + ((job.Raised > 0f) ? (", поднята над водой: " + Metres(job.Raised) + " м") : "") + ((job.Bridges > 0) ? (", мостов: " + job.Bridges + " (" + Metres(job.BridgeMetres) + " м)" + ((job.Islands > 0) ? (", островов между ними: " + job.Islands) : "")) : "") + ((job.Cleared > 0) ? (", снесено: " + job.Cleared) : "") + ((job.Order.Torch > 0) ? (", факелов: " + job.Placed + ((job.Skipped > 0) ? (", " + job.Skipped + " некуда поставить") : "")) : "") + ((job.Bends > 0) ? (", обойдено: " + job.Bends) : "") + ((job.Unbent > 0) ? (", обойти не вышло: " + job.Unbent) : "") + ((job.Incomplete > 0) ? (", кусков без сноса и факелов: " + job.Incomplete + " — там не догрузились объекты") : "") + ((job.ShortOf > 0) ? "\nземля двигается не дальше 8 м — местами не дотянулась" : "");
			Vector3 end = PointAt(job, laidTo);
			bool flag = failure == null && pieces > 0 && laidTo >= job.X.Count - 1;
			if (failure != null)
			{
				Tell(job.Asker, job.Id, (News)4, "Дорожка встала на " + Metres(job.Laid) + " из " + Metres(num2) + " м: " + failure + text, end, job.Laid, num2);
			}
			else if (!flag)
			{
				Tell(job.Asker, job.Id, (News)3, "Укладка остановлена: " + Metres(job.Laid) + " из " + Metres(num2) + " м" + text, end, job.Laid, num2);
			}
			else
			{
				Tell(job.Asker, job.Id, (News)2, "Дорожка " + Metres(num2) + " м готова, ширина " + job.Order.Width.ToString("0.#", CultureInfo.InvariantCulture).Replace('.', ',') + " м" + text, end, job.Laid, num2);
			}
			Plugin.Log.LogInfo((object)(job.Label + " " + ((failure != null) ? ("failed: " + failure) : (flag ? "done" : "stopped")) + ": " + (job.Circle ? ("a circle of " + job.Half.ToString("0.#", CultureInfo.InvariantCulture) + " m, ") : (Metres(job.Laid) + " of " + Metres(num2) + " m in " + pieces + " pieces, ")) + (Time.realtimeSinceStartup - began).ToString("0", CultureInfo.InvariantCulture) + " s, " + job.Record.Before.Count + " zones, cleared " + job.Cleared + ", painted " + job.Painted + ((job.Order.Smooth || job.Raised > 0f) ? (", " + job.ShortOf + " held by the 8 m") : "") + ((job.Raised > 0f) ? (", raised over the water " + Metres(job.Raised) + " m") : "") + ((job.Bridges > 0) ? (", " + job.Bridges + " bridge(s) over " + Metres(job.BridgeMetres) + " m of water, " + job.BridgeParts + " parts" + ((job.Islands > 0) ? (", " + job.Islands + " island(s) between") : "")) : "") + ", torches " + job.Placed + " placed " + job.Skipped + " skipped" + ((job.Spared > 0) ? (", " + job.Spared + " spared beside the net's own") : "") + ((job.Net != null && job.Net.Doused > 0) ? (", " + job.Net.Doused + " of the net's put out") : "") + (job.Circle ? "" : (", went round " + job.Bends + ((job.Unbent > 0) ? (" (" + job.Unbent + " left standing)") : ""))) + "."));
		}

		private static void Hand(Job job, string failure, int pieces, int laidTo)
		{
			NetPiece net = job.Net;
			net.Failure = failure;
			net.Stopped = failure == null && (pieces == 0 || laidTo < job.X.Count - 1);
			net.Laid = job.Laid;
			net.Radius = job.Half;
			net.Cleared = job.Cleared;
			net.Placed = job.Placed;
			net.Skipped = job.Skipped;
			net.Bends = job.Bends;
			net.Unbent = job.Unbent;
			net.Spared = job.Spared;
			net.Raised = job.Raised;
			net.Bridges = job.Bridges;
			net.BridgeMetres = job.BridgeMetres;
			net.Over = true;
		}

		private static void Grow(Job job)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			Location zoneLocation = Location.GetZoneLocation(new Vector3(job.X[0], 0f, job.Z[0]));
			if (!((Object)(object)zoneLocation == (Object)null) && zoneLocation.m_noBuild)
			{
				float num = Mathf.Min((zoneLocation.m_noBuildRadiusOverride > 0f) ? zoneLocation.m_noBuildRadiusOverride : zoneLocation.GetMaxRadius(), 32f);
				if (!(num <= job.Half + 0.01f))
				{
					Plugin.Log.LogInfo((object)(job.Label + ": ring " + job.Half.ToString("0.0", CultureInfo.InvariantCulture) + " → " + num.ToString("0.0", CultureInfo.InvariantCulture) + " m, to the no-build edge of " + ((Object)zoneLocation).name + "."));
					job.Half = num;
					job.Blend = (job.Order.Smooth ? RoadLevel.BlendFor(num) : 0f);
				}
			}
		}

		private static int Douse(List<ZDOID> lamps)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			ZDOMan instance2 = ZDOMan.instance;
			if (lamps.Count == 0 || (Object)(object)instance == (Object)null || instance2 == null)
			{
				return 0;
			}
			long sessionID = ZDOMan.GetSessionID();
			int num = 0;
			foreach (ZDOID lamp in lamps)
			{
				ZDO zDO = instance2.GetZDO(lamp);
				if (zDO == null)
				{
					continue;
				}
				GameObject val = instance.FindInstance(lamp);
				if ((Object)(object)val != (Object)null)
				{
					ZNetView component = val.GetComponent<ZNetView>();
					if (component != null)
					{
						component.ClaimOwnership();
					}
					instance.Destroy(val);
				}
				else
				{
					zDO.SetOwner(sessionID);
					instance2.DestroyZDO(zDO);
				}
				num++;
			}
			return num;
		}

		private static float Apart(Job job)
		{
			return job.Order.Spacing * 0.55f;
		}

		private static float[] XsOf(RoadLine line)
		{
			float[] array = new float[line.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = line.XAt(i);
			}
			return array;
		}

		private static float[] ZsOf(RoadLine line)
		{
			float[] array = new float[line.Count];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = line.ZAt(i);
			}
			return array;
		}

		private static string Lay(Job job, RoadLine piece, bool complete)
		{
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			if (ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return "сервер закрывается";
			}
			try
			{
				float[] array = null;
				float[] array2 = null;
				List<Causeway.Stretch> list = null;
				bool flag = job.Order.Bridge && !job.Circle;
				if (!job.Order.Smooth || flag)
				{
					array = GroundUnder(piece);
					if (array != null)
					{
						array2 = Causeway.Raise(array, StepOf(piece), Floor());
					}
					if (array2 != null)
					{
						list = Causeway.Spans(array, array2);
					}
				}
				float num = ((array2 != null) ? Mathf.Max(job.Reach, job.Half + RoadLevel.BlendFor(job.Half)) : job.Reach);
				bool flag2 = default(bool);
				List<TerrainComp> list2 = Ground.CompilersAlong(piece, num, true, ref flag2);
				if (list2 == null)
				{
					return flag2 ? "земля под куском не подгружена" : "рельеф зоны не поднялся на сервере";
				}
				foreach (TerrainComp item in list2)
				{
					CheckLoadMethod?.Invoke(item, null);
					Remember(job.Record, item);
				}
				if (complete && (!job.Circle || job.Order.Clear))
				{
					job.Cleared += Clearing.Along(piece, job.Half, job.Order.Clear, true, "Road clearing");
				}
				job.Spanned = ((flag && complete && list != null) ? Bridge(job, piece, array, list, list2) : null);
				if (job.Order.Smooth)
				{
					job.ShortOf += Level(job, piece, list2, job.Spanned);
				}
				else if (list != null)
				{
					job.ShortOf += RaiseOver(job, piece, list2, array2, list, job.Spanned);
				}
				int num2 = 0;
				foreach (TerrainComp item2 in list2)
				{
					RoadPaint val = RoadPaint.Of(item2);
					if (val == null)
					{
						num2++;
						continue;
					}
					foreach (Causeway.Stretch item3 in Dry(piece.Count, job.Spanned))
					{
						job.Painted += val.Stretch(piece, item3.First, item3.Last, job.Half, job.Paint);
					}
				}
				if (num2 > 0)
				{
					Plugin.Log.LogWarning((object)(job.Label + ": " + num2 + " of " + list2.Count + " compilers would not say what is under them; no paint there."));
				}
				Vector3 val2 = PointAt(job, 0);
				float num3 = default(float);
				float num4 = default(float);
				float num5 = default(float);
				float num6 = default(float);
				piece.Bounds(ref num3, ref num4, ref num5, ref num6);
				((Vector3)(ref val2))..ctor((num3 + num5) * 0.5f, val2.y, (num4 + num6) * 0.5f);
				Vector2 val3 = new Vector2(num5 - num3, num6 - num4);
				float num7 = ((Vector2)(ref val3)).magnitude * 0.5f + num + 2f;
				foreach (TerrainComp item4 in list2)
				{
					Ground.Note(item4, val2, num7);
					Ground.Save(item4);
				}
				Ground.Refresh(val2, num7 + 16f);
				return null;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)(job.Label + ": a piece failed: " + ex));
				return "ошибка на сервере, см. его лог";
			}
		}

		private static float Floor()
		{
			if (!((Object)(object)ZoneSystem.instance != (Object)null))
			{
				return float.MinValue;
			}
			return ZoneSystem.instance.m_waterLevel + 1f;
		}

		private static int Level(Job job, RoadLine piece, List<TerrainComp> comps, List<Causeway.Stretch> spanned = null)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			float[] array = GroundUnder(piece);
			if (array == null)
			{
				return 0;
			}
			if (spanned == null || spanned.Count == 0)
			{
				return LevelAlong(job, piece, array, comps);
			}
			int num = 0;
			foreach (Causeway.Stretch item in Dry(piece.Count, spanned))
			{
				if (item.Last > item.First)
				{
					RoadLine val = new RoadLine();
					float[] array2 = new float[item.Last - item.First + 1];
					for (int i = item.First; i <= item.Last; i++)
					{
						val.Add(piece.XAt(i), piece.ZAt(i));
						array2[i - item.First] = array[i];
					}
					num += LevelAlong(job, val, array2, comps);
				}
			}
			return num;
		}

		private static int LevelAlong(Job job, RoadLine piece, float[] ground, List<TerrainComp> comps)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			float[] array = RoadLevel.Profile((IList<float>)ground, StepOf(piece), job.Half, Floor());
			int num = 0;
			List<int> list = new List<int>();
			Heightmap val2 = default(Heightmap);
			foreach (TerrainComp comp in comps)
			{
				Zone val = Ground.Heights(comp, ref val2);
				if (val != null)
				{
					Vector3 position = ((Component)val2).transform.position;
					float num2 = (float)(val.Size - 1) * 0.5f * val.Scale;
					piece.SegmentsNear(position.x - num2, position.z - num2, position.x + num2, position.z + num2, job.Reach, list);
					num += RoadLevel.Along(val, position.x, position.y, position.z, piece, list, array, job.Half, job.Blend, (Before)null);
				}
			}
			return num;
		}

		private static float[] GroundUnder(RoadLine piece)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			float[] array = new float[piece.Count];
			float num = 0f;
			bool flag = false;
			float num2 = default(float);
			for (int i = 0; i < piece.Count; i++)
			{
				if (Heightmap.GetHeight(new Vector3(piece.XAt(i), 0f, piece.ZAt(i)), ref num2))
				{
					num = num2;
					flag = true;
				}
				array[i] = num;
			}
			if (!flag)
			{
				return null;
			}
			return array;
		}

		private static float StepOf(RoadLine piece)
		{
			if (piece.Count <= 1)
			{
				return 0f;
			}
			return piece.Length / (float)(piece.Count - 1);
		}

		private static int RaiseOver(Job job, RoadLine piece, List<TerrainComp> comps, float[] raised, List<Causeway.Stretch> wet, List<Causeway.Stretch> spanned)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			float num = RoadLevel.BlendFor(job.Half);
			int num2 = 0;
			List<int> list = new List<int>();
			Heightmap val3 = default(Heightmap);
			foreach (Causeway.Stretch item in wet)
			{
				if (spanned != null && spanned.Contains(item))
				{
					continue;
				}
				RoadLine val = new RoadLine();
				float[] array = new float[item.Last - item.First + 1];
				for (int i = item.First; i <= item.Last; i++)
				{
					val.Add(piece.XAt(i), piece.ZAt(i));
					array[i - item.First] = raised[i];
				}
				foreach (TerrainComp comp in comps)
				{
					Zone val2 = Ground.Heights(comp, ref val3);
					if (val2 != null)
					{
						Vector3 position = ((Component)val3).transform.position;
						float num3 = (float)(val2.Size - 1) * 0.5f * val2.Scale;
						val.SegmentsNear(position.x - num3, position.z - num3, position.x + num3, position.z + num3, job.Half + num, list);
						num2 += RoadLevel.Along(val2, position.x, position.y, position.z, val, list, array, job.Half, num, (Before)null);
					}
				}
				job.Raised += ((val.Count > 1) ? val.Length : (job.Half * 2f));
			}
			return num2;
		}

		private static List<Causeway.Stretch> Dry(int count, List<Causeway.Stretch> spanned)
		{
			List<Causeway.Stretch> list = new List<Causeway.Stretch>();
			int num = 0;
			if (spanned != null)
			{
				foreach (Causeway.Stretch item in spanned)
				{
					list.Add(new Causeway.Stretch
					{
						First = num,
						Last = item.First
					});
					num = item.Last;
				}
			}
			list.Add(new Causeway.Stretch
			{
				First = num,
				Last = Math.Max(num, count - 1)
			});
			return list;
		}

		private static int PastWater(Job job, int last, int ahead)
		{
			WorldGenerator instance = WorldGenerator.instance;
			if (instance == null)
			{
				return last;
			}
			float num = Floor();
			while (last < ahead && instance.GetHeight(job.X[last], job.Z[last]) < num)
			{
				last++;
			}
			return last;
		}

		private static List<Causeway.Stretch> Bridge(Job job, RoadLine piece, float[] under, List<Causeway.Stretch> wet, List<TerrainComp> comps)
		{
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			List<Causeway.Stretch> list = new List<Causeway.Stretch>();
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || under == null)
			{
				return list;
			}
			int num = Mathf.Max(1, Mathf.RoundToInt(job.Order.Width / 2f));
			foreach (Causeway.Stretch item in wet)
			{
				if (item.First == 0 || item.Last == piece.Count - 1)
				{
					continue;
				}
				float num2 = Between(piece, item.First, item.Last);
				if (num2 < 4f)
				{
					continue;
				}
				float deck = Mathf.Max(Mathf.Max(under[item.First], under[item.Last]), Floor());
				Crossing crossing = null;
				string why = null;
				for (int i = Mathf.Max(1, Mathf.CeilToInt(num2 / 64f)); i <= 4; i++)
				{
					if (crossing != null)
					{
						break;
					}
					crossing = Plan(piece, under, item, deck, num, job.Order.BridgeRoof, i, out why);
				}
				if (crossing == null)
				{
					Plugin.Log.LogInfo((object)(job.Label + ": no bridge over " + Metres(num2) + " m of water — " + why + "; the fill takes it."));
					continue;
				}
				float half = Mathf.Max(job.Half, (float)num * 2f * 0.5f);
				foreach (Causeway.Stretch island in crossing.Islands)
				{
					job.ShortOf += Island(job, piece, comps, island, deck, half);
				}
				int num3 = 0;
				foreach (Span bridge in crossing.Bridges)
				{
					num3 += Put(job, instan