Decompiled source of PathOfValheiman TreePresets v0.2.5

TreePresets.dll

Decompiled a week ago
using System;
using System.Collections.Concurrent;
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.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using PassiveTree;
using PassiveTree.UI;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TreePresets")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TreePresets")]
[assembly: AssemblyTitle("TreePresets")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace TreePresets;

internal sealed class ActivePresetHud : MonoBehaviour
{
	private const float BottomMargin = 26f;

	private const float Height = 30f;

	private GUIStyle textStyle;

	private void OnGUI()
	{
		//IL_00de: 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_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_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Expected O, but got Unknown
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		if (!Plugin.ShowActivePresetHud.Value || (Object)(object)Player.m_localPlayer == (Object)null || TreeWindowState.IsOpen)
		{
			return;
		}
		int activeSlot = PresetManager.GetActiveSlot();
		PresetData presetData = ((activeSlot > 0) ? PresetManager.GetSlot(activeSlot) : null);
		if (presetData != null)
		{
			TreePresetsSkin.EnsureBuilt();
			if (textStyle == null)
			{
				textStyle = new GUIStyle(GUI.skin.label)
				{
					font = ValheimFont.Get(),
					fontStyle = (FontStyle)1,
					fontSize = 16,
					alignment = (TextAnchor)4
				};
				textStyle.normal.textColor = TreePresetsSkin.TextGold;
			}
			string name = presetData.Name;
			float num = Mathf.Max(200f, (float)name.Length * 10f + 20f);
			GUI.Label(new Rect(((float)Screen.width - num) / 2f, (float)Screen.height - 30f - 26f, num, 30f), name, textStyle);
		}
	}
}
internal static class DiscordRemoval
{
	private static readonly MethodInfo ReturnFalseMethod = typeof(DiscordRemoval).GetMethod("ReturnFalse", BindingFlags.Static | BindingFlags.NonPublic);

	public static void Apply(Harmony harmony)
	{
		PatchStaticNoop(harmony, "PassiveTree.UI.DiscordInvite", "MaybeShow", "плашка Discord при открытии дерева");
		PatchNestedNoop(harmony, "PassiveTree.UI.MenuDiscord", "FejdStartup_Start_Discord", "Postfix", "кнопка Discord в главном меню");
		PatchStaticNoop(harmony, "PassiveTree.DiscordLink", "Pump", "фоновый опрос Discord");
		PatchStaticNoop(harmony, "PassiveTree.DiscordLink", "Begin", "запуск привязки Discord");
	}

	private static bool ReturnFalse()
	{
		return false;
	}

	private static void PatchStaticNoop(Harmony harmony, string typeName, string methodName, string humanLabel)
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		try
		{
			Type type = AccessTools.TypeByName(typeName);
			MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				LogSkip(humanLabel, typeName + "." + methodName);
				return;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(ReturnFalseMethod), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)("TreePresets: отключено - " + humanLabel + "."));
		}
		catch (Exception e)
		{
			LogSkip(humanLabel, typeName + "." + methodName, e);
		}
	}

	private static void PatchNestedNoop(Harmony harmony, string outerTypeName, string innerTypeName, string methodName, string humanLabel)
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Expected O, but got Unknown
		try
		{
			Type type = AccessTools.TypeByName(outerTypeName);
			Type type2 = ((type != null) ? AccessTools.Inner(type, innerTypeName) : null);
			MethodInfo methodInfo = ((type2 != null) ? AccessTools.Method(type2, methodName, (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				LogSkip(humanLabel, outerTypeName + "/" + innerTypeName + "." + methodName);
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(ReturnFalseMethod), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Plugin.Log.LogInfo((object)("TreePresets: отключено - " + humanLabel + "."));
			}
		}
		catch (Exception e)
		{
			LogSkip(humanLabel, outerTypeName + "/" + innerTypeName + "." + methodName, e);
		}
	}

	private static void LogSkip(string humanLabel, string target, Exception e = null)
	{
		Plugin.Log.LogWarning((object)("TreePresets: не удалось отключить \"" + humanLabel + "\" (" + target + ") - в этой версии Path of Valheiman структура другая. " + ((e != null) ? ("Ошибка: " + e.Message) : "Метод не найден.") + " Остальной функционал мода не затронут."));
	}
}
internal static class ExportImportService
{
	public static readonly ConcurrentQueue<Action> MainThreadQueue = new ConcurrentQueue<Action>();

	private const string DialogFilter = "Пресет дерева (*.povpreset)|*.povpreset|Все файлы (*.*)|*.*";

	public static void RunOnMainThread(Action action)
	{
		MainThreadQueue.Enqueue(action);
	}

	public static void PumpMainThreadQueue()
	{
		Action result;
		while (MainThreadQueue.TryDequeue(out result))
		{
			try
			{
				result();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("TreePresets main-thread action failed: " + ex));
			}
		}
	}

	public static void ExportPresetInteractive(int slot, string description)
	{
		PresetData preset = PresetManager.GetSlot(slot);
		if (preset == null || preset.IsEmpty)
		{
			Plugin.Message($"Пресет {slot} пуст - нечего экспортировать.");
			return;
		}
		string version = GameModVersion.GetInstalledModVersion() ?? "unknown";
		string suggestedName = SanitizeFileName(preset.Name);
		RunBackground(delegate
		{
			string path = NativeFileDialog.ShowSave("Экспорт пресета дерева Path of Valheiman", "Пресет дерева (*.povpreset)|*.povpreset|Все файлы (*.*)|*.*", suggestedName, "povpreset");
			if (!string.IsNullOrEmpty(path))
			{
				string contents = PresetFile.BuildForExport("preset", preset.Name, "", description, preset.NodeIds, preset.Choices, version);
				File.WriteAllText(path, contents, Encoding.UTF8);
				RunOnMainThread(delegate
				{
					Plugin.Message("Пресет «" + preset.Name + "» сохранён в файл: " + path);
				});
			}
		});
	}

	public static void ImportGuideInteractive()
	{
		RunBackground(delegate
		{
			string text = NativeFileDialog.ShowOpen("Импорт билда как гайда", "Пресет дерева (*.povpreset)|*.povpreset|Все файлы (*.*)|*.*");
			if (!string.IsNullOrEmpty(text))
			{
				string text2;
				try
				{
					text2 = File.ReadAllText(text, Encoding.UTF8);
				}
				catch (Exception ex)
				{
					Exception ex2 = ex;
					Exception e = ex2;
					RunOnMainThread(delegate
					{
						Plugin.Message("Не удалось прочитать файл: " + e.Message);
					});
					return;
				}
				if (!PresetFile.TryParse(text2, out var fields, out var parseError))
				{
					RunOnMainThread(delegate
					{
						Plugin.Message("Импорт отклонён: " + parseError);
					});
				}
				else
				{
					fields.TryGetValue("game_mod_version", out var fileVersion);
					fields.TryGetValue("nodes", out var nodes);
					List<string> nodeIds = (from s in (nodes ?? "").Split(new char[1] { ',' })
						select s.Trim() into s
						where s.Length > 0
						select s).ToList();
					if (nodeIds.Count == 0)
					{
						RunOnMainThread(delegate
						{
							Plugin.Message("В файле нет ни одного узла - импорт пропущен.");
						});
					}
					else
					{
						fields.TryGetValue("name", out var name);
						fields.TryGetValue("author", out var author);
						fields.TryGetValue("description", out var description);
						fields.TryGetValue("exported_at", out var exportedAt);
						RunOnMainThread(delegate
						{
							if (!GameModVersion.CanResolveAllNodes(nodeIds, out var missingNodeIds))
							{
								Plugin.Message("Импорт отклонён: гайд не соответствует вашей версии дерева - " + $"в нём {missingNodeIds.Count} из {nodeIds.Count} узлов, которых сейчас нет в дереве " + "(файл был сделан для версии " + (fileVersion ?? "?") + ").");
							}
							else
							{
								GuideData guideData = GuideData.Deserialize(null, name, author, description, nodes, fileVersion, exportedAt);
								GuideManager.AddGuide(guideData);
								Plugin.Message($"Гайд «{guideData.Name}» импортирован ({guideData.NodeIds.Count} узлов). " + "Он не занимает слот пресета и не меняет ваш билд - только подсказывает.");
							}
						});
					}
				}
			}
		});
	}

	public static void ExportGuideInteractive(string guideId)
	{
		GuideData guide = GuideManager.GetGuide(guideId);
		if (guide == null)
		{
			return;
		}
		RunBackground(delegate
		{
			string path = NativeFileDialog.ShowSave("Экспорт гайда", "Пресет дерева (*.povpreset)|*.povpreset|Все файлы (*.*)|*.*", SanitizeFileName(guide.Name), "povpreset");
			if (!string.IsNullOrEmpty(path))
			{
				string contents = PresetFile.BuildForExport("guide", guide.Name, guide.Author, guide.Description, guide.NodeIds, null, guide.SourceGameModVersion);
				File.WriteAllText(path, contents, Encoding.UTF8);
				RunOnMainThread(delegate
				{
					Plugin.Message("Гайд сохранён в файл: " + path);
				});
			}
		});
	}

	private static void RunBackground(Action body)
	{
		Thread thread = new Thread((ThreadStart)delegate
		{
			try
			{
				body();
			}
			catch (Exception ex)
			{
				Exception ex2 = ex;
				Exception e = ex2;
				RunOnMainThread(delegate
				{
					Plugin.Message("Ошибка диалога файла: " + e.Message);
				});
				Plugin.Log.LogError((object)("TreePresets file dialog error: " + e));
			}
		});
		thread.IsBackground = true;
		thread.Start();
	}

	private static string SanitizeFileName(string name)
	{
		if (string.IsNullOrWhiteSpace(name))
		{
			return "preset";
		}
		char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
		foreach (char oldChar in invalidFileNameChars)
		{
			name = name.Replace(oldChar, '_');
		}
		return name;
	}
}
internal static class GameModVersion
{
	public const string PathOfValheimanGuid = "treextr.pathofvalheiman";

	public static string GetInstalledModVersion()
	{
		if (Chainloader.PluginInfos.TryGetValue("treextr.pathofvalheiman", out var value))
		{
			return value.Metadata.Version.ToString();
		}
		return null;
	}

	public static string GetTreeContentVersion()
	{
		try
		{
			return 5.ToString();
		}
		catch
		{
			return "?";
		}
	}

	public static bool CanResolveAllNodes(IEnumerable<string> nodeIds, out List<string> missingNodeIds)
	{
		missingNodeIds = new List<string>();
		foreach (string nodeId in nodeIds)
		{
			if (!Tree.Nodes.TryGetValue(nodeId, out var value) || value == null || value.Broken)
			{
				missingNodeIds.Add(nodeId);
			}
		}
		return missingNodeIds.Count == 0;
	}
}
internal sealed class GuideData
{
	public string Id = Guid.NewGuid().ToString("N");

	public string Name = "";

	public string Author = "";

	public string Description = "";

	public List<string> NodeIds = new List<string>();

	public string SourceGameModVersion = "";

	public DateTime ImportedAtUtc = DateTime.UtcNow;

	public string SerializeNodes()
	{
		return string.Join(",", NodeIds);
	}

	public static GuideData Deserialize(string id, string name, string author, string description, string nodesRaw, string sourceVersion, string importedAtRaw)
	{
		GuideData guideData = new GuideData
		{
			Id = (string.IsNullOrEmpty(id) ? Guid.NewGuid().ToString("N") : id),
			Name = (name ?? ""),
			Author = (author ?? ""),
			Description = (description ?? ""),
			SourceGameModVersion = (sourceVersion ?? "")
		};
		if (!string.IsNullOrEmpty(nodesRaw))
		{
			guideData.NodeIds = (from s in nodesRaw.Split(new char[1] { ',' })
				select s.Trim() into s
				where s.Length > 0
				select s).ToList();
		}
		if (!string.IsNullOrEmpty(importedAtRaw) && DateTime.TryParse(importedAtRaw, null, DateTimeStyles.RoundtripKind, out var result))
		{
			guideData.ImportedAtUtc = result;
		}
		return guideData;
	}
}
internal static class PresetFile
{
	public const string Extension = ".povpreset";

	private const int FormatVersion = 1;

	public const string KindPreset = "preset";

	public const string KindGuide = "guide";

	public static string BuildForExport(string kind, string name, string author, string description, IEnumerable<string> nodeIds, IDictionary<string, int> choices, string gameModVersion)
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.AppendLine("# Path of Valheiman - TreePresets export file");
		stringBuilder.AppendLine("format_version=" + 1);
		stringBuilder.AppendLine("kind=" + kind);
		stringBuilder.AppendLine("name=" + Escape(name));
		stringBuilder.AppendLine("author=" + Escape(author ?? ""));
		stringBuilder.AppendLine("description=" + Escape(description ?? ""));
		stringBuilder.AppendLine("game_mod_version=" + gameModVersion);
		stringBuilder.AppendLine("exported_at=" + DateTime.UtcNow.ToString("o"));
		stringBuilder.AppendLine("nodes=" + string.Join(",", nodeIds));
		if (choices != null && choices.Count > 0)
		{
			stringBuilder.AppendLine("choices=" + string.Join(",", choices.Select((KeyValuePair<string, int> kv) => kv.Key + "=" + kv.Value)));
		}
		return stringBuilder.ToString();
	}

	public static bool TryParse(string text, out Dictionary<string, string> fields, out string error)
	{
		fields = new Dictionary<string, string>();
		error = null;
		string[] array = text.Split(new char[1] { '\n' });
		for (int i = 0; i < array.Length; i++)
		{
			string text2 = array[i].Trim('\r', '\n', ' ');
			if (text2.Length != 0 && !text2.StartsWith("#"))
			{
				int num = text2.IndexOf('=');
				if (num > 0)
				{
					fields[text2.Substring(0, num).Trim()] = Unescape(text2.Substring(num + 1));
				}
			}
		}
		if (!fields.ContainsKey("kind") || !fields.ContainsKey("nodes"))
		{
			error = "Файл повреждён или это не файл пресета TreePresets.";
			return false;
		}
		if (!fields.TryGetValue("format_version", out var value) || value != 1.ToString())
		{
			error = string.Format("Неизвестная версия формата файла ({0} вместо {1}). ", value ?? "?", 1) + "Возможно, файл сделан более новой версией TreePresets.";
			return false;
		}
		return true;
	}

	private static string Escape(string s)
	{
		return (s ?? "").Replace("\\", "\\\\").Replace("\n", "\\n").Replace("=", "\\=");
	}

	private static string Unescape(string s)
	{
		return (s ?? "").Replace("\\n", "\n").Replace("\\=", "=").Replace("\\\\", "\\");
	}
}
internal static class GuideHighlighter
{
	private sealed class GuideMark : MonoBehaviour
	{
	}

	private static FieldInfo viewsField;

	private static bool probed;

	private static string activeGuideId;

	private static HashSet<string> activeGuideNodeIds;

	private static readonly Color HighlightColor = new Color(1f, 0.83f, 0.25f, 0.95f);

	private static Sprite ringSprite;

	public static bool IsAvailable { get; private set; }

	public static string ActiveGuideId => activeGuideId;

	private static Sprite RingSprite
	{
		get
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: 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_00ea: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ringSprite != (Object)null)
			{
				return ringSprite;
			}
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				hideFlags = (HideFlags)61,
				wrapMode = (TextureWrapMode)1
			};
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(64f, 64f);
			Color32[] array = (Color32[])(object)new Color32[16384];
			for (int i = 0; i < 128; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					float num = Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), val2);
					float num2 = ((!(num > 62f)) ? ((!(num < 52f)) ? 1f : Mathf.Clamp01(1f - (52f - num))) : Mathf.Clamp01(1f - (num - 62f)));
					array[i * 128 + j] = Color32.op_Implicit(new Color(1f, 1f, 1f, num2));
				}
			}
			val.SetPixels32(array);
			val.Apply();
			ringSprite = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f));
			((Object)ringSprite).hideFlags = (HideFlags)61;
			return ringSprite;
		}
	}

	public static void SetActiveGuide(GuideData guide)
	{
		activeGuideId = guide?.Id;
		activeGuideNodeIds = ((guide != null) ? new HashSet<string>(guide.NodeIds) : null);
	}

	public static void Tick()
	{
		TreeWindow instance = TreeWindow.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			return;
		}
		Dictionary<string, NodeView> dictionary = TryGetViews(instance);
		if (dictionary == null)
		{
			return;
		}
		foreach (KeyValuePair<string, NodeView> item in dictionary)
		{
			string key = item.Key;
			NodeView value = item.Value;
			if (!((Object)(object)value == (Object)null))
			{
				bool flag = activeGuideNodeIds != null && activeGuideNodeIds.Contains(key);
				GuideMark componentInChildren = ((Component)value).GetComponentInChildren<GuideMark>(true);
				if (flag && (Object)(object)componentInChildren == (Object)null)
				{
					CreateMark(value);
				}
				else if (!flag && (Object)(object)componentInChildren != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)componentInChildren).gameObject);
				}
			}
		}
	}

	private static void CreateMark(NodeView view)
	{
		//IL_0032: 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_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_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: 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_0084: 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)
		GameObject val = new GameObject("TreePresets_GuideMark", new Type[3]
		{
			typeof(RectTransform),
			typeof(Image),
			typeof(GuideMark)
		});
		RectTransform val2 = (RectTransform)val.transform;
		((Transform)val2).SetParent(((Component)view).transform, false);
		val2.anchorMin = new Vector2(-0.15f, -0.15f);
		val2.anchorMax = new Vector2(1.15f, 1.15f);
		val2.offsetMin = Vector2.zero;
		val2.offsetMax = Vector2.zero;
		Image component = val.GetComponent<Image>();
		component.sprite = RingSprite;
		component.type = (Type)0;
		((Graphic)component).color = HighlightColor;
		((Graphic)component).raycastTarget = false;
	}

	private static Dictionary<string, NodeView> TryGetViews(TreeWindow window)
	{
		if (!probed)
		{
			probed = true;
			try
			{
				viewsField = typeof(TreeWindow).GetField("views", BindingFlags.Instance | BindingFlags.NonPublic);
				IsAvailable = viewsField != null && typeof(Dictionary<string, NodeView>).IsAssignableFrom(viewsField.FieldType);
			}
			catch (Exception ex)
			{
				IsAvailable = false;
				Plugin.Log.LogWarning((object)("TreePresets: не удалось найти поле TreeWindow.views, подсветка гайдов отключена. " + ex.Message));
			}
			if (!IsAvailable)
			{
				Plugin.Log.LogWarning((object)"TreePresets: подсветка гайдов недоступна в этой версии Path of Valheiman (изменилась внутренняя структура окна дерева). Остальные функции мода (пресеты, экспорт/импорт) работают как обычно.");
			}
		}
		if (!IsAvailable)
		{
			return null;
		}
		try
		{
			return (Dictionary<string, NodeView>)viewsField.GetValue(window);
		}
		catch (Exception ex2)
		{
			IsAvailable = false;
			Plugin.Log.LogWarning((object)("TreePresets: доступ к TreeWindow.views перестал работать, подсветка гайдов отключена. " + ex2.Message));
			return null;
		}
	}
}
internal static class GuideManager
{
	public const int MaxGuides = 25;

	private const string ListKey = "treextr.guides.ids";

	private const string Prefix = "treextr.guides.";

	private static Dictionary<string, string> CustomData
	{
		get
		{
			if (!((Object)(object)Player.m_localPlayer != (Object)null))
			{
				return null;
			}
			return Player.m_localPlayer.m_customData;
		}
	}

	public static List<GuideData> GetAll()
	{
		List<GuideData> list = new List<GuideData>();
		Dictionary<string, string> customData = CustomData;
		if (customData == null)
		{
			return list;
		}
		if (!customData.TryGetValue("treextr.guides.ids", out var value) || string.IsNullOrEmpty(value))
		{
			return list;
		}
		string[] array = value.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			GuideData guide = GetGuide(array[i]);
			if (guide != null)
			{
				list.Add(guide);
			}
		}
		return list;
	}

	public static GuideData GetGuide(string id)
	{
		Dictionary<string, string> customData = CustomData;
		if (customData == null || string.IsNullOrEmpty(id))
		{
			return null;
		}
		if (!customData.TryGetValue("treextr.guides." + id + ".nodes", out var value))
		{
			return null;
		}
		customData.TryGetValue("treextr.guides." + id + ".name", out var value2);
		customData.TryGetValue("treextr.guides." + id + ".author", out var value3);
		customData.TryGetValue("treextr.guides." + id + ".desc", out var value4);
		customData.TryGetValue("treextr.guides." + id + ".ver", out var value5);
		customData.TryGetValue("treextr.guides." + id + ".importedAt", out var value6);
		return GuideData.Deserialize(id, value2, value3, value4, value, value5, value6);
	}

	public static bool AddGuide(GuideData guide)
	{
		Dictionary<string, string> customData = CustomData;
		if (customData == null)
		{
			return false;
		}
		List<string> ids = GetIds(customData);
		if (ids.Count >= 25)
		{
			Plugin.Message($"Достигнут предел в {25} гайдов - удалите старый перед импортом нового.");
			return false;
		}
		ids.Add(guide.Id);
		customData["treextr.guides.ids"] = string.Join(",", ids);
		customData["treextr.guides." + guide.Id + ".name"] = guide.Name;
		customData["treextr.guides." + guide.Id + ".author"] = guide.Author;
		customData["treextr.guides." + guide.Id + ".desc"] = guide.Description ?? "";
		customData["treextr.guides." + guide.Id + ".nodes"] = guide.SerializeNodes();
		customData["treextr.guides." + guide.Id + ".ver"] = guide.SourceGameModVersion;
		customData["treextr.guides." + guide.Id + ".importedAt"] = guide.ImportedAtUtc.ToString("o");
		return true;
	}

	public static void RemoveGuide(string id)
	{
		Dictionary<string, string> customData = CustomData;
		if (customData != null)
		{
			List<string> ids = GetIds(customData);
			ids.Remove(id);
			customData["treextr.guides.ids"] = string.Join(",", ids);
			customData.Remove("treextr.guides." + id + ".name");
			customData.Remove("treextr.guides." + id + ".author");
			customData.Remove("treextr.guides." + id + ".desc");
			customData.Remove("treextr.guides." + id + ".nodes");
			customData.Remove("treextr.guides." + id + ".ver");
			customData.Remove("treextr.guides." + id + ".importedAt");
		}
	}

	private static List<string> GetIds(Dictionary<string, string> cd)
	{
		if (!cd.TryGetValue("treextr.guides.ids", out var value) || string.IsNullOrEmpty(value))
		{
			return new List<string>();
		}
		return (from s in value.Split(new char[1] { ',' })
			where s.Length > 0
			select s).ToList();
	}
}
internal static class InputBlocker
{
	private const int SortingOrder = 30000;

	private static GameObject blockerGo;

	private static Image image;

	public static void SetBlockingRect(Rect? guiRect)
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
		if (!guiRect.HasValue)
		{
			if ((Object)(object)blockerGo != (Object)null)
			{
				blockerGo.SetActive(false);
			}
			return;
		}
		EnsureBlocker();
		blockerGo.SetActive(true);
		Rect value = guiRect.Value;
		RectTransform rectTransform = ((Graphic)image).rectTransform;
		rectTransform.anchorMin = new Vector2(0f, 1f);
		rectTransform.anchorMax = new Vector2(0f, 1f);
		rectTransform.pivot = new Vector2(0f, 1f);
		rectTransform.anchoredPosition = new Vector2(((Rect)(ref value)).x, 0f - ((Rect)(ref value)).y);
		rectTransform.sizeDelta = new Vector2(((Rect)(ref value)).width, ((Rect)(ref value)).height);
	}

	private static void EnsureBlocker()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)blockerGo != (Object)null))
		{
			blockerGo = new GameObject("TreePresets_InputBlocker");
			Object.DontDestroyOnLoad((Object)(object)blockerGo);
			Canvas obj = blockerGo.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 30000;
			blockerGo.AddComponent<GraphicRaycaster>();
			image = blockerGo.AddComponent<Image>();
			((Graphic)image).color = new Color(0f, 0f, 0f, 0f);
			((Graphic)image).raycastTarget = true;
		}
	}
}
internal static class Localization
{
	private static bool IsRussian => PlayerPrefs.GetString("language", "English") == "Russian";

	public static string T(string ru, string en)
	{
		if (!IsRussian)
		{
			return en;
		}
		return ru;
	}
}
internal static class NativeFileDialog
{
	[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
	private class OpenFileName
	{
		public int lStructSize = Marshal.SizeOf(typeof(OpenFileName));

		public IntPtr hwndOwner = IntPtr.Zero;

		public IntPtr hInstance = IntPtr.Zero;

		public string lpstrFilter;

		public string lpstrCustomFilter;

		public int nMaxCustFilter;

		public int nFilterIndex;

		public string lpstrFile;

		public int nMaxFile = 4096;

		public string lpstrFileTitle;

		public int nMaxFileTitle = 4096;

		public string lpstrInitialDir;

		public string lpstrTitle;

		public int Flags;

		public short nFileOffset;

		public short nFileExtension;

		public string lpstrDefExt;

		public IntPtr lCustData = IntPtr.Zero;

		public IntPtr lpfnHook = IntPtr.Zero;

		public string lpTemplateName;

		public IntPtr pvReserved = IntPtr.Zero;

		public int dwReserved;

		public int flagsEx;
	}

	private const int MaxPath = 4096;

	private const int OFN_EXPLORER = 524288;

	private const int OFN_FILEMUSTEXIST = 4096;

	private const int OFN_PATHMUSTEXIST = 2048;

	private const int OFN_OVERWRITEPROMPT = 2;

	private const int OFN_NOCHANGEDIR = 8;

	[DllImport("comdlg32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
	private static extern bool GetOpenFileNameW([In][Out] OpenFileName ofn);

	[DllImport("comdlg32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
	private static extern bool GetSaveFileNameW([In][Out] OpenFileName ofn);

	private static string BuildFilter(string filter)
	{
		return filter.Replace('|', '\0') + "\0\0";
	}

	public static string ShowOpen(string title, string filter)
	{
		OpenFileName openFileName = new OpenFileName
		{
			lpstrFilter = BuildFilter(filter),
			lpstrFile = new string('\0', 4096),
			lpstrTitle = title,
			Flags = 530440
		};
		if (!GetOpenFileNameW(openFileName))
		{
			return null;
		}
		return CleanResult(openFileName.lpstrFile);
	}

	public static string ShowSave(string title, string filter, string suggestedFileName, string defaultExt)
	{
		string text = suggestedFileName ?? "";
		if (text.Length >= 4096)
		{
			text = text.Substring(0, 4095);
		}
		text = text.PadRight(4096, '\0');
		OpenFileName openFileName = new OpenFileName
		{
			lpstrFilter = BuildFilter(filter),
			lpstrFile = text,
			lpstrTitle = title,
			lpstrDefExt = defaultExt,
			Flags = 524298
		};
		if (!GetSaveFileNameW(openFileName))
		{
			return null;
		}
		return CleanResult(openFileName.lpstrFile);
	}

	private static string CleanResult(string raw)
	{
		if (string.IsNullOrEmpty(raw))
		{
			return null;
		}
		int num = raw.IndexOf('\0');
		if (num < 0)
		{
			return raw;
		}
		return raw.Substring(0, num);
	}
}
[BepInPlugin("treextr.pathofvalheiman.presets", "Path of Valheiman - Presets", "0.2.5")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	private static class TerminalCommandsPatch
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__0_0;

			public static ConsoleEvent <>9__0_1;

			public static ConsoleEvent <>9__0_2;

			internal void <Postfix>b__0_0(ConsoleEventArgs args)
			{
				if (!TreeWindowState.IsOpen)
				{
					Message(Localization.T("Сначала откройте дерево навыков (P).", "Open the skill tree first (P)."));
				}
				else
				{
					PresetMageUI.Toggle();
				}
			}

			internal void <Postfix>b__0_1(ConsoleEventArgs args)
			{
				for (int i = 1; i <= 3; i++)
				{
					PresetData slot = PresetManager.GetSlot(i);
					Message((slot == null) ? Localization.T($"{i}: пусто", $"{i}: empty") : string.Format("{0}: {1} - {2} {3}", i, slot.Name, slot.MeaningfulNodeCount, Localization.T("узлов", "nodes")));
				}
				foreach (GuideData item in GuideManager.GetAll())
				{
					Message(string.Format("{0}: {1} - {2} {3}", Localization.T("Гайд", "Guide"), item.Name, item.NodeIds.Count, Localization.T("узлов", "nodes")));
				}
			}

			internal void <Postfix>b__0_2(ConsoleEventArgs args)
			{
				ExportImportService.ImportGuideInteractive();
			}
		}

		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		[HarmonyPostfix]
		private static void Postfix()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_006c: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate
				{
					if (!TreeWindowState.IsOpen)
					{
						Message(Localization.T("Сначала откройте дерево навыков (P).", "Open the skill tree first (P)."));
					}
					else
					{
						PresetMageUI.Toggle();
					}
				};
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("presets_open", "presets_open - open the presets panel (tree must be open)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj2 = <>c.<>9__0_1;
			if (obj2 == null)
			{
				ConsoleEvent val2 = delegate
				{
					for (int i = 1; i <= 3; i++)
					{
						PresetData slot = PresetManager.GetSlot(i);
						Message((slot == null) ? Localization.T($"{i}: пусто", $"{i}: empty") : string.Format("{0}: {1} - {2} {3}", i, slot.Name, slot.MeaningfulNodeCount, Localization.T("узлов", "nodes")));
					}
					foreach (GuideData item in GuideManager.GetAll())
					{
						Message(string.Format("{0}: {1} - {2} {3}", Localization.T("Гайд", "Guide"), item.Name, item.NodeIds.Count, Localization.T("узлов", "nodes")));
					}
				};
				<>c.<>9__0_1 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("presets_list", "presets_list - list all slots and guides", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj3 = <>c.<>9__0_2;
			if (obj3 == null)
			{
				ConsoleEvent val3 = delegate
				{
					ExportImportService.ImportGuideInteractive();
				};
				<>c.<>9__0_2 = val3;
				obj3 = (object)val3;
			}
			new ConsoleCommand("presets_import", "presets_import - open the file import dialog", (ConsoleEvent)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}

	public const string Guid = "treextr.pathofvalheiman.presets";

	public const string Name = "Path of Valheiman - Presets";

	public const string Version = "0.2.5";

	internal static Plugin Instance;

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> FreeSwitching;

	internal static ConfigEntry<bool> ShowMessages;

	internal static ConfigEntry<bool> AutoSaveActivePreset;

	internal static ConfigEntry<float> BackgroundTickInterval;

	internal static ConfigEntry<bool> ShowActivePresetHud;

	internal static ConfigEntry<bool> RemoveDiscordFeatures;

	internal static ConfigEntry<bool> PlayUiClickSound;

	internal static ConfigEntry<float> TopBarExtraLeftOffset;

	internal static ConfigEntry<float> PanelTopOffset;

	private Harmony harmony;

	private float nextBackgroundTick;

	private void Awake()
	{
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		FreeSwitching = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Free preset switching", true, "Переключение пресетов не тратит монеты/глаза грейдваргов на снятие узлов.");
		ShowMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show chat messages", true, "Показывать сообщения о результатах операций.");
		AutoSaveActivePreset = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Auto-save active preset", true, "Пока выбран активный пресет, любые изменения дерева (новые узлы, снятые узлы, выбор мастерства) автоматически записываются обратно в этот же слот.");
		BackgroundTickInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Background tick interval (seconds)", 0.5f, "Как часто (пока открыто окно дерева) проверять изменения дерева для автосохранения и обновлять подсветку гайда. У Progress нет публичного события изменения, поэтому используется периодическая проверка вместо мгновенной реакции.");
		ShowActivePresetHud = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "Show active preset HUD", true, "Небольшая постоянная подпись снизу по центру экрана с названием активного пресета.");
		RemoveDiscordFeatures = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Remove Discord features", true, "Отключает всё, что связано с Discord в самом Path of Valheiman.");
		PlayUiClickSound = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "Play UI click sound", true, "Проигрывать игровой звук клика при нажатии кнопок этого мода.");
		TopBarExtraLeftOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "Top bar left offset", 90f, "Насколько левее сдвинуть кнопку \"Пресеты\" от правого края экрана");
		PanelTopOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "Panel top offset", 56f, "Насколько ниже верхнего края экрана открывается окно пресетов");
		harmony = new Harmony("treextr.pathofvalheiman.presets");
		harmony.PatchAll(typeof(TerminalCommandsPatch));
		if (RemoveDiscordFeatures.Value)
		{
			DiscordRemoval.Apply(harmony);
		}
		((Object)((Component)this).gameObject.AddComponent<TopBarUI>()).hideFlags = (HideFlags)61;
		((Object)((Component)this).gameObject.AddComponent<PresetMageUI>()).hideFlags = (HideFlags)61;
		((Object)((Component)this).gameObject.AddComponent<ActivePresetHud>()).hideFlags = (HideFlags)61;
		Log.LogInfo((object)"Path of Valheiman - Presets 0.2.5 loaded.");
	}

	private void OnDestroy()
	{
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
	}

	private void Update()
	{
		ExportImportService.PumpMainThreadQueue();
		if (!((Object)(object)Player.m_localPlayer == (Object)null) && TreeWindowState.IsOpen && Time.unscaledTime >= nextBackgroundTick)
		{
			nextBackgroundTick = Time.unscaledTime + Mathf.Max(0.1f, BackgroundTickInterval.Value);
			PresetManager.AutoSave.Tick();
			GuideHighlighter.Tick();
		}
	}

	internal static void Message(string text)
	{
		if (ShowMessages.Value && (Object)(object)Player.m_localPlayer != (Object)null)
		{
			((Character)Player.m_localPlayer).Message((MessageType)1, text, 0, (Sprite)null, false);
		}
		Log.LogInfo((object)text);
	}

	internal static void MessageQuiet(string text)
	{
		Log.LogInfo((object)text);
	}
}
internal sealed class PresetData
{
	public string Name = "";

	public string Description = "";

	public List<string> NodeIds = new List<string>();

	public Dictionary<string, int> Choices = new Dictionary<string, int>();

	public DateTime SavedAtUtc = DateTime.UtcNow;

	public int MeaningfulNodeCount
	{
		get
		{
			string rootId = SafeRootId();
			if (string.IsNullOrEmpty(rootId))
			{
				return NodeIds.Count;
			}
			return NodeIds.Count((string id) => id != rootId);
		}
	}

	public bool IsEmpty => MeaningfulNodeCount == 0;

	private static string SafeRootId()
	{
		try
		{
			return "root";
		}
		catch
		{
			return null;
		}
	}

	public string SerializeNodes()
	{
		return string.Join(",", NodeIds);
	}

	public string SerializeChoices()
	{
		return string.Join(",", Choices.Select((KeyValuePair<string, int> kv) => kv.Key + "=" + kv.Value));
	}

	public static PresetData Deserialize(string name, string nodesRaw, string choicesRaw, string savedAtRaw, string description = null)
	{
		PresetData presetData = new PresetData
		{
			Name = (name ?? ""),
			Description = (description ?? "")
		};
		if (!string.IsNullOrEmpty(nodesRaw))
		{
			string[] array = nodesRaw.Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					presetData.NodeIds.Add(text);
				}
			}
		}
		if (!string.IsNullOrEmpty(choicesRaw))
		{
			string[] array = choicesRaw.Split(new char[1] { ',' });
			foreach (string text2 in array)
			{
				int num = text2.IndexOf('=');
				if (num > 0)
				{
					string text3 = text2.Substring(0, num).Trim();
					string s = text2.Substring(num + 1).Trim();
					if (text3.Length > 0 && int.TryParse(s, out var result))
					{
						presetData.Choices[text3] = result;
					}
				}
			}
		}
		if (!string.IsNullOrEmpty(savedAtRaw) && DateTime.TryParse(savedAtRaw, null, DateTimeStyles.RoundtripKind, out var result2))
		{
			presetData.SavedAtUtc = result2;
		}
		return presetData;
	}
}
public sealed class PresetMage : MonoBehaviour, Hoverable, Interactable
{
	public string GetHoverName()
	{
		return "Хранитель пресетов";
	}

	public string GetHoverText()
	{
		return "Хранитель пресетов\n[<color=yellow><b>E</b></color>] Открыть";
	}

	public float GetHoverOffset()
	{
		return 0f;
	}

	public bool Interact(Humanoid user, bool hold, bool alt)
	{
		if (hold)
		{
			return false;
		}
		if (!TreeWindowState.IsOpen)
		{
			Plugin.Message(Localization.T("Сначала откройте дерево навыков (P), чтобы управлять пресетами.", "Open the skill tree (P) first to manage presets."));
			return true;
		}
		PresetMageUI.Toggle();
		return true;
	}

	public bool UseItem(Humanoid user, ItemData item)
	{
		return false;
	}
}
internal sealed class PresetMageUI : MonoBehaviour
{
	private const float WindowWidth = 420f;

	private const float WindowHeight = 480f;

	private const float ScreenMargin = 16f;

	private static bool visible;

	private Rect windowRect = new Rect(0f, 0f, 420f, 480f);

	private Vector2 guidesScroll;

	private int renamingSlot = -1;

	private string renameBuffer = "";

	private int exportingPresetSlot = -1;

	private string exportingGuideId;

	private string exportDescriptionBuffer = "";

	private string viewingGuideDescriptionId;

	public static void Toggle()
	{
		visible = !visible;
	}

	public static void Close()
	{
		visible = false;
	}

	private void OnGUI()
	{
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Expected O, but got Unknown
		//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_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		if (visible && ((Object)(object)Player.m_localPlayer == (Object)null || !TreeWindowState.IsOpen))
		{
			visible = false;
		}
		if (!visible)
		{
			ApplyInputBlock(null);
			return;
		}
		TreePresetsSkin.EnsureBuilt();
		if (((Rect)(ref windowRect)).x == 0f && ((Rect)(ref windowRect)).y == 0f)
		{
			((Rect)(ref windowRect)).x = (float)Screen.width - 420f - 16f - Plugin.TopBarExtraLeftOffset.Value;
			((Rect)(ref windowRect)).y = 16f + Plugin.PanelTopOffset.Value;
		}
		windowRect = GUILayout.Window(((Object)this).GetInstanceID(), windowRect, new WindowFunction(DrawWindow), Localization.T("Пресеты", "Presets"), TreePresetsSkin.Window, Array.Empty<GUILayoutOption>());
		ApplyInputBlock((Rect)((exportingPresetSlot != -1 || exportingGuideId != null || viewingGuideDescriptionId != null) ? new Rect(0f, 0f, (float)Screen.width, (float)Screen.height) : windowRect));
		if (exportingPresetSlot != -1 || exportingGuideId != null)
		{
			DrawExportPrompt();
		}
		if (viewingGuideDescriptionId != null)
		{
			DrawGuideDescriptionPopup();
		}
	}

	private void DrawWindow(int id)
	{
		//IL_0072: 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_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
		GUILayout.Label(Localization.T("Активные пресеты:", "Active presets:"), TreePresetsSkin.Label, Array.Empty<GUILayoutOption>());
		int activeSlot = PresetManager.GetActiveSlot();
		for (int i = 1; i <= 3; i++)
		{
			DrawPresetRow(i, PresetManager.GetSlot(i), activeSlot);
		}
		GUILayout.Space(8f);
		GUILayout.Label(Localization.T("Гайды:", "Guides:"), TreePresetsSkin.Label, Array.Empty<GUILayoutOption>());
		guidesScroll = GUILayout.BeginScrollView(guidesScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(140f) });
		List<GuideData> all = GuideManager.GetAll();
		if (all.Count == 0)
		{
			GUILayout.Label(Localization.T("Пока нет ни одного гайда.", "No guides yet."), TreePresetsSkin.Label, Array.Empty<GUILayoutOption>());
		}
		foreach (GuideData item in all)
		{
			DrawGuideRow(item);
		}
		GUILayout.EndScrollView();
		if (!GuideHighlighter.IsAvailable)
		{
			GUILayout.Label(Localization.T("(Подсветка гайдов недоступна в этой версии Path of Valheiman.)", "(Guide highlighting is unavailable in this Path of Valheiman version.)"), TreePresetsSkin.Label, Array.Empty<GUILayoutOption>());
		}
		if (TreePresetsSkin.Btn(Localization.T("Импортировать файл...", "Import file..."), GUILayout.Height(28f)))
		{
			ExportImportService.ImportGuideInteractive();
		}
		GUILayout.Space(8f);
		if (TreePresetsSkin.Btn(Localization.T("Закрыть", "Close")))
		{
			Close();
		}
		GUILayout.EndVertical();
		GUI.DragWindow(new Rect(0f, 0f, 420f, 30f));
	}

	private void DrawPresetRow(int slot, PresetData preset, int active)
	{
		GUILayout.BeginVertical(TreePresetsSkin.Box, Array.Empty<GUILayoutOption>());
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		if (renamingSlot == slot)
		{
			renameBuffer = GUILayout.TextField(renameBuffer, TreePresetsSkin.TextField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) });
			if (TreePresetsSkin.Btn("OK", GUILayout.Width(44f)))
			{
				PresetManager.RenameSlot(slot, renameBuffer);
				renamingSlot = -1;
			}
			if (TreePresetsSkin.Btn("X", GUILayout.Width(34f)))
			{
				renamingSlot = -1;
			}
		}
		else
		{
			string text = ((preset == null) ? Localization.T($"{slot}. пусто", $"{slot}. empty") : string.Format("{0}. {1} ({2} {3})", slot, preset.Name, preset.MeaningfulNodeCount, Localization.T("узлов", "nodes")));
			if (slot == active)
			{
				text = "★ " + text;
			}
			GUILayout.Label(text, TreePresetsSkin.Label, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(300f),
				GUILayout.ExpandWidth(true)
			});
			GUI.enabled = preset != null;
			if (TreePresetsSkin.Btn("✎", GUILayout.Width(32f)))
			{
				renamingSlot = slot;
				renameBuffer = preset?.Name ?? "";
			}
			GUI.enabled = true;
		}
		GUILayout.EndHorizontal();
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		if (TreePresetsSkin.Btn(Localization.T("Выбрать", "Select"), GUILayout.ExpandWidth(true)))
		{
			ApplyResult result = PresetManager.LoadSlotIntoCurrentBuild(slot, Plugin.FreeSwitching.Value);
			ReportApply(slot, result);
		}
		GUI.enabled = preset != null && preset.MeaningfulNodeCount > 0;
		if (TreePresetsSkin.Btn(Localization.T("Экспорт", "Export"), GUILayout.ExpandWidth(true)))
		{
			exportingPresetSlot = slot;
			exportingGuideId = null;
			exportDescriptionBuffer = preset?.Description ?? "";
		}
		GUI.enabled = true;
		GUILayout.EndHorizontal();
		GUILayout.EndVertical();
	}

	private void DrawGuideRow(GuideData guide)
	{
		GUILayout.BeginVertical(TreePresetsSkin.Box, Array.Empty<GUILayoutOption>());
		bool flag = GuideHighlighter.ActiveGuideId == guide.Id;
		GUILayout.Label((flag ? "★ " : "") + $"{guide.Name} ({guide.NodeIds.Count})", TreePresetsSkin.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		if (TreePresetsSkin.Btn(flag ? Localization.T("Скрыть", "Hide") : Localization.T("Подсветить", "Highlight"), GUILayout.ExpandWidth(true)))
		{
			GuideHighlighter.SetActiveGuide(flag ? null : guide);
		}
		GUI.enabled = !string.IsNullOrWhiteSpace(guide.Description);
		if (TreePresetsSkin.Btn(Localization.T("Описание", "Notes"), GUILayout.ExpandWidth(true)))
		{
			viewingGuideDescriptionId = guide.Id;
		}
		GUI.enabled = true;
		if (TreePresetsSkin.Btn(Localization.T("Экспорт", "Export"), GUILayout.ExpandWidth(true)))
		{
			exportingGuideId = guide.Id;
			exportingPresetSlot = -1;
			exportDescriptionBuffer = guide.Description ?? "";
		}
		if (TreePresetsSkin.Btn("X", GUILayout.Width(30f)))
		{
			if (flag)
			{
				GuideHighlighter.SetActiveGuide(null);
			}
			GuideManager.RemoveGuide(guide.Id);
		}
		GUILayout.EndHorizontal();
		GUILayout.EndVertical();
	}

	private void DrawExportPrompt()
	{
		//IL_003d: 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_0068: Expected O, but got Unknown
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		Rect val = default(Rect);
		((Rect)(ref val))..ctor((float)(Screen.width - 400) / 2f, (float)(Screen.height - 220) / 2f, 400f, 220f);
		GUILayout.Window(((Object)this).GetInstanceID() + 1, val, (WindowFunction)delegate
		{
			GUILayout.Label(Localization.T("Описание для файла (необязательно) - увидит тот, кто это импортирует:", "Description for the file (optional) - shown to whoever imports it:"), TreePresetsSkin.Label, Array.Empty<GUILayoutOption>());
			exportDescriptionBuffer = GUILayout.TextArea(exportDescriptionBuffer, TreePresetsSkin.TextField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(80f) });
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (TreePresetsSkin.Btn(Localization.T("Сохранить как...", "Save as..."), GUILayout.Height(28f)))
			{
				if (exportingPresetSlot != -1)
				{
					ExportImportService.ExportPresetInteractive(exportingPresetSlot, exportDescriptionBuffer);
				}
				else if (exportingGuideId != null)
				{
					ExportImportService.ExportGuideInteractive(exportingGuideId);
				}
				exportingPresetSlot = -1;
				exportingGuideId = null;
			}
			if (TreePresetsSkin.Btn(Localization.T("Отмена", "Cancel"), GUILayout.Height(28f)))
			{
				exportingPresetSlot = -1;
				exportingGuideId = null;
			}
			GUILayout.EndHorizontal();
		}, Localization.T("Экспорт", "Export"), TreePresetsSkin.Window, Array.Empty<GUILayoutOption>());
	}

	private void DrawGuideDescriptionPopup()
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		GuideData guide = GuideManager.GetGuide(viewingGuideDescriptionId);
		Rect val = default(Rect);
		((Rect)(ref val))..ctor((float)(Screen.width - 400) / 2f, (float)(Screen.height - 260) / 2f, 400f, 260f);
		GUILayout.Window(((Object)this).GetInstanceID() + 2, val, (WindowFunction)delegate
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			if (guide != null && !string.IsNullOrEmpty(guide.Author))
			{
				GUILayout.Label(Localization.T("Автор: " + guide.Author, "Author: " + guide.Author), TreePresetsSkin.Label, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Label(Localization.T("Описание:", "Notes:"), TreePresetsSkin.Label, Array.Empty<GUILayoutOption>());
			GUILayout.BeginScrollView(Vector2.zero, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) });
			GUILayout.TextArea(guide?.Description ?? "", TreePresetsSkin.TextField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			GUILayout.EndScrollView();
			if (TreePresetsSkin.Btn(Localization.T("Закрыть", "Close")))
			{
				viewingGuideDescriptionId = null;
			}
		}, Localization.T("Описание гайда", "Guide notes"), TreePresetsSkin.Window, Array.Empty<GUILayoutOption>());
	}

	private static void ApplyInputBlock(Rect? rect)
	{
		InputBlocker.SetBlockingRect(rect);
	}

	private void OnDestroy()
	{
		ApplyInputBlock(null);
	}

	private static void ReportApply(int slot, ApplyResult result)
	{
		switch (result.Outcome)
		{
		case ApplyOutcome.Success:
			Plugin.Message(Localization.T($"Активен пресет {slot}.", $"Preset {slot} is now active."));
			break;
		case ApplyOutcome.Partial:
			Plugin.Message(Localization.T($"Пресет {slot} применён частично: {result.SkippedNodeIds.Count} узлов не удалось вернуть.", $"Preset {slot} applied partially: {result.SkippedNodeIds.Count} nodes could not be restored."));
			break;
		case ApplyOutcome.EmptySlot:
			Plugin.Message(Localization.T($"Пресет {slot} пуст.", $"Preset {slot} is empty."));
			break;
		case ApplyOutcome.NoLocalPlayer:
			Plugin.Message(Localization.T("Нет активного персонажа.", "No active character."));
			break;
		}
	}
}
internal enum ApplyOutcome
{
	NoLocalPlayer,
	EmptySlot,
	Success,
	Partial
}
internal readonly struct ApplyResult
{
	public readonly ApplyOutcome Outcome;

	public readonly IReadOnlyList<string> SkippedNodeIds;

	public ApplyResult(ApplyOutcome outcome, IReadOnlyList<string> skipped = null)
	{
		Outcome = outcome;
		SkippedNodeIds = skipped ?? Array.Empty<string>();
	}
}
internal static class PresetManager
{
	internal static class AutoSave
	{
		private static HashSet<string> lastSeen;

		private static Dictionary<string, int> lastSeenChoices;

		public static void ResetBaseline()
		{
			Progress current = Progress.Current;
			lastSeen = ((current != null) ? new HashSet<string>(current.Allocated) : null);
			lastSeenChoices = ((current != null) ? new Dictionary<string, int>(current.Choices) : null);
		}

		public static void Tick()
		{
			if (!Plugin.AutoSaveActivePreset.Value)
			{
				return;
			}
			int activeSlot = GetActiveSlot();
			if (activeSlot == 0)
			{
				return;
			}
			Progress current = Progress.Current;
			if (current != null)
			{
				if (lastSeen == null)
				{
					ResetBaseline();
				}
				else if (!current.Allocated.SetEquals(lastSeen) || !DictionariesEqual(current.Choices, lastSeenChoices))
				{
					SaveCurrentBuildToSlot(activeSlot, null);
					ResetBaseline();
					Plugin.MessageQuiet($"Автосохранение: пресет {activeSlot} обновлён.");
				}
			}
		}

		private static bool DictionariesEqual(Dictionary<string, int> a, Dictionary<string, int> b)
		{
			if (a.Count != b.Count)
			{
				return false;
			}
			foreach (KeyValuePair<string, int> item in a)
			{
				if (!b.TryGetValue(item.Key, out var value) || value != item.Value)
				{
					return false;
				}
			}
			return true;
		}
	}

	public const int MaxPresets = 3;

	private const string KeyPrefix = "treextr.presets.";

	private const string ActiveKey = "treextr.presets.active";

	private static Dictionary<string, string> CustomData
	{
		get
		{
			if (!((Object)(object)Player.m_localPlayer != (Object)null))
			{
				return null;
			}
			return Player.m_localPlayer.m_customData;
		}
	}

	private static string KName(int slot)
	{
		return "treextr.presets.slot" + slot + ".name";
	}

	private static string KDescription(int slot)
	{
		return "treextr.presets.slot" + slot + ".desc";
	}

	private static string KNodes(int slot)
	{
		return "treextr.presets.slot" + slot + ".nodes";
	}

	private static string KChoices(int slot)
	{
		return "treextr.presets.slot" + slot + ".choices";
	}

	private static string KSavedAt(int slot)
	{
		return "treextr.presets.slot" + slot + ".savedAt";
	}

	public static bool IsSlotValid(int slot)
	{
		if (slot >= 1)
		{
			return slot <= 3;
		}
		return false;
	}

	public static int GetActiveSlot()
	{
		Dictionary<string, string> customData = CustomData;
		if (customData == null)
		{
			return 0;
		}
		if (customData.TryGetValue("treextr.presets.active", out var value) && int.TryParse(value, out var result) && IsSlotValid(result))
		{
			return result;
		}
		return 0;
	}

	public static void SetActiveSlotSilently(int slot)
	{
		Dictionary<string, string> customData = CustomData;
		if (customData != null)
		{
			customData["treextr.presets.active"] = (IsSlotValid(slot) ? slot.ToString() : "0");
		}
	}

	public static PresetData GetSlot(int slot)
	{
		if (!IsSlotValid(slot))
		{
			return null;
		}
		Dictionary<string, string> customData = CustomData;
		if (customData == null)
		{
			return null;
		}
		customData.TryGetValue(KNodes(slot), out var value);
		if (string.IsNullOrEmpty(value))
		{
			return null;
		}
		customData.TryGetValue(KName(slot), out var value2);
		customData.TryGetValue(KDescription(slot), out var value3);
		customData.TryGetValue(KChoices(slot), out var value4);
		customData.TryGetValue(KSavedAt(slot), out var value5);
		return PresetData.Deserialize(value2, value, value4, value5, value3);
	}

	public static void DeleteSlot(int slot)
	{
		if (!IsSlotValid(slot))
		{
			return;
		}
		Dictionary<string, string> customData = CustomData;
		if (customData != null)
		{
			customData.Remove(KName(slot));
			customData.Remove(KDescription(slot));
			customData.Remove(KNodes(slot));
			customData.Remove(KChoices(slot));
			customData.Remove(KSavedAt(slot));
			if (GetActiveSlot() == slot)
			{
				SetActiveSlotSilently(0);
			}
		}
	}

	public static bool SaveCurrentBuildToSlot(int slot, string name, string description = null)
	{
		if (!IsSlotValid(slot))
		{
			return false;
		}
		Progress current = Progress.Current;
		Dictionary<string, string> customData = CustomData;
		if (current == null || customData == null)
		{
			return false;
		}
		PresetData slot2 = GetSlot(slot);
		PresetData data = new PresetData
		{
			Name = ((!string.IsNullOrWhiteSpace(name)) ? name.Trim() : (slot2?.Name ?? ("Preset " + slot))),
			Description = (description ?? slot2?.Description ?? ""),
			NodeIds = new List<string>(current.Allocated),
			Choices = new Dictionary<string, int>(current.Choices),
			SavedAtUtc = DateTime.UtcNow
		};
		WriteSlot(slot, data);
		return true;
	}

	public static bool RenameSlot(int slot, string newName)
	{
		if (!IsSlotValid(slot) || string.IsNullOrWhiteSpace(newName))
		{
			return false;
		}
		Dictionary<string, string> customData = CustomData;
		PresetData slot2 = GetSlot(slot);
		if (customData == null || slot2 == null)
		{
			return false;
		}
		customData[KName(slot)] = newName.Trim();
		return true;
	}

	public static bool SetSlotDescription(int slot, string description)
	{
		if (!IsSlotValid(slot))
		{
			return false;
		}
		Dictionary<string, string> customData = CustomData;
		PresetData slot2 = GetSlot(slot);
		if (customData == null || slot2 == null)
		{
			return false;
		}
		customData[KDescription(slot)] = description ?? "";
		return true;
	}

	private static void WriteSlot(int slot, PresetData data)
	{
		Dictionary<string, string> customData = CustomData;
		if (customData != null)
		{
			customData[KName(slot)] = data.Name;
			customData[KDescription(slot)] = data.Description ?? "";
			customData[KNodes(slot)] = data.SerializeNodes();
			customData[KChoices(slot)] = data.SerializeChoices();
			customData[KSavedAt(slot)] = data.SavedAtUtc.ToString("o");
		}
	}

	public static ApplyResult LoadSlotIntoCurrentBuild(int slot, bool forceFree)
	{
		Progress current = Progress.Current;
		if (current == null)
		{
			return new ApplyResult(ApplyOutcome.NoLocalPlayer);
		}
		if (!IsSlotValid(slot))
		{
			return new ApplyResult(ApplyOutcome.EmptySlot);
		}
		PresetData slot2 = GetSlot(slot);
		HashSet<string> hashSet = ((slot2 != null) ? new HashSet<string>(slot2.NodeIds) : new HashSet<string>());
		HashSet<string> hashSet2 = new HashSet<string>(current.Allocated);
		HashSet<string> hashSet3 = new HashSet<string>(hashSet2);
		hashSet3.ExceptWith(hashSet);
		HashSet<string> hashSet4 = new HashSet<string>(hashSet);
		hashSet4.ExceptWith(hashSet2);
		bool freeAllocation = Progress.FreeAllocation;
		if (forceFree)
		{
			Progress.FreeAllocation = true;
		}
		try
		{
			if (hashSet3.Count > 0)
			{
				current.DeallocateSet((ICollection<string>)hashSet3);
			}
			List<string> list = new List<string>(hashSet4);
			bool flag = true;
			while (list.Count > 0 && flag)
			{
				flag = false;
				for (int num = list.Count - 1; num >= 0; num--)
				{
					string text = list[num];
					if (current.CanAllocate(text) && current.Allocate(text))
					{
						list.RemoveAt(num);
						flag = true;
					}
				}
			}
			if (slot2 != null)
			{
				foreach (KeyValuePair<string, int> choice in slot2.Choices)
				{
					if (current.IsAllocated(choice.Key) && current.ChoiceOf(choice.Key) != choice.Value)
					{
						current.Choose(choice.Key, choice.Value);
					}
				}
			}
			current.AfterChange();
			SetActiveSlotSilently(slot);
			SaveCurrentBuildToSlot(slot, slot2?.Name);
			AutoSave.ResetBaseline();
			return (list.Count == 0) ? new ApplyResult(ApplyOutcome.Success) : new ApplyResult(ApplyOutcome.Partial, list);
		}
		finally
		{
			Progress.FreeAllocation = freeAllocation;
		}
	}

	public static bool CurrentBuildMatches(PresetData preset)
	{
		Progress current = Progress.Current;
		if (current == null || preset == null)
		{
			return false;
		}
		if (!current.Allocated.SetEquals(preset.NodeIds))
		{
			return false;
		}
		if (current.Choices.Count != preset.Choices.Count)
		{
			return false;
		}
		foreach (KeyValuePair<string, int> choice in preset.Choices)
		{
			if (!current.Choices.TryGetValue(choice.Key, out var value) || value != choice.Value)
			{
				return false;
			}
		}
		return true;
	}
}
internal sealed class TopBarUI : MonoBehaviour
{
	private const float RightMargin = 16f;

	private const float TopOffset = 35f;

	private const float ButtonHeight = 32f;

	private void OnGUI()
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		if (TreeWindowState.IsOpen && !((Object)(object)Player.m_localPlayer == (Object)null))
		{
			TreePresetsSkin.EnsureBuilt();
			string text = Localization.T("Пресеты", "Presets");
			float num = Mathf.Max(110f, (float)text.Length * 9f + 20f);
			float num2 = (float)Screen.width - 16f - Plugin.TopBarExtraLeftOffset.Value;
			if (TreePresetsSkin.Btn(new Rect(num2 - num, 35f, num, 32f), text))
			{
				PresetMageUI.Toggle();
			}
		}
	}
}
internal static class TreePresetsSkin
{
	public static readonly Color TextGold = new Color(0.9f, 0.78f, 0.48f);

	public static readonly Color TextGoldBright = new Color(0.97f, 0.88f, 0.62f);

	private static bool built;

	public static GUIStyle Box { get; private set; }

	public static GUIStyle Button { get; private set; }

	public static GUIStyle Window { get; private set; }

	public static GUIStyle Label { get; private set; }

	public static GUIStyle TextField { get; private set; }

	public static void EnsureBuilt()
	{
		//IL_0028: 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_0066: 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_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Expected O, but got Unknown
		//IL_00d1: Expected O, but got Unknown
		//IL_00d7: 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_00f0: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Expected O, but got Unknown
		//IL_0115: 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_0124: Expected O, but got Unknown
		//IL_0129: Expected O, but got Unknown
		//IL_0143: 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_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_021b: Unknown result type (might be due to invalid IL or missing references)
		//IL_023f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_0258: Unknown result type (might be due to invalid IL or missing references)
		//IL_025f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0266: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Expected O, but got Unknown
		//IL_0286: Expected O, but got Unknown
		//IL_028c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b9: Expected O, but got Unknown
		//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f3: Expected O, but got Unknown
		//IL_030e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0333: Unknown result type (might be due to invalid IL or missing references)
		if (!built)
		{
			built = true;
			Font font = ValheimFont.Get();
			Texture2D bg = MakeTex(new Color(0.08f, 0.06f, 0.035f, 0.94f));
			Texture2D background = MakeTex(new Color(0.2f, 0.14f, 0.08f, 0.97f));
			Texture2D background2 = MakeTex(new Color(0.33f, 0.23f, 0.12f, 0.99f));
			Texture2D background3 = MakeTex(new Color(0.05f, 0.04f, 0.03f, 0.92f));
			Box = new GUIStyle(GUI.skin.box)
			{
				font = font,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)4,
				fontSize = 16,
				border = new RectOffset(3, 3, 3, 3)
			};
			SetAllStates(Box, bg, TextGold);
			Button = new GUIStyle(GUI.skin.button)
			{
				font = font,
				fontStyle = (FontStyle)1,
				fontSize = 16,
				padding = new RectOffset(8, 8, 6, 6),
				border = new RectOffset(3, 3, 3, 3)
			};
			Button.normal.background = background;
			Button.normal.textColor = TextGold;
			Button.hover.background = background2;
			Button.hover.textColor = TextGoldBright;
			Button.active.background = background2;
			Button.active.textColor = TextGoldBright;
			Button.focused.background = background;
			Button.focused.textColor = TextGold;
			Button.onNormal.background = background;
			Button.onNormal.textColor = TextGold;
			Button.onHover.background = background2;
			Button.onHover.textColor = TextGoldBright;
			Button.onActive.background = background2;
			Button.onActive.textColor = TextGoldBright;
			Button.onFocused.background = background;
			Button.onFocused.textColor = TextGold;
			Window = new GUIStyle(GUI.skin.window)
			{
				font = font,
				fontStyle = (FontStyle)1,
				fontSize = 18,
				padding = new RectOffset(12, 12, 26, 12)
			};
			SetAllStates(Window, bg, TextGoldBright);
			Label = new GUIStyle(GUI.skin.label)
			{
				font = font,
				fontSize = 15
			};
			Label.normal.textColor = TextGold;
			Label.wordWrap = true;
			TextField = new GUIStyle(GUI.skin.textField)
			{
				font = font
			};
			TextField.normal.background = background3;
			TextField.normal.textColor = Color.white;
			TextField.focused.background = background3;
			TextField.focused.textColor = Color.white;
		}
	}

	private static void SetAllStates(GUIStyle style, Texture2D bg, Color textColor)
	{
		//IL_0012: 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_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: 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_00a2: 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)
		style.normal.background = bg;
		style.normal.textColor = textColor;
		style.hover.background = bg;
		style.hover.textColor = textColor;
		style.active.background = bg;
		style.active.textColor = textColor;
		style.focused.background = bg;
		style.focused.textColor = textColor;
		style.onNormal.background = bg;
		style.onNormal.textColor = textColor;
		style.onHover.background = bg;
		style.onHover.textColor = textColor;
		style.onActive.background = bg;
		style.onActive.textColor = textColor;
		style.onFocused.background = bg;
		style.onFocused.textColor = textColor;
	}

	public static bool Btn(string text, params GUILayoutOption[] options)
	{
		EnsureBuilt();
		bool num = GUILayout.Button(text, Button, options);
		if (num)
		{
			UiSfx.PlayClick();
		}
		return num;
	}

	public static bool Btn(Rect rect, string text)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		EnsureBuilt();
		bool num = GUI.Button(rect, text, Button);
		if (num)
		{
			UiSfx.PlayClick();
		}
		return num;
	}

	private static Texture2D MakeTex(Color color)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		//IL_0016: 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)
		Texture2D val = new Texture2D(4, 4);
		Color[] array = (Color[])(object)new Color[16];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = color;
		}
		val.SetPixels(array);
		val.Apply();
		((Object)val).hideFlags = (HideFlags)61;
		return val;
	}
}
internal static class TreeWindowState
{
	public static bool IsOpen
	{
		get
		{
			try
			{
				return TreeWindow.IsOpen;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("TreePresets: TreeWindow.IsOpen недоступен, считаю дерево закрытым: " + ex.Message));
				}
				return false;
			}
		}
	}
}
internal static class UiSfx
{
	private static readonly string[] CandidatePrefabNames = new string[4] { "sfx_gui_button", "gui_select", "sfx_UI_pick", "gui_click" };

	private static AudioClip cachedClip;

	private static float cachedVolume = 1f;

	private static bool probed;

	public static void PlayClick()
	{
		//IL_004a: 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_004f: 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)
		if (!Plugin.PlayUiClickSound.Value)
		{
			return;
		}
		if (!probed)
		{
			probed = true;
			TryResolveClip();
		}
		if ((Object)(object)cachedClip == (Object)null)
		{
			return;
		}
		try
		{
			Camera main = Camera.main;
			Vector3 val = (((Object)(object)main != (Object)null) ? ((Component)main).transform.position : Vector3.zero);
			AudioSource.PlayClipAtPoint(cachedClip, val, cachedVolume);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("TreePresets: не удалось воспроизвести звук клика: " + ex.Message));
		}
	}

	private static void TryResolveClip()
	{
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return;
		}
		string[] candidatePrefabNames = CandidatePrefabNames;
		foreach (string text in candidatePrefabNames)
		{
			GameObject val;
			try
			{
				val = instance.GetPrefab(text);
			}
			catch
			{
				val = null;
			}
			if (!((Object)(object)val == (Object)null))
			{
				AudioSource component = val.GetComponent<AudioSource>();
				if (!((Object)(object)component == (Object)null) && !((Object)(object)component.clip == (Object)null))
				{
					cachedClip = component.clip;
					cachedVolume = ((component.volume > 0f) ? component.volume : 1f);
					Plugin.Log.LogInfo((object)("TreePresets: звук клика взят из префаба \"" + text + "\"."));
					return;
				}
			}
		}
		Plugin.Log.LogWarning((object)("TreePresets: не нашёл звук UI-клика ни по одному из известных имён (" + string.Join(", ", CandidatePrefabNames) + "). Звук при нажатии кнопок не будет играть - см. комментарий в UiSfx.cs, как это поправить вручную."));
	}
}
internal static class ValheimFont
{
	private static readonly string[] FontNameHints = new string[5] { "Norse", "AveriaSerifLibre", "valheim", "Immortal", "UnifrakturMaguntia" };

	private static Font cached;

	private static bool probed;

	public static Font Get()
	{
		if (!probed)
		{
			probed = true;
			TryFind();
		}
		return cached;
	}

	private static void TryFind()
	{
		try
		{
			Font[] array = Resources.FindObjectsOfTypeAll<Font>();
			if (array.Length == 0)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"TreePresets: в памяти не найдено ни одного UnityEngine.Font (игра, скорее всего, использует TextMeshPro) - используется системный шрифт.");
				}
				return;
			}
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)("TreePresets: найденные Font-объекты: " + string.Join(", ", array.Select((Font f) => ((Object)f).name))));
			}
			string[] fontNameHints = FontNameHints;
			foreach (string hint in fontNameHints)
			{
				Font val = ((IEnumerable<Font>)array).FirstOrDefault((Func<Font, bool>)((Font f) => ((Object)f).name.IndexOf(hint, StringComparison.OrdinalIgnoreCase) >= 0));
				if ((Object)(object)val != (Object)null)
				{
					cached = val;
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogInfo((object)("TreePresets: используется шрифт \"" + ((Object)val).name + "\"."));
					}
					return;
				}
			}
			ManualLogSource log4 = Plugin.Log;
			if (log4 != null)
			{
				log4.LogInfo((object)"TreePresets: среди найденных шрифтов нет явного кандидата на \"шрифт Valheim\" - используется системный. Проверьте список выше и допишите нужное имя в ValheimFont.FontNameHints при необходимости.");
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log5 = Plugin.Log;
			if (log5 != null)
			{
				log5.LogWarning((object)("TreePresets: не удалось найти игровой шрифт: " + ex.Message));
			}
		}
	}
}