Decompiled source of SkillDrasil v0.3.2

plugins/SkillDrasil.dll

Decompiled 6 days 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.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SkillDrasil")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+205d2158360dd449e982e6c7a048a3a3b9a4247e")]
[assembly: AssemblyProduct("SkillDrasil")]
[assembly: AssemblyTitle("SkillDrasil")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 SkillDrasil
{
	internal static class Abilities
	{
		private class Def
		{
			public string Id;

			public string Fx;

			public string Sfx;

			public float Cooldown;

			public int Hash;
		}

		private class Slot
		{
			public GameObject Root;

			public Image Icon;

			public Image Sweep;

			public Text Key;

			public Text Time;
		}

		[HarmonyPatch]
		private static class Patches
		{
			[HarmonyPrefix]
			[HarmonyPatch(typeof(Player), "UseHotbarItem")]
			private static bool KeepHotbarFree(Player __instance)
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					return !ModifierHeld;
				}
				return true;
			}
		}

		private static readonly Def[] defs = new Def[5]
		{
			new Def
			{
				Id = "warcry",
				Cooldown = 120f,
				Fx = "fx_GP_Activation",
				Sfx = "sfx_goblinbrute_shout"
			},
			new Def
			{
				Id = "huntersmark",
				Cooldown = 90f,
				Fx = "fx_guardstone_activate",
				Sfx = "sfx_StaffLightning_charge"
			},
			new Def
			{
				Id = "volvaschant",
				Cooldown = 120f,
				Fx = "vfx_HealthUpgrade",
				Sfx = "sfx_greydwarf_shaman_heal"
			},
			new Def
			{
				Id = "galestep",
				Cooldown = 60f,
				Fx = "vfx_StaminaUpgrade",
				Sfx = "sfx_Potion_stamina_Start"
			},
			new Def
			{
				Id = "tirelesstoil",
				Cooldown = 180f,
				Fx = "vfx_Potion_stamina_medium",
				Sfx = "sfx_Potion_stamina_Start_lingering"
			}
		};

		private const float AllyRange = 15f;

		private const float MarkRange = 25f;

		private const float ChantEitr = 30f;

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

		private static readonly List<StatusEffect> made = new List<StatusEffect>();

		private static readonly Slot[] slots = new Slot[5];

		public static bool ModifierHeld => ZInput.GetKey(Plugin.AbilityModifier.Value, false);

		public static Node NodeOf(Branch b)
		{
			return TreeDef.Nodes.First((Node n) => n.IsAbility && n.Branch == b);
		}

		public static float CooldownOf(string id)
		{
			return defs.First((Def d) => d.Id == id).Cooldown;
		}

		public static float Remaining(string id)
		{
			if (!readyAt.TryGetValue(id, out var value))
			{
				return 0f;
			}
			return Mathf.Max(0f, value - Time.time);
		}

		public static string KeyLabel(Branch b)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			return ShortKey(Plugin.AbilityModifier.Value) + "+" + ShortKey(Plugin.AbilityKeys[(int)b].Value);
		}

		private unsafe static string ShortKey(KeyCode k)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected I4, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Invalid comparison between Unknown and I4
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			if ((int)k < 48 || (int)k > 57)
			{
				if ((int)k != 308 && (int)k != 307)
				{
					if ((int)k != 306 && (int)k != 305)
					{
						if ((int)k != 304 && (int)k != 303)
						{
							return ((object)(*(KeyCode*)(&k))/*cast due to .constrained prefix*/).ToString();
						}
						return "Shift";
					}
					return "Ctrl";
				}
				return "Alt";
			}
			return (k - 48).ToString();
		}

		public static void Register()
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			StatusEffect[] array = (StatusEffect[])(object)new StatusEffect[5]
			{
				Make<SE_WarCry>("warcry", 15f),
				Make<SE_Marked>("huntersmark", 15f),
				Make<SE_Chant>("volvaschant", 10f),
				Make<SE_Gale>("galestep", 8f),
				Make<SE_Toil>("tirelesstoil", 30f)
			};
			foreach (StatusEffect val in array)
			{
				ItemManager.Instance.AddStatusEffect(new CustomStatusEffect(val, false));
			}
			Def[] array2 = defs;
			foreach (Def def in array2)
			{
				def.Hash = StringExtensionMethods.GetStableHashCode("SkillDrasil_" + def.Id);
			}
			PrefabManager.OnVanillaPrefabsAvailable += SetIcons;
		}

		private static T Make<T>(string id, float ttl) where T : StatusEffect
		{
			T val = ScriptableObject.CreateInstance<T>();
			((Object)(object)val).name = "SkillDrasil_" + id;
			((StatusEffect)val).m_name = "$sd_name_" + id;
			((StatusEffect)val).m_tooltip = "$sd_effect_" + id;
			((StatusEffect)val).m_ttl = ttl;
			made.Add((StatusEffect)(object)val);
			return val;
		}

		private static void SetIcons()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= SetIcons;
			foreach (StatusEffect item in made)
			{
				item.m_icon = Icons.Get(((Object)item).name.Substring("SkillDrasil_".Length));
			}
		}

		public static void Tick()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (Object.op_Implicit((Object)(object)localPlayer) && !((Character)localPlayer).IsDead() && ModifierHeld && !TreeUI.IsOpen && !Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && !InventoryGui.IsVisible())
			{
				for (int i = 0; i < 5; i++)
				{
					if (ZInput.GetKeyDown(Plugin.AbilityKeys[i].Value, false))
					{
						Cast(localPlayer, NodeOf((Branch)i));
					}
				}
			}
			UpdateBar(localPlayer);
		}

		private static void Cast(Player p, Node n)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			TreeState treeState = Tree.For(p);
			if (treeState == null || treeState.Rank(n.Id) == 0)
			{
				return;
			}
			float num = Remaining(n.Id);
			if (num > 0f)
			{
				((Character)p).Message((MessageType)1, L.T("ability_cooldown", L.Name(n), Mathf.CeilToInt(num)), 0, (Sprite)null, false);
				return;
			}
			Def def = defs.First((Def d) => d.Id == n.Id);
			Vector3 position = ((Component)p).transform.position;
			switch (n.Id)
			{
			case "warcry":
			case "volvaschant":
			{
				if (n.Id == "volvaschant")
				{
					if (!((Character)p).HaveEitr(30f))
					{
						Hud.instance.EitrBarEmptyFlash();
						((Character)p).Message((MessageType)2, "$hud_eitrrequired", 0, (Sprite)null, false);
						return;
					}
					((Character)p).UseEitr(30f);
				}
				List<Player> list2 = new List<Player>();
				Player.GetPlayersInRange(position, 15f, list2);
				foreach (Player item in list2)
				{
					((Character)item).GetSEMan().AddStatusEffect(def.Hash, true, 0, 0f, (short)(-1));
				}
				break;
			}
			case "huntersmark":
			{
				List<Character> list = new List<Character>();
				Character.GetCharactersInRange(position, 25f, list);
				int num2 = 0;
				foreach (Character item2 in list.Where((Character c) => (Object)(object)c != (Object)(object)p && !c.IsDead() && BaseAI.IsEnemy((Character)(object)p, c)))
				{
					item2.GetSEMan().AddStatusEffect(def.Hash, true, 0, 0f, (short)(-1));
					num2++;
				}
				if (num2 == 0)
				{
					((Character)p).Message((MessageType)1, L.T("ability_no_targets"), 0, (Sprite)null, false);
					return;
				}
				break;
			}
			case "galestep":
				((Character)p).AddStamina(((Character)p).GetMaxStamina() * 0.5f);
				((Character)p).GetSEMan().AddStatusEffect(def.Hash, true, 0, 0f, (short)(-1));
				break;
			case "tirelesstoil":
				((Character)p).GetSEMan().AddStatusEffect(def.Hash, true, 0, 0f, (short)(-1));
				break;
			}
			PlayFx(def.Fx, p);
			PlayFx(def.Sfx, p);
			readyAt[n.Id] = Time.time + def.Cooldown;
			((Character)p).Message((MessageType)1, L.Name(n) + "!", 0, (Sprite)null, false);
		}

		private static void PlayFx(string prefabName, Player p)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//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)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_005a: 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_0078: 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 = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(prefabName) : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				EffectList val2 = new EffectList();
				val2.m_effectPrefabs = (EffectData[])(object)new EffectData[1]
				{
					new EffectData
					{
						m_prefab = val,
						m_enabled = true,
						m_attach = true
					}
				};
				val2.Create(((Component)p).transform.position, ((Component)p).transform.rotation, ((Component)p).transform, 1f, -1, default(ZDOID));
			}
		}

		private static void UpdateBar(Player p)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			TreeState treeState = ((Object.op_Implicit((Object)(object)p) && Object.op_Implicit((Object)(object)Hud.instance)) ? Tree.For(p) : null);
			bool flag = treeState != null && !TreeUI.IsOpen && Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront) && !((Character)p).IsDead();
			int num = 0;
			for (int i = 0; i < 5; i++)
			{
				Node node = NodeOf((Branch)i);
				if (!flag || treeState.Rank(node.Id) <= 0)
				{
					if (slots[i] != null && Object.op_Implicit((Object)(object)slots[i].Root))
					{
						slots[i].Root.SetActive(false);
					}
					continue;
				}
				if (slots[i] == null || !Object.op_Implicit((Object)(object)slots[i].Root))
				{
					slots[i] = MakeSlot(node);
				}
				Slot obj = slots[i];
				obj.Root.SetActive(true);
				((RectTransform)obj.Root.transform).anchoredPosition = new Vector2(-50f - (float)num * 66f, 96f);
				num++;
				float num2 = Remaining(node.Id);
				obj.Sweep.fillAmount = num2 / CooldownOf(node.Id);
				obj.Time.text = ((num2 > 0f) ? Mathf.CeilToInt(num2).ToString() : "");
				((Graphic)obj.Icon).color = ((num2 > 0f) ? new Color(0.5f, 0.45f, 0.4f) : new Color(1f, 0.74f, 0.28f));
				obj.Key.text = KeyLabel((Branch)i);
			}
		}

		private static Slot MakeSlot(Node n)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: 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_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_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(1f, 0f);
			GameObject val2 = new GameObject("SkillDrasil_" + n.Id, new Type[1] { typeof(RectTransform) });
			RectTransform rt = (RectTransform)val2.transform;
			((Transform)rt).SetParent(GUIManager.CustomGUIFront.transform, false);
			RectTransform obj = rt;
			Vector2 anchorMin = (rt.anchorMax = val);
			obj.anchorMin = anchorMin;
			rt.sizeDelta = new Vector2(58f, 58f);
			Slot obj2 = new Slot
			{
				Root = val2
			};
			Add("frame", Icons.Get("frame_perk"), 58f, Color.white);
			obj2.Icon = Add("icon", Icons.Get(n.Id), 36f, Color.white);
			obj2.Sweep = Add("sweep", Icons.Get("frame_perk"), 52f, new Color(0f, 0f, 0f, 0.65f));
			obj2.Sweep.type = (Type)3;
			obj2.Sweep.fillMethod = (FillMethod)4;
			obj2.Sweep.fillOrigin = 2;
			obj2.Sweep.fillClockwise = false;
			obj2.Time = GUIManager.Instance.CreateText("", (Transform)(object)rt, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, GUIManager.Instance.AveriaSerifBold, 20, Color.white, true, Color.black, 58f, 30f, false).GetComponent<Text>();
			obj2.Time.alignment = (TextAnchor)4;
			obj2.Key = GUIManager.Instance.CreateText("", (Transform)(object)rt, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -38f), GUIManager.Instance.AveriaSerifBold, 13, new Color(0.88f, 0.82f, 0.68f), true, Color.black, 80f, 20f, false).GetComponent<Text>();
			obj2.Key.alignment = (TextAnchor)4;
			return obj2;
			Image Add(string name, Sprite sprite, float size, Color color)
			{
				//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_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_0041: 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)
				GameObject val4 = new GameObject(name, new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				RectTransform val5 = (RectTransform)val4.transform;
				((Transform)val5).SetParent((Transform)(object)rt, false);
				val5.sizeDelta = new Vector2(size, size);
				Image component = val4.GetComponent<Image>();
				component.sprite = sprite;
				((Graphic)component).color = color;
				((Graphic)component).raycastTarget = false;
				return component;
			}
		}
	}
	internal class SE_WarCry : StatusEffect
	{
		public override void ModifyAttack(SkillType skill, ref HitData hit)
		{
			((DamageTypes)(ref hit.m_damage)).Modify(1.2f);
		}
	}
	internal class SE_Marked : StatusEffect
	{
		public override void OnDamaged(HitData hit, Character attacker)
		{
			hit.ApplyModifier(1.25f);
		}
	}
	internal class SE_Chant : StatusEffect
	{
		public override void UpdateStatusEffect(float dt)
		{
			((StatusEffect)this).UpdateStatusEffect(dt);
			if (base.m_character.IsOwner())
			{
				base.m_character.Heal(base.m_character.GetMaxHealth() * 0.04f * dt, false);
			}
		}
	}
	internal class SE_Gale : StatusEffect
	{
		public override void ModifySpeed(float baseSpeed, ref float speed, Character character, Vector3 dir)
		{
			speed += baseSpeed * 0.2f;
		}
	}
	internal class SE_Toil : StatusEffect
	{
		public override void ModifyAttackStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			//IL_0033: 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)
			//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)
			//IL_0049: Invalid comparison between Unknown and I4
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Invalid comparison between Unknown and I4
			Character character = base.m_character;
			Character obj = ((character is Humanoid) ? character : null);
			SkillType valueOrDefault = ((obj == null) ? ((SkillType?)null) : ((Humanoid)obj).GetCurrentWeapon()?.m_shared.m_skillType).GetValueOrDefault();
			if ((int)valueOrDefault == 12 || (int)valueOrDefault == 7)
			{
				staminaUse = 0f;
			}
		}

		public override void ModifyHomeItemStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			staminaUse = 0f;
		}
	}
	public enum Branch
	{
		Warrior,
		Hunter,
		Seidr,
		Wanderer,
		Artisan
	}
	public sealed class Node
	{
		public readonly string Id;

		public readonly string Name;

		public readonly Branch Branch;

		public readonly int Skill;

		public readonly int Tier;

		public readonly bool IsAbility;

		private readonly int perkRanks;

		public readonly string Effect;

		public readonly string Icon;

		public readonly string Lore;

		public const int KeystoneTier = 4;

		public const int KeystoneSpent = 15;

		public bool IsMastery => Skill != 0;

		public bool IsKeystone => Tier == 4;

		public int MaxRank
		{
			get
			{
				if (!IsMastery)
				{
					return perkRanks;
				}
				return 5;
			}
		}

		public Node(string id, string name, Branch branch, int skill, int tier, string effect, string icon = null, string lore = null, int ranks = 1, bool ability = false)
		{
			Id = id;
			Name = name;
			Branch = branch;
			Skill = skill;
			Tier = tier;
			Effect = effect;
			Icon = icon;
			Lore = lore;
			perkRanks = ranks;
			IsAbility = ability;
		}

		public int RequiredSpent(int nextRank)
		{
			if (!IsMastery)
			{
				if (!IsKeystone)
				{
					if (!IsAbility)
					{
						return Tier * 3;
					}
					return 3;
				}
				return 15;
			}
			return (nextRank - 1) * 2;
		}
	}
	public static class TreeDef
	{
		public const int LevelsPerRank = 20;

		public static readonly string[] BranchNames = new string[5] { "Warrior", "Hunter", "Seiðr", "Wanderer", "Artisan" };

		public static readonly int[] BranchIconSkill = new int[5] { 1, 8, 9, 102, 12 };

		public static readonly string[] BranchLore = new string[5] { "Steel, shield and the will to stand.", "Patience, silence and the clean kill.", "The eitr-craft of the völva.", "Wind at your heels, sky beneath your feet.", "Hands that shape the world." };

		public static readonly Node[] Nodes = new Node[64]
		{
			M("Swords", Branch.Warrior, 1),
			M("Axes", Branch.Warrior, 7),
			M("Clubs", Branch.Warrior, 3),
			M("Polearms", Branch.Warrior, 4),
			M("Spears", Branch.Warrior, 5),
			M("Unarmed", Branch.Warrior, 11),
			M("Blocking", Branch.Warrior, 6),
			P("shieldwall", "Shield-Wall", Branch.Warrior, 1, "Blocking costs 25% less stamina.", "ShieldBanded", "Lock shields. Hold the line."),
			P("berserkgang", "Berserkgang", Branch.Warrior, 2, "Below 30% health: +20% melee damage.", "AxeBerzerkr", "The bear-shirt's fury wakes when blood runs low."),
			P("riposte", "Riposte", Branch.Warrior, 3, "Parries are 30% stronger.", "ShieldBronzeBuckler", "Turn their strength against them."),
			P("ironhide", "Iron Hide", Branch.Warrior, 1, "+3 armor per rank.", null, "Scars are a second coat of mail.", 3),
			P("battlefervor", "Battle Fervor", Branch.Warrior, 2, "Melee attacks cost 10% less stamina per rank.", null, "The fight feeds itself.", 2),
			P("unshaken", "Unshaken", Branch.Warrior, 3, "Take 40% less stagger.", null, "Roots run deep. The oak does not fall."),
			A("warcry", "War Cry", Branch.Warrior, "You and allies within 15 m deal 20% more damage for 15 s.\nCooldown 2 min.", "One voice, and the line holds."),
			K("bearshirt", "Bear Shirt", Branch.Warrior, "+30% melee damage.\nDrawback: 40% less armor.", "Shed the mail. Become the bear."),
			M("Bows", Branch.Hunter, 8),
			M("Crossbows", Branch.Hunter, 14),
			M("Knives", Branch.Hunter, 2),
			M("Sneak", Branch.Hunter, 101),
			P("silentdeath", "Silent Death", Branch.Hunter, 1, "Sneak attacks deal 50% more bonus damage.", "KnifeFlint", "Strike before they know you are there."),
			P("steadyaim", "Steady Aim", Branch.Hunter, 2, "+15% bow and crossbow damage while crouched.", "BowHuntsman", "Breathe. Kneel. Loose."),
			P("fletcher", "Fletcher's Luck", Branch.Hunter, 3, "25% chance to not use up ammo.", "ArrowWood", "A good hunter wastes nothing."),
			P("shadowstep", "Shadowstep", Branch.Hunter, 1, "Sneaking costs 20% less stamina per rank.", null, "Move as the mist moves.", 2),
			P("keeneye", "Keen Eye", Branch.Hunter, 2, "+5% bow and crossbow damage per rank.", null, "The hawk's eye misses nothing.", 3),
			P("ghost", "Ghost", Branch.Hunter, 3, "50% harder to notice while sneaking.", null, "Here, then gone."),
			A("huntersmark", "Hunter's Mark", Branch.Hunter, "Enemies within 25 m take 25% more damage from everyone for 15 s.\nCooldown 90 s.", "Point, and the pack follows."),
			K("lonewolf", "Lone Wolf", Branch.Hunter, "+30% bow, crossbow and knife damage.\nDrawback: other melee weapons deal 30% less.", "No shield-brothers. No mercy."),
			M("Elemental Magic", Branch.Seidr, 9),
			M("Blood Magic", Branch.Seidr, 10),
			P("eitrwell", "Eitr Well", Branch.Seidr, 1, "+25% eitr regeneration.", "Eitr", "Drink deep from the well beneath the roots."),
			P("focusedmind", "Focused Mind", Branch.Seidr, 2, "All eitr costs 20% less.", "MeadEitrMinor", "A quiet mind spends less of itself."),
			P("arcanesurge", "Arcane Surge", Branch.Seidr, 3, "+15% elemental and blood magic damage.", "StaffFireball", "Let the eitr burn bright."),
			P("vigor", "Völva's Vigor", Branch.Seidr, 1, "+15% health regeneration per rank.", null, "The seeress outlives the warrior.", 2),
			P("galdr", "Galdr", Branch.Seidr, 2, "+5% elemental and blood magic damage per rank.", null, "Sing the runes and they answer.", 3),
			P("runeward", "Rune Ward", Branch.Seidr, 3, "Resistant to fire, frost and lightning.", null, "Carved on the skin, the runes hold back the storm."),
			A("volvaschant", "Völva's Chant", Branch.Seidr, "Costs 30 eitr. You and allies within 15 m regain 40% health over 10 s.\nCooldown 2 min.", "The old songs mend what steel has torn."),
			K("volvasoath", "Völva's Oath", Branch.Seidr, "+30% magic damage, +25% eitr regeneration.\nDrawback: weapons deal 30% less.", "Lay down the blade. Take up the staff for good."),
			M("Run", Branch.Wanderer, 102),
			M("Jump", Branch.Wanderer, 100),
			M("Swim", Branch.Wanderer, 103),
			M("Dodge", Branch.Wanderer, 108),
			M("Ride", Branch.Wanderer, 110),
			P("fleetfoot", "Fleetfoot", Branch.Wanderer, 1, "Running costs 20% less stamina.", "MeadStaminaMinor", "The road is long. Your stride is longer."),
			P("secondwind", "Second Wind", Branch.Wanderer, 2, "Dodging costs 25% less stamina.", "MeadStaminaLingering", "One more breath. One more step."),
			P("featherfall", "Featherfall", Branch.Wanderer, 3, "Take 50% less fall damage.", "CapeFeather", "Drift down like a leaf from Yggdrasil's crown."),
			P("sealborn", "Seal-Born", Branch.Wanderer, 1, "Swimming costs 20% less stamina per rank.", null, "The fjord is just another road.", 2),
			P("springstep", "Spring Step", Branch.Wanderer, 2, "Jumping costs 10% less stamina per rank.", null, "Leap the stream. Leap the wall.", 3),
			P("windrunner", "Windrunner", Branch.Wanderer, 3, "+8% movement speed.", null, "Outrun the storm."),
			A("galestep", "Gale Step", Branch.Wanderer, "Restore 50% stamina and move 20% faster for 8 s.\nCooldown 60 s.", "Borrow the storm's legs."),
			K("endlessroad", "Endless Road", Branch.Wanderer, "+40% stamina regeneration.\nDrawback: -100 carry weight.", "Travel light. Travel forever."),
			M("Pickaxes", Branch.Artisan, 12),
			M("Wood Cutting", Branch.Artisan, 13),
			M("Fishing", Branch.Artisan, 104),
			M("Cooking", Branch.Artisan, 105),
			M("Farming", Branch.Artisan, 106),
			M("Crafting", Branch.Artisan, 107),
			P("deeppockets", "Deep Pockets", Branch.Artisan, 1, "+50 carry weight.", "BeltStrength", "Carry the world on your back."),
			P("buildersease", "Builder's Ease", Branch.Artisan, 2, "Building and repairing cost 30% less stamina.", "Hammer", "The hammer remembers the work."),
			P("hearthkeeper", "Hearthkeeper", Branch.Artisan, 3, "Food lasts 25% longer.", "CookedMeat", "A full belly lasts the long night."),
			P("quickstudy", "Quick Study", Branch.Artisan, 1, "+5% XP for every branch per rank.", null, "Every task teaches the willing.", 3),
			P("minersgrit", "Miner's Grit", Branch.Artisan, 2, "Pickaxe swings cost 15% less stamina per rank.", null, "The mountain yields to the patient.", 2),
			P("hardworker", "Hard Worker", Branch.Artisan, 3, "+15% stamina regeneration.", null, "Rest is for the dead."),
			A("tirelesstoil", "Tireless Toil", Branch.Artisan, "For 30 s chopping, mining, building and repairing cost no stamina.\nCooldown 3 min.", "Work until the work is done."),
			K("homesteader", "Homesteader", Branch.Artisan, "+100 carry weight, +25% XP for every branch.\nDrawback: you take 25% more damage.", "Build the hall. Let others hold the shield wall.")
		};

		public static readonly Dictionary<string, Node> ById = Nodes.ToDictionary((Node n) => n.Id);

		public static readonly Dictionary<int, Node> BySkill = Nodes.Where((Node n) => n.IsMastery).ToDictionary((Node n) => n.Skill);

		public const string BossKeyPrefix = "defeated_";

		public static readonly HashSet<string> MilestoneBiomes = new HashSet<string> { "$biome_blackforest", "$biome_swamp", "$biome_mountain", "$biome_plains", "$biome_mistlands", "$biome_ashlands", "$biome_deepnorth", "$biome_ocean" };

		private static Node M(string name, Branch b, int skill)
		{
			return new Node(name.Replace(" ", "").ToLowerInvariant(), name, b, skill, 0, $"+{20} {name} skill levels per rank.");
		}

		private static Node P(string id, string name, Branch b, int tier, string effect, string icon, string lore, int ranks = 1)
		{
			return new Node(id, name, b, 0, tier, effect, icon, lore, ranks);
		}

		private static Node K(string id, string name, Branch b, string effect, string lore)
		{
			return new Node(id, name, b, 0, 4, effect, null, lore);
		}

		private static Node A(string id, string name, Branch b, string effect, string lore)
		{
			return new Node(id, name, b, 0, 0, effect, null, lore, 1, ability: true);
		}
	}
	public enum Block
	{
		None,
		Maxed,
		NeedSpent,
		NeedPerk,
		KeystoneTaken,
		NoPoints
	}
	public sealed class TreeState
	{
		public const string Key = "SkillDrasil";

		private const string Format = "1";

		private const int Branches = 5;

		public readonly float[] Xp = new float[5];

		public readonly int[] Earned = new int[5];

		public int FreeEarned;

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

		public readonly HashSet<string> Claimed = new HashSet<string>();

		public int FreeAvailable
		{
			get
			{
				int num = 0;
				for (int i = 0; i < 5; i++)
				{
					num += Math.Max(0, Spent((Branch)i) - Earned[i]);
				}
				return FreeEarned - num;
			}
		}

		public string Keystone => Ranks.Keys.FirstOrDefault((string id) => Ranks[id] > 0 && TreeDef.ById[id].IsKeystone);

		public int Rank(string id)
		{
			if (!Ranks.TryGetValue(id, out var value))
			{
				return 0;
			}
			return value;
		}

		public int Spent(Branch b)
		{
			return Ranks.Where((KeyValuePair<string, int> kv) => TreeDef.ById[kv.Key].Branch == b).Sum((KeyValuePair<string, int> kv) => kv.Value);
		}

		public int BranchAvailable(Branch b)
		{
			return Math.Max(0, Earned[(int)b] - Spent(b));
		}

		public static float PointCost(int earned, float firstXp, float exponent)
		{
			return firstXp * (float)Math.Pow(earned + 1, exponent);
		}

		public int AddXp(Branch b, float amount, float firstXp, float exponent)
		{
			int num = 0;
			if (float.IsNaN(amount) || float.IsInfinity(amount) || amount <= 0f)
			{
				return 0;
			}
			Xp[(int)b] += amount;
			float num2;
			while ((num2 = PointCost(Earned[(int)b], firstXp, exponent)) > 0f && !float.IsInfinity(num2) && Xp[(int)b] >= num2)
			{
				Xp[(int)b] -= num2;
				Earned[(int)b]++;
				num++;
			}
			return num;
		}

		public Block CanSpend(string id)
		{
			int need;
			return CanSpend(id, out need);
		}

		public Block CanSpend(string id, out int need)
		{
			Node n = TreeDef.ById[id];
			int num = Rank(id);
			need = n.RequiredSpent(num + 1);
			if (num >= n.MaxRank)
			{
				return Block.Maxed;
			}
			if (Spent(n.Branch) < need)
			{
				return Block.NeedSpent;
			}
			if (n.IsAbility && !TreeDef.Nodes.Any((Node p) => p.Branch == n.Branch && p.Tier == 1 && Rank(p.Id) > 0))
			{
				return Block.NeedPerk;
			}
			if (n.IsKeystone && num == 0 && Keystone != null)
			{
				return Block.KeystoneTaken;
			}
			if (BranchAvailable(n.Branch) == 0 && FreeAvailable <= 0)
			{
				return Block.NoPoints;
			}
			return Block.None;
		}

		public bool Spend(string id)
		{
			if (CanSpend(id) != Block.None)
			{
				return false;
			}
			Ranks[id] = Rank(id) + 1;
			return true;
		}

		public void Respec()
		{
			Ranks.Clear();
		}

		public void LoseXp(float factor)
		{
			float num = 1f - Math.Max(0f, Math.Min(1f, factor));
			for (int i = 0; i < 5; i++)
			{
				Xp[i] *= num;
			}
		}

		public void Wipe()
		{
			Array.Clear(Xp, 0, 5);
			Array.Clear(Earned, 0, 5);
			Ranks.Clear();
		}

		public bool Claim(string key)
		{
			if (!Claimed.Add(key))
			{
				return false;
			}
			FreeEarned++;
			return true;
		}

		public static TreeState Migrate(IEnumerable<KeyValuePair<int, float>> vanillaLevels)
		{
			TreeState treeState = new TreeState();
			foreach (KeyValuePair<int, float> vanillaLevel in vanillaLevels)
			{
				if (TreeDef.BySkill.TryGetValue(vanillaLevel.Key, out var value))
				{
					treeState.Earned[(int)value.Branch] += (int)(vanillaLevel.Value / 20f);
				}
			}
			return treeState;
		}

		public string Serialize()
		{
			CultureInfo inv = CultureInfo.InvariantCulture;
			return string.Join("|", "1", string.Join(",", Xp.Select((float x) => x.ToString("R", inv))), string.Join(",", Earned), FreeEarned.ToString(inv), string.Join(",", Ranks.Select((KeyValuePair<string, int> kv) => kv.Key + ":" + kv.Value)), string.Join(",", Claimed));
		}

		public static TreeState Parse(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return null;
			}
			string[] array = raw.Split(new char[1] { '|' });
			if (array.Length != 6 || array[0] != "1")
			{
				return null;
			}
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			try
			{
				TreeState treeState = new TreeState();
				string[] array2 = array[1].Split(new char[1] { ',' });
				string[] array3 = array[2].Split(new char[1] { ',' });
				if (array2.Length != 5 || array3.Length != 5)
				{
					return null;
				}
				for (int i = 0; i < 5; i++)
				{
					treeState.Xp[i] = float.Parse(array2[i], invariantCulture);
					treeState.Earned[i] = int.Parse(array3[i], invariantCulture);
				}
				treeState.FreeEarned = int.Parse(array[3], invariantCulture);
				string[] array4 = array[4].Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
				for (int j = 0; j < array4.Length; j++)
				{
					string[] array5 = array4[j].Split(new char[1] { ':' });
					if (array5.Length == 2 && TreeDef.ById.TryGetValue(array5[0], out var value))
					{
						treeState.Ranks[array5[0]] = Math.Min(value.MaxRank, int.Parse(array5[1], invariantCulture));
					}
				}
				array4 = array[5].Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string item in array4)
				{
					treeState.Claimed.Add(item);
				}
				return treeState;
			}
			catch (FormatException)
			{
				return null;
			}
			catch (OverflowException)
			{
				return null;
			}
		}
	}
	public static class VersionUtil
	{
		public static bool IsNewer(string tag, string current)
		{
			if (Version.TryParse(tag?.TrimStart('v', 'V'), out Version result) && Version.TryParse(current, out Version result2))
			{
				return result > result2;
			}
			return false;
		}
	}
	internal static class Icons
	{
		private static Dictionary<string, Sprite> sprites;

		public static Sprite Get(string name)
		{
			if (sprites == null)
			{
				Load();
			}
			if (sprites.TryGetValue(name, out var value))
			{
				return value;
			}
			Plugin.Log.LogWarning((object)("Missing icon '" + name + "'"));
			return null;
		}

		private static void Load()
		{
			//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_00a6: Expected O, but got Unknown
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			sprites = new Dictionary<string, Sprite>();
			Assembly assembly = typeof(Icons).Assembly;
			byte[] array;
			using (Stream stream = assembly.GetManifestResourceStream("SkillDrasil.atlas.png"))
			{
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			string[] array2;
			using (StreamReader streamReader = new StreamReader(assembly.GetManifestResourceStream("SkillDrasil.atlas.txt")))
			{
				array2 = streamReader.ReadToEnd().Split(new char[1] { '\n' });
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true)
			{
				name = "SkillDrasilAtlas"
			};
			typeof(ImageConversion).GetMethod("LoadImage", new Type[3]
			{
				typeof(Texture2D),
				typeof(byte[]),
				typeof(bool)
			}).Invoke(null, new object[3] { val, array, false });
			((Texture)val).filterMode = (FilterMode)2;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			string[] array3 = array2[0].Split(new char[1] { ' ' });
			int num = int.Parse(array3[0]);
			int num2 = int.Parse(array3[1]);
			Rect val2 = default(Rect);
			for (int i = 1; i < array2.Length; i++)
			{
				string text = array2[i].Trim();
				if (text.Length != 0)
				{
					int num3 = (i - 1) % num2;
					int num4 = (i - 1) / num2;
					((Rect)(ref val2))..ctor((float)(num3 * num), (float)(((Texture)val).height - (num4 + 1) * num), (float)num, (float)num);
					Sprite val3 = Sprite.Create(val, val2, new Vector2(0.5f, 0.5f), 100f);
					((Object)val3).name = text;
					sprites[text] = val3;
				}
			}
		}
	}
	internal static class L
	{
		private static readonly Dictionary<string, string> English = new Dictionary<string, string>
		{
			["free"] = "Free points: {0}",
			["severed_banner"] = "Severed Roots: masteries -1 rank, tier 3 perks and keystone off",
			["close"] = "Close",
			["branch_kind"] = "Branch",
			["branch_help"] = "This branch's skills earn its points.\nBosses and new biomes give free points.",
			["hover_help"] = "Hover a node for details, click to select.",
			["kind_mastery"] = "Mastery  ·  {0}",
			["kind_perk"] = "Tier {0} perk  ·  {1}",
			["kind_keystone"] = "Keystone  ·  {0}",
			["kind_ability"] = "Active ability  ·  {0}",
			["ability_key"] = "Learned. Cast with {0}",
			["ability_cooldown"] = "{0} is ready in {1} s",
			["ability_no_targets"] = "No enemies nearby",
			["block_perk"] = "Learn a tier 1 perk of this branch first",
			["hud_points"] = "SkillDrasil: {0} points to spend  [{1}]",
			["tip_learn"] = "Click, then Learn  ({0} → {1})",
			["tip_maxed"] = "Fully learned",
			["other_skills"] = "Other skills",
			["other_help"] = "Skills added by other mods are not part of the tree. They level up the vanilla way.",
			["other_level"] = "Level {0}",
			["rank_of"] = "Rank {0} / {1}",
			["block_keystone"] = "Only one keystone. Use a Rune of Forgetting to choose again",
			["mastery_effect"] = "+{0} {1} skill levels per rank.",
			["level_max"] = "Level {0} (max)",
			["level_next"] = "Level {0}  →  {1} at rank {2}",
			["learned"] = "Learned",
			["learned_inactive"] = "Learned (inactive: Severed Roots)",
			["not_learned"] = "Not learned",
			["ready"] = "Ready to learn",
			["click_select"] = "click to select",
			["learn_branch"] = "Learn ({0} point)",
			["learn_free"] = "Learn (free point)",
			["branch_line"] = "{0}:  {1} unspent  ·  {2} spent",
			["xp"] = "{0} / {1} XP to next point",
			["block_maxed"] = "Maxed",
			["block_need"] = "Needs {0} points spent in {1}",
			["block_none"] = "No points available",
			["msg_point"] = "{0} point earned ({1} unspent)",
			["msg_milestone"] = "Milestone reached! Free points +{0}",
			["msg_severed"] = "Your roots are severed: branch XP lost",
			["msg_wither"] = "Your tree withers: all branch points lost",
			["msg_migrated"] = "SkillDrasil: {0} points granted from your old skills. Press {1}.",
			["msg_respec"] = "Your roots are unbound. All points refunded.",
			["se_severed"] = "Severed Roots",
			["se_severed_tip"] = "Masteries count one rank lower. Tier 3 perks and your keystone are inactive.",
			["item_rune"] = "Rune of Forgetting",
			["item_rune_desc"] = "Consume to refund every point spent in your SkillDrasil tree.",
			["update"] = "SkillDrasil {0} available - click to open"
		};

		private static readonly Dictionary<string, string> Russian = new Dictionary<string, string>
		{
			["free"] = "Свободные очки: {0}",
			["severed_banner"] = "Подрубленные корни: мастерство −1 ранг, перки 3-го уровня и камень отключены",
			["close"] = "Закрыть",
			["branch_kind"] = "Ветвь",
			["branch_help"] = "Навыки ветви приносят её очки.\nБоссы и новые биомы дают свободные очки.",
			["hover_help"] = "Наведите на узел — подробности, нажмите — выбор.",
			["kind_mastery"] = "Мастерство  ·  {0}",
			["kind_perk"] = "Перк {0}-го уровня  ·  {1}",
			["kind_keystone"] = "Краеугольный камень  ·  {0}",
			["kind_ability"] = "Активная способность  ·  {0}",
			["ability_key"] = "Изучено. Применение: {0}",
			["ability_cooldown"] = "«{0}» будет готово через {1} с",
			["ability_no_targets"] = "Рядом нет врагов",
			["block_perk"] = "Сначала изучите перк 1-го уровня этой ветви",
			["name_warcry"] = "Боевой клич",
			["effect_warcry"] = "Вы и союзники в радиусе 15 м наносите на 20% больше урона 15 с.\nПерезарядка 2 мин.",
			["lore_warcry"] = "Один голос — и строй устоит.",
			["name_huntersmark"] = "Метка охотника",
			["effect_huntersmark"] = "Враги в радиусе 25 м получают на 25% больше урона от всех 15 с.\nПерезарядка 90 с.",
			["lore_huntersmark"] = "Укажи — и стая последует.",
			["name_volvaschant"] = "Песнь вёльвы",
			["effect_volvaschant"] = "Стоит 30 эйтра. Вы и союзники в радиусе 15 м восстанавливаете 40% здоровья за 10 с.\nПерезарядка 2 мин.",
			["lore_volvaschant"] = "Старые песни лечат то, что порвала сталь.",
			["name_galestep"] = "Шаг бури",
			["effect_galestep"] = "Восстанавливает 50% выносливости, +20% к скорости на 8 с.\nПерезарядка 60 с.",
			["lore_galestep"] = "Одолжи у бури её ноги.",
			["name_tirelesstoil"] = "Неустанный труд",
			["effect_tirelesstoil"] = "30 с рубка, добыча, строительство и ремонт не тратят выносливость.\nПерезарядка 3 мин.",
			["lore_tirelesstoil"] = "Работай, пока работа не сделана.",
			["hud_points"] = "SkillDrasil: очков к распределению: {0}  [{1}]",
			["tip_learn"] = "Нажмите, затем «Изучить»  ({0} → {1})",
			["tip_maxed"] = "Изучено полностью",
			["other_skills"] = "Другие навыки",
			["other_help"] = "Навыки из других модов не входят в древо и растут по-обычному.",
			["other_level"] = "Уровень {0}",
			["rank_of"] = "Ранг {0} / {1}",
			["block_keystone"] = "Камень может быть только один. Руна забвения позволит выбрать заново",
			["mastery_effect"] = "+{0} к уровню навыка «{1}» за ранг.",
			["level_max"] = "Уровень {0} (макс.)",
			["level_next"] = "Уровень {0}  →  {1} на ранге {2}",
			["learned"] = "Изучено",
			["learned_inactive"] = "Изучено (неактивно: подрубленные корни)",
			["not_learned"] = "Не изучено",
			["ready"] = "Можно изучить",
			["click_select"] = "нажмите, чтобы выбрать",
			["learn_branch"] = "Изучить (очко ветви «{0}»)",
			["learn_free"] = "Изучить (свободное очко)",
			["branch_line"] = "{0}:  свободно {1}  ·  потрачено {2}",
			["xp"] = "{0} / {1} опыта до следующего очка",
			["block_maxed"] = "Максимум",
			["block_need"] = "Нужно потратить в ветви «{1}» очков: {0}",
			["block_none"] = "Нет доступных очков",
			["msg_point"] = "Получено очко ветви «{0}» (свободно: {1})",
			["msg_milestone"] = "Веха пройдена! Свободные очки +{0}",
			["msg_severed"] = "Ваши корни подрублены: часть опыта ветвей потеряна",
			["msg_wither"] = "Ваше древо увядает: все очки ветвей потеряны",
			["msg_migrated"] = "SkillDrasil: за прежние навыки начислено очков: {0}. Нажмите {1}.",
			["msg_respec"] = "Ваши корни освобождены. Все очки возвращены.",
			["se_severed"] = "Подрубленные корни",
			["se_severed_tip"] = "Мастерство считается на один ранг ниже. Перки 3-го уровня и краеугольный камень неактивны.",
			["item_rune"] = "Руна забвения",
			["item_rune_desc"] = "Используйте, чтобы вернуть все очки, потраченные в древе SkillDrasil.",
			["update"] = "Доступна SkillDrasil {0} — нажмите, чтобы открыть",
			["branch_0"] = "Воин",
			["branch_1"] = "Охотник",
			["branch_2"] = "Сейд",
			["branch_3"] = "Странник",
			["branch_4"] = "Ремесленник",
			["branchlore_0"] = "Сталь, щит и воля выстоять.",
			["branchlore_1"] = "Терпение, тишина и точный удар.",
			["branchlore_2"] = "Колдовство эйтра, искусство вёльвы.",
			["branchlore_3"] = "Ветер за спиной, небо под ногами.",
			["branchlore_4"] = "Руки, что создают мир.",
			["name_shieldwall"] = "Стена щитов",
			["effect_shieldwall"] = "Блок расходует на 25% меньше выносливости.",
			["lore_shieldwall"] = "Сомкнуть щиты. Держать строй.",
			["name_berserkgang"] = "Берсеркганг",
			["effect_berserkgang"] = "При здоровье ниже 30%: +20% к урону в ближнем бою.",
			["lore_berserkgang"] = "Ярость медвежьей шкуры просыпается, когда течёт кровь.",
			["name_riposte"] = "Ответный удар",
			["effect_riposte"] = "Парирование на 30% сильнее.",
			["lore_riposte"] = "Обрати их силу против них.",
			["name_silentdeath"] = "Тихая смерть",
			["effect_silentdeath"] = "Скрытные атаки наносят на 50% больше бонусного урона.",
			["lore_silentdeath"] = "Бей, прежде чем тебя заметят.",
			["name_steadyaim"] = "Твёрдая рука",
			["effect_steadyaim"] = "+15% к урону луков и арбалетов, пока вы пригнулись.",
			["lore_steadyaim"] = "Вдох. Колено. Выстрел.",
			["name_fletcher"] = "Бережливый стрелок",
			["effect_fletcher"] = "Шанс 25% не потратить боеприпас.",
			["lore_fletcher"] = "Хороший охотник ничего не тратит зря.",
			["name_eitrwell"] = "Источник эйтра",
			["effect_eitrwell"] = "+25% к восстановлению эйтра.",
			["lore_eitrwell"] = "Пей из колодца под корнями.",
			["name_focusedmind"] = "Ясный разум",
			["effect_focusedmind"] = "Все затраты эйтра на 20% меньше.",
			["lore_focusedmind"] = "Спокойный разум тратит меньше себя.",
			["name_arcanesurge"] = "Всплеск сейда",
			["effect_arcanesurge"] = "+15% к урону магией стихий и крови.",
			["lore_arcanesurge"] = "Пусть эйтр горит ярко.",
			["name_fleetfoot"] = "Быстроногий",
			["effect_fleetfoot"] = "Бег расходует на 20% меньше выносливости.",
			["lore_fleetfoot"] = "Дорога длинна. Твой шаг длиннее.",
			["name_secondwind"] = "Второе дыхание",
			["effect_secondwind"] = "Уклонение расходует на 25% меньше выносливости.",
			["lore_secondwind"] = "Ещё один вдох. Ещё один шаг.",
			["name_featherfall"] = "Лёгкое падение",
			["effect_featherfall"] = "Урон от падения на 50% меньше.",
			["lore_featherfall"] = "Опустись, как лист с кроны Иггдрасиля.",
			["name_deeppockets"] = "Бездонные карманы",
			["effect_deeppockets"] = "+50 к переносимому весу.",
			["lore_deeppockets"] = "Неси мир на своих плечах.",
			["name_buildersease"] = "Рука мастера",
			["effect_buildersease"] = "Строительство и ремонт расходуют на 30% меньше выносливости.",
			["lore_buildersease"] = "Молот помнит работу.",
			["name_hearthkeeper"] = "Хранитель очага",
			["effect_hearthkeeper"] = "Еда действует на 25% дольше.",
			["lore_hearthkeeper"] = "Сытый желудок переживёт долгую ночь.",
			["name_ironhide"] = "Железная шкура",
			["effect_ironhide"] = "+3 к броне за ранг.",
			["lore_ironhide"] = "Шрамы — вторая кольчуга.",
			["name_battlefervor"] = "Боевой пыл",
			["effect_battlefervor"] = "Атаки ближнего боя расходуют на 10% меньше выносливости за ранг.",
			["lore_battlefervor"] = "Битва питает сама себя.",
			["name_unshaken"] = "Непоколебимый",
			["effect_unshaken"] = "Вы получаете на 40% меньше ошеломления.",
			["lore_unshaken"] = "Корни глубоки. Дуб не падает.",
			["name_bearshirt"] = "Медвежья рубаха",
			["effect_bearshirt"] = "+30% к урону в ближнем бою.\nНедостаток: броня на 40% ниже.",
			["lore_bearshirt"] = "Сбрось кольчугу. Стань медведем.",
			["name_shadowstep"] = "Шаг тени",
			["effect_shadowstep"] = "Скрытное передвижение расходует на 20% меньше выносливости за ранг.",
			["lore_shadowstep"] = "Двигайся, как движется туман.",
			["name_keeneye"] = "Зоркий глаз",
			["effect_keeneye"] = "+5% к урону луков и арбалетов за ранг.",
			["lore_keeneye"] = "Глаз ястреба ничего не упускает.",
			["name_ghost"] = "Призрак",
			["effect_ghost"] = "При скрытном передвижении вас на 50% труднее заметить.",
			["lore_ghost"] = "Был здесь — и исчез.",
			["name_lonewolf"] = "Одинокий волк",
			["effect_lonewolf"] = "+30% к урону луков, арбалетов и ножей.\nНедостаток: остальное оружие ближнего боя наносит на 30% меньше.",
			["lore_lonewolf"] = "Ни братьев по щиту. Ни пощады.",
			["name_vigor"] = "Сила вёльвы",
			["effect_vigor"] = "+15% к восстановлению здоровья за ранг.",
			["lore_vigor"] = "Провидица переживёт воина.",
			["name_galdr"] = "Гальдр",
			["effect_galdr"] = "+5% к урону магией стихий и крови за ранг.",
			["lore_galdr"] = "Пой руны — и они ответят.",
			["name_runeward"] = "Рунный оберег",
			["effect_runeward"] = "Сопротивление огню, холоду и молнии.",
			["lore_runeward"] = "Вырезанные на коже, руны сдерживают бурю.",
			["name_volvasoath"] = "Клятва вёльвы",
			["effect_volvasoath"] = "+30% к урону магией, +25% к восстановлению эйтра.\nНедостаток: оружие наносит на 30% меньше урона.",
			["lore_volvasoath"] = "Отложи клинок. Возьми посох навсегда.",
			["name_sealborn"] = "Рождённый тюленем",
			["effect_sealborn"] = "Плавание расходует на 20% меньше выносливости за ранг.",
			["lore_sealborn"] = "Фьорд — всего лишь ещё одна дорога.",
			["name_springstep"] = "Пружинистый шаг",
			["effect_springstep"] = "Прыжки расходуют на 10% меньше выносливости за ранг.",
			["lore_springstep"] = "Перепрыгни ручей. Перепрыгни стену.",
			["name_windrunner"] = "Бегущий с ветром",
			["effect_windrunner"] = "+8% к скорости передвижения.",
			["lore_windrunner"] = "Обгони бурю.",
			["name_endlessroad"] = "Бесконечный путь",
			["effect_endlessroad"] = "+40% к восстановлению выносливости.\nНедостаток: −100 к переносимому весу.",
			["lore_endlessroad"] = "Путешествуй налегке. Путешествуй вечно.",
			["name_quickstudy"] = "Быстрый ученик",
			["effect_quickstudy"] = "+5% к опыту всех ветвей за ранг.",
			["lore_quickstudy"] = "Каждое дело учит того, кто готов учиться.",
			["name_minersgrit"] = "Хватка рудокопа",
			["effect_minersgrit"] = "Удары киркой расходуют на 15% меньше выносливости за ранг.",
			["lore_minersgrit"] = "Гора уступает терпеливым.",
			["name_hardworker"] = "Трудяга",
			["effect_hardworker"] = "+15% к восстановлению выносливости.",
			["lore_hardworker"] = "Отдыхать будем в Вальхалле.",
			["name_homesteader"] = "Хозяин усадьбы",
			["effect_homesteader"] = "+100 к переносимому весу, +25% к опыту всех ветвей.\nНедостаток: вы получаете на 25% больше урона.",
			["lore_homesteader"] = "Построй чертог. Пусть другие держат стену щитов."
		};

		public static string Language
		{
			get
			{
				Localization instance = Localization.instance;
				return ((instance != null) ? instance.GetSelectedLanguage() : null) ?? "English";
			}
		}

		public static void Register()
		{
			for (int i = 0; i < TreeDef.BranchNames.Length; i++)
			{
				English["branch_" + i] = TreeDef.BranchNames[i];
				English["branchlore_" + i] = TreeDef.BranchLore[i];
			}
			Node[] nodes = TreeDef.Nodes;
			foreach (Node node in nodes)
			{
				if (!node.IsMastery)
				{
					English["name_" + node.Id] = node.Name;
					English["effect_" + node.Id] = node.Effect;
					English["lore_" + node.Id] = node.Lore;
				}
			}
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, Prefixed(English));
			text = "Russian";
			localization.AddTranslation(ref text, Prefixed(Russian));
		}

		private static Dictionary<string, string> Prefixed(Dictionary<string, string> d)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (KeyValuePair<string, string> item in d)
			{
				dictionary["sd_" + item.Key] = item.Value;
			}
			return dictionary;
		}

		public static string T(string key, params object[] args)
		{
			Localization instance = Localization.instance;
			string value = ((instance != null) ? instance.Localize("$sd_" + key) : null);
			if (string.IsNullOrEmpty(value) || value[0] == '[')
			{
				English.TryGetValue(key, out value);
			}
			value = value ?? key;
			if (args.Length == 0)
			{
				return value;
			}
			return string.Format(value, args);
		}

		public static string Branch(Branch b)
		{
			int num = (int)b;
			return T("branch_" + num);
		}

		public static string BranchLore(Branch b)
		{
			int num = (int)b;
			return T("branchlore_" + num);
		}

		public static string Name(Node n)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!n.IsMastery)
			{
				return T("name_" + n.Id);
			}
			Localization instance = Localization.instance;
			string text = ((instance != null) ? instance.Localize("$skill_" + ((object)(SkillType)n.Skill/*cast due to .constrained prefix*/).ToString().ToLowerInvariant()) : null);
			if (!string.IsNullOrEmpty(text) && text[0] != '[')
			{
				return text;
			}
			return n.Name;
		}

		public static string Effect(Node n)
		{
			if (!n.IsMastery)
			{
				return T("effect_" + n.Id);
			}
			return T("mastery_effect", 20, Name(n));
		}

		public static string Lore(Node n)
		{
			return T("lore_" + n.Id);
		}

		public static string Reason(Block block, int need, Branch b)
		{
			return block switch
			{
				Block.NoPoints => T("block_none"), 
				Block.KeystoneTaken => T("block_keystone"), 
				Block.NeedPerk => T("block_perk"), 
				Block.NeedSpent => T("block_need", need, Branch(b)), 
				Block.Maxed => T("block_maxed"), 
				_ => T("ready"), 
			};
		}
	}
	internal static class Tree
	{
		private static Player owner;

		private static TreeState state;

		private static int migratedPoints;

		private static float milestoneTimer;

		public static bool InDeath;

		public static bool PendingSevered;

		public static TreeState Local => For(Player.m_localPlayer);

		public static TreeState For(Player p)
		{
			if (!Object.op_Implicit((Object)(object)p))
			{
				return null;
			}
			if ((Object)(object)p == (Object)(object)owner)
			{
				return state;
			}
			if ((Object)(object)p != (Object)(object)Player.m_localPlayer)
			{
				return null;
			}
			Bind(p);
			return state;
		}

		public static void Bind(Player p)
		{
			owner = p;
			migratedPoints = 0;
			p.m_customData.TryGetValue("SkillDrasil", out var value);
			state = TreeState.Parse(value);
			if (state == null)
			{
				if (!string.IsNullOrEmpty(value))
				{
					Plugin.Log.LogError((object)"Unreadable SkillDrasil data, kept as backup and starting over.");
					p.m_customData["SkillDrasil.bak"] = value;
				}
				state = TreeState.Migrate(p.m_skills.m_skillData.Select((KeyValuePair<SkillType, Skill> kv) => new KeyValuePair<int, float>((int)kv.Key, kv.Value.m_level)));
				migratedPoints = state.Earned.Sum();
			}
		}

		public static void Reset()
		{
			state = new TreeState();
		}

		public static bool Severed(Player p)
		{
			return ((Character)p).GetSEMan().HaveStatusEffect(Perks.SeveredHash);
		}

		public static bool PerkActive(string id)
		{
			return PerkRank(id) > 0;
		}

		public static int PerkRank(string id)
		{
			Player localPlayer = Player.m_localPlayer;
			int num = For(localPlayer)?.Rank(id) ?? 0;
			if (num <= 0 || TreeDef.ById[id].Tier < 3 || !Severed(localPlayer))
			{
				return num;
			}
			return 0;
		}

		public static void Save(Player p)
		{
			if ((Object)(object)p == (Object)(object)owner && state != null)
			{
				p.m_customData["SkillDrasil"] = state.Serialize();
			}
		}

		public static void OnSpawned(Player p)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)p != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			For(p);
			if (PendingSevered)
			{
				PendingSevered = false;
				StatusEffect val = ((Character)p).GetSEMan().AddStatusEffect(Perks.SeveredHash, true, 0, 0f, (short)(-1));
				if (Object.op_Implicit((Object)(object)val))
				{
					val.m_ttl = Plugin.SeveredSeconds.Value;
				}
			}
			if (migratedPoints > 0)
			{
				((Character)p).Message((MessageType)2, L.T("msg_migrated", migratedPoints, Plugin.OpenKey.Value), 0, (Sprite)null, false);
				migratedPoints = 0;
			}
		}

		public static void Tick(Player p)
		{
			if ((Object)(object)p != (Object)(object)Player.m_localPlayer || ((Character)p).IsDead())
			{
				return;
			}
			SEMan sEMan = ((Character)p).GetSEMan();
			if (!sEMan.HaveStatusEffect(Perks.CarrierHash))
			{
				sEMan.AddStatusEffect(Perks.CarrierHash, false, 0, 0f, (short)(-1));
			}
			if (!((milestoneTimer -= Time.deltaTime) > 0f) && Object.op_Implicit((Object)(object)ZoneSystem.instance))
			{
				milestoneTimer = 5f;
				TreeState treeState = For(p);
				int num = (from k in ZoneSystem.instance.GetGlobalKeys()
					where k.StartsWith("defeated_")
					select k).Concat(p.m_knownBiome.Where(TreeDef.MilestoneBiomes.Contains)).Count(treeState.Claim);
				if (num > 0)
				{
					((Character)p).Message((MessageType)1, L.T("msg_milestone", num), 0, (Sprite)null, false);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class SkillPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Load")]
		private static void Load(Player __instance)
		{
			Tree.Bind(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "Save")]
		private static void Save(Player __instance)
		{
			Tree.Save(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "OnSpawned")]
		private static void OnSpawned(Player __instance)
		{
			Tree.OnSpawned(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Update")]
		private static void Update(Player __instance)
		{
			Tree.Tick(__instance);
		}

		private static bool InTree(SkillType skill, out Node node)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected I4, but got Unknown
			return TreeDef.BySkill.TryGetValue((int)skill, out node);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "GetSkillLevel")]
		private static bool GetSkillLevel(Skills __instance, SkillType skillType, ref float __result)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			TreeState treeState = Tree.For(__instance.m_player);
			if (treeState == null || !InTree(skillType, out var node))
			{
				return true;
			}
			int num = treeState.Rank(node.Id);
			if (num > 0 && Tree.Severed(__instance.m_player))
			{
				num--;
			}
			float num2 = num * 20;
			((Character)__instance.m_player).GetSEMan().ModifySkillLevel(skillType, ref num2);
			__result = Mathf.Floor(num2);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "RaiseSkill")]
		private static bool RaiseSkill(Skills __instance, SkillType skillType, float factor)
		{
			//IL_000f: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			TreeState treeState = Tree.For(__instance.m_player);
			if (treeState == null || !InTree(skillType, out var node))
			{
				return true;
			}
			SkillDef skillDef = __instance.GetSkillDef(skillType);
			float amount = (skillDef?.m_increseStep ?? 1f) * factor * Game.m_skillGainRate;
			if (treeState.AddXp(node.Branch, amount, Plugin.FirstPointXp.Value, Plugin.CostExponent.Value) > 0)
			{
				Player player = __instance.m_player;
				player.OnSkillLevelup(skillType, 0f);
				((Character)player).Message((MessageType)1, L.T("msg_point", L.Branch(node.Branch), treeState.BranchAvailable(node.Branch)), 0, skillDef?.m_icon, false);
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "OnDeath")]
		private static bool OnDeath(Skills __instance)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			TreeState treeState = Tree.For(__instance.m_player);
			if (treeState == null)
			{
				return true;
			}
			float num = __instance.m_DeathLowerFactor * Game.m_skillReductionRate;
			treeState.LoseXp(num);
			foreach (KeyValuePair<SkillType, Skill> skillDatum in __instance.m_skillData)
			{
				if (!InTree(skillDatum.Key, out var _))
				{
					Skill value = skillDatum.Value;
					value.m_level -= skillDatum.Value.m_level * num;
					skillDatum.Value.m_accumulator = 0f;
				}
			}
			Tree.PendingSevered = true;
			((Character)__instance.m_player).Message((MessageType)1, L.T("msg_severed"), 0, (Sprite)null, false);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static void PlayerDeathStart()
		{
			Tree.InDeath = true;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static void PlayerDeathEnd()
		{
			Tree.InDeath = false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "Clear")]
		private static bool Clear(Skills __instance)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			TreeState treeState = Tree.For(__instance.m_player);
			if (treeState == null)
			{
				return true;
			}
			if (!Tree.InDeath)
			{
				Tree.Reset();
				return true;
			}
			treeState.Wipe();
			Node node;
			foreach (SkillType item in __instance.m_skillData.Keys.Where((SkillType k) => !InTree(k, out node)).ToList())
			{
				__instance.m_skillData.Remove(item);
			}
			((Character)__instance.m_player).Message((MessageType)1, L.T("msg_wither"), 0, (Sprite)null, false);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "CheatRaiseSkill")]
		private static bool CheatRaiseSkill()
		{
			return BlockCheat();
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "CheatResetSkill")]
		private static bool CheatResetSkill()
		{
			return BlockCheat();
		}

		private static bool BlockCheat()
		{
			Console.instance.Print("SkillDrasil replaces skills. Use skilldrasil_points <n> instead.");
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(InventoryGui), "OnOpenSkills")]
		private static bool OnOpenSkills()
		{
			InventoryGui.instance.Hide();
			TreeUI.Open();
			return false;
		}
	}
	internal static class Perks
	{
		public static readonly int CarrierHash = StringExtensionMethods.GetStableHashCode("SkillDrasil_Perks");

		public static readonly int SeveredHash = StringExtensionMethods.GetStableHashCode("SkillDrasil_Severed");

		private const string RuneName = "SkillDrasil_RuneOfForgetting";

		private static StatusEffect severed;

		private static StatusEffect forget;

		public static void Register()
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			SE_Perks sE_Perks = ScriptableObject.CreateInstance<SE_Perks>();
			((Object)sE_Perks).name = "SkillDrasil_Perks";
			((StatusEffect)sE_Perks).m_name = "SkillDrasil";
			severed = ScriptableObject.CreateInstance<StatusEffect>();
			((Object)severed).name = "SkillDrasil_Severed";
			severed.m_name = "$sd_se_severed";
			severed.m_tooltip = "$sd_se_severed_tip";
			forget = (StatusEffect)(object)ScriptableObject.CreateInstance<SE_Forget>();
			((Object)forget).name = "SkillDrasil_Forget";
			forget.m_name = "Rune of Forgetting";
			forget.m_ttl = 1f;
			StatusEffect[] array = (StatusEffect[])(object)new StatusEffect[3] { sE_Perks, severed, forget };
			foreach (StatusEffect val in array)
			{
				ItemManager.Instance.AddStatusEffect(new CustomStatusEffect(val, false));
			}
			PrefabManager.OnVanillaPrefabsAvailable += AddRune;
		}

		private static void AddRune()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			PrefabManager.OnVanillaPrefabsAvailable -= AddRune;
			severed.m_icon = Icons.Get("se_severed");
			ItemConfig val = new ItemConfig();
			val.Name = "$sd_item_rune";
			val.Description = "$sd_item_rune_desc";
			val.CraftingStation = CraftingStations.Workbench;
			val.Icons = (Sprite[])(object)new Sprite[1] { Icons.Get("item_rune") };
			ItemConfig val2 = val;
			val2.AddRequirement("Stone", 10, 0);
			val2.AddRequirement("Resin", 10, 0);
			val2.AddRequirement("GreydwarfEye", 5, 0);
			CustomItem val3 = new CustomItem("SkillDrasil_RuneOfForgetting", "MeadTasty", val2);
			val3.ItemDrop.m_itemData.m_shared.m_consumeStatusEffect = forget;
			ItemManager.Instance.AddItem(val3);
		}
	}
	internal class SE_Perks : StatusEffect
	{
		private static readonly List<DamageModPair> runeWard = new List<DamageModPair>
		{
			new DamageModPair
			{
				m_type = (DamageType)32,
				m_modifier = (DamageModifier)1
			},
			new DamageModPair
			{
				m_type = (DamageType)64,
				m_modifier = (DamageModifier)1
			},
			new DamageModPair
			{
				m_type = (DamageType)128,
				m_modifier = (DamageModifier)1
			}
		};

		private SkillType Weapon
		{
			get
			{
				//IL_0033: 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)
				Character character = base.m_character;
				Character obj = ((character is Humanoid) ? character : null);
				return ((obj == null) ? ((SkillType?)null) : ((Humanoid)obj).GetCurrentWeapon()?.m_shared.m_skillType).GetValueOrDefault();
			}
		}

		private static bool On(string id)
		{
			return Tree.PerkActive(id);
		}

		private static int Rank(string id)
		{
			return Tree.PerkRank(id);
		}

		public static float More(float pct)
		{
			return 1f + pct * Plugin.PerkStrength.Value;
		}

		public static float Less(float pct)
		{
			return Mathf.Max(0f, 1f - pct * Plugin.PerkStrength.Value);
		}

		private static float Flat(float amount)
		{
			return amount * Plugin.PerkStrength.Value;
		}

		private static bool IsMagic(SkillType s)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			if ((int)s != 9)
			{
				return (int)s == 10;
			}
			return true;
		}

		private static bool IsRanged(SkillType s)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)s != 8)
			{
				return (int)s == 14;
			}
			return true;
		}

		private static bool IsMelee(SkillType s)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			if ((int)s != 1 && (int)s != 7 && (int)s != 3 && (int)s != 4 && (int)s != 5 && (int)s != 2)
			{
				return (int)s == 11;
			}
			return true;
		}

		public override void ModifyAttack(SkillType skill, ref HitData hit)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Invalid comparison between Unknown and I4
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			Character character = base.m_character;
			Player val = (Player)(object)((character is Player) ? character : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				if (On("berserkgang") && IsMelee(skill) && ((Character)val).GetHealthPercentage() < 0.3f)
				{
					((DamageTypes)(ref hit.m_damage)).Modify(More(0.2f));
				}
				if (On("silentdeath"))
				{
					HitData obj = hit;
					obj.m_backstabBonus *= More(0.5f);
				}
				if (On("steadyaim") && IsRanged(skill) && ((Character)val).IsCrouching())
				{
					((DamageTypes)(ref hit.m_damage)).Modify(More(0.15f));
				}
				if (On("arcanesurge") && IsMagic(skill))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(More(0.15f));
				}
				if (IsRanged(skill) && Rank("keeneye") > 0)
				{
					((DamageTypes)(ref hit.m_damage)).Modify(More(0.05f * (float)Rank("keeneye")));
				}
				if (IsMagic(skill) && Rank("galdr") > 0)
				{
					((DamageTypes)(ref hit.m_damage)).Modify(More(0.05f * (float)Rank("galdr")));
				}
				if (On("bearshirt") && IsMelee(skill))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(More(0.3f));
				}
				if (On("lonewolf"))
				{
					((DamageTypes)(ref hit.m_damage)).Modify((IsRanged(skill) || (int)skill == 2) ? More(0.3f) : (IsMelee(skill) ? 0.7f : 1f));
				}
				if (On("volvasoath"))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(IsMagic(skill) ? More(0.3f) : 0.7f);
				}
			}
		}

		public override void OnDamaged(HitData hit, Character attacker)
		{
			if (On("homesteader"))
			{
				hit.ApplyModifier(1.25f);
			}
		}

		public override void ModifyArmorMods(ref float armor)
		{
			armor += Flat(3f * (float)Rank("ironhide"));
			if (On("bearshirt"))
			{
				armor *= 0.6f;
			}
		}

		public override void ModifyAttackStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			//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_002b: Invalid comparison between Unknown and I4
			SkillType weapon = Weapon;
			if (IsMelee(weapon))
			{
				staminaUse *= Less(0.1f * (float)Rank("battlefervor"));
			}
			if ((int)weapon == 12)
			{
				staminaUse *= Less(0.15f * (float)Rank("minersgrit"));
			}
		}

		public override void ModifyStagger(float baseValue, ref float use)
		{
			if (On("unshaken"))
			{
				use *= Less(0.4f);
			}
		}

		public override void ModifySneakStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			staminaUse *= Less(0.2f * (float)Rank("shadowstep"));
		}

		public override void ModifyStealth(float baseStealth, ref float stealth)
		{
			if (On("ghost"))
			{
				stealth *= Less(0.5f);
			}
		}

		public override void ModifyHealthRegen(ref float regenMultiplier)
		{
			regenMultiplier += Flat(0.15f * (float)Rank("vigor"));
		}

		public override void ModifyDamageMods(ref DamageModifiers modifiers)
		{
			if (On("runeward"))
			{
				((DamageModifiers)(ref modifiers)).Apply(runeWard);
			}
		}

		public override void ModifySwimStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			staminaUse *= Less(0.2f * (float)Rank("sealborn"));
		}

		public override void ModifyJumpStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			staminaUse *= Less(0.1f * (float)Rank("springstep"));
		}

		public override void ModifySpeed(float baseSpeed, ref float speed, Character character, Vector3 dir)
		{
			if (On("windrunner") && !character.IsSwimming())
			{
				speed += baseSpeed * Flat(0.08f);
			}
		}

		public override void ModifyStaminaRegen(ref float staminaRegen)
		{
			if (On("hardworker"))
			{
				staminaRegen += Flat(0.15f);
			}
			if (On("endlessroad"))
			{
				staminaRegen += Flat(0.4f);
			}
		}

		public override void ModifyRaiseSkill(SkillType skill, ref float value)
		{
			value += Flat(0.05f * (float)Rank("quickstudy"));
			if (On("homesteader"))
			{
				value += Flat(0.25f);
			}
		}

		public override void ModifyBlockStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			if (On("shieldwall"))
			{
				staminaUse *= Less(0.25f);
			}
		}

		public override void ModifyTimedBlockBonus(ref float timedBlockBonus)
		{
			if (On("riposte"))
			{
				timedBlockBonus *= More(0.3f);
			}
		}

		public override void ModifyEitrRegen(ref float eitrRegen)
		{
			if (On("eitrwell"))
			{
				eitrRegen *= More(0.25f);
			}
			if (On("volvasoath"))
			{
				eitrRegen *= More(0.25f);
			}
		}

		public override void ModifyRunStaminaDrain(float baseDrain, ref float drain, Vector3 dir)
		{
			if (On("fleetfoot"))
			{
				drain *= Less(0.2f);
			}
		}

		public override void ModifyDodgeStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			if (On("secondwind"))
			{
				staminaUse *= Less(0.25f);
			}
		}

		public override void ModifyFallDamage(float baseDamage, ref float damage)
		{
			if (On("featherfall"))
			{
				damage *= Less(0.5f);
			}
		}

		public override void ModifyMaxCarryWeight(float baseLimit, ref float limit)
		{
			if (On("deeppockets"))
			{
				limit += Flat(50f);
			}
			if (On("homesteader"))
			{
				limit += Flat(100f);
			}
			if (On("endlessroad"))
			{
				limit -= 100f;
			}
		}

		public override void ModifyHomeItemStaminaUsage(float baseStaminaUse, ref float staminaUse)
		{
			if (On("buildersease"))
			{
				staminaUse *= Less(0.3f);
			}
		}
	}
	internal class SE_Forget : StatusEffect
	{
		public override void Setup(Character character)
		{
			((StatusEffect)this).Setup(character);
			if (!((Object)(object)character != (Object)(object)Player.m_localPlayer))
			{
				Tree.Local?.Respec();
				character.Message((MessageType)2, L.T("msg_respec"), 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch]
	internal static class PerkPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(Attack), "UseAmmo")]
		private static void FletchersLuck(Attack __instance, bool __result, ref ItemData ammoItem)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			if (__result && ammoItem != null && !((Object)(object)__instance.m_character != (Object)(object)Player.m_localPlayer) && (int)ammoItem.m_shared.m_itemType != 2)
			{
				Inventory inventory = __instance.m_character.GetInventory();
				if (Tree.PerkActive("fletcher") && inventory.ContainsItem(ammoItem) && !(Random.value >= 0.25f * Plugin.PerkStrength.Value))
				{
					ItemData obj = ammoItem;
					obj.m_stack++;
					inventory.Changed(false, false);
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "UseEitr")]
		private static void FocusedMind(Player __instance, ref float v)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && Tree.PerkActive("focusedmind"))
			{
				v *= SE_Perks.Less(0.2f);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "UpdateFood")]
		private static void Hearthkeeper(Player __instance, ref float dt)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && Tree.PerkActive("hearthkeeper"))
			{
				dt /= SE_Perks.More(0.25f);
			}
		}
	}
	[BepInPlugin("rgboutl4w.skilldrasil", "SkillDrasil", "0.3.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "rgboutl4w.skilldrasil";

		public const string ModName = "SkillDrasil";

		public const string Version = "0.3.2";

		private const string ReleasesApi = "https://api.github.com/repos/RGB-Outl4w/SkillDrasil/releases/latest";

		private const string ReleasesPage = "https://github.com/RGB-Outl4w/SkillDrasil/releases/latest";

		public static ConfigEntry<float> FirstPointXp;

		public static ConfigEntry<float> CostExponent;

		public static ConfigEntry<float> SeveredSeconds;

		public static ConfigEntry<float> PerkStrength;

		public static ConfigEntry<KeyCode> OpenKey;

		public static ConfigEntry<bool> ShowPointsHint;

		public static ConfigEntry<KeyCode> AbilityModifier;

		public static readonly ConfigEntry<KeyCode>[] AbilityKeys = new ConfigEntry<KeyCode>[5];

		private static ConfigEntry<bool> checkForUpdates;

		private static string latestRelease;

		public static ManualLogSource Log;

		private void Awake()
		{
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			FirstPointXp = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "Progression", "FirstPointXp", 60f, "Branch XP for the first point. Point n costs FirstPointXp * n^CostExponent.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			CostExponent = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "Progression", "CostExponent", 1.25f, "How steeply point cost grows. 1 = linear, 1.5 = vanilla-like (slow).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			if (FirstPointXp.Value == 80f && CostExponent.Value == 1.5f)
			{
				FirstPointXp.Value = (float)((ConfigEntryBase)FirstPointXp).DefaultValue;
				CostExponent.Value = (float)((ConfigEntryBase)CostExponent).DefaultValue;
			}
			SeveredSeconds = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "Balance", "SeveredRootsSeconds", 600f, "Duration of the Severed Roots debuff after death.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config = ((BaseUnityPlugin)this).Config;
			AcceptableValueBase val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f);
			PerkStrength = ConfigFileExtensions.BindConfig<float>(config, "Balance", "PerkStrength", 1f, "Scales every perk and keystone bonus (drawbacks stay fixed). 0.5 = half as strong, 2 = twice.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ShowPointsHint = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "Client", "ShowPointsHint", true, "Show a HUD hint while you have unspent points.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			AbilityModifier = ConfigFileExtensions.BindConfig<KeyCode>(((BaseUnityPlugin)this).Config, "Abilities", "Modifier", (KeyCode)308, "Hold this and press an ability key to cast. While held, number keys don't use the hotbar.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			for (int i = 0; i < 5; i++)
			{
				AbilityKeys[i] = ConfigFileExtensions.BindConfig<KeyCode>(((BaseUnityPlugin)this).Config, "Abilities", "Key" + TreeDef.BranchNames[i].Replace("ð", "d"), (KeyCode)(49 + i), "Ability key for the " + TreeDef.BranchNames[i] + " branch.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			}
			OpenKey = ConfigFileExtensions.BindConfig<KeyCode>(((BaseUnityPlugin)this).Config, "Client", "OpenTreeKey", (KeyCode)107, "Key that opens the skill tree.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			checkForUpdates = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "Client", "CheckForUpdates", true, "Check GitHub for a newer release on the main menu.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			L.Register();
			Perks.Register();
			Abilities.Register();
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PointsCommand());
			new Harmony("rgboutl4w.skilldrasil").PatchAll();
			if (!GUIManager.IsHeadless() && checkForUpdates.Value)
			{
				GUIManager.OnCustomGUIAvailable += ShowUpdateNotice;
				((MonoBehaviour)this).StartCoroutine(CheckForUpdate());
			}
		}

		private void Update()
		{
			TreeUI.Tick();
			Abilities.Tick();
		}

		private IEnumerator CheckForUpdate()
		{
			UnityWebRequest req = UnityWebRequest.Get("https://api.github.com/repos/RGB-Outl4w/SkillDrasil/releases/latest");
			try
			{
				req.SetRequestHeader("User-Agent", "SkillDrasil");
				req.timeout = 5;
				yield return req.SendWebRequest();
				if ((int)req.result != 1)
				{
					Log.LogDebug((object)("Update check failed: " + req.error));
					yield break;
				}
				Match match = Regex.Match(req.downloadHandler.text, "\"tag_name\"\\s*:\\s*\"([^\"]+)\"");
				if (!match.Success || !VersionUtil.IsNewer(match.Groups[1].Value, "0.3.2"))
				{
					yield break;
				}
				latestRelease = match.Groups[1].Value;
				Log.LogWarning((object)("SkillDrasil " + latestRelease + " is available (installed 0.3.2): https://github.com/RGB-Outl4w/SkillDrasil/releases/latest"));
				ShowUpdateNotice();
			}
			finally
			{
				((IDisposable)req)?.Dispose();
			}
		}

		private static void ShowUpdateNotice()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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: Expected O, but got Unknown
			if (latestRelease != null && Object.op_Implicit((Object)(object)FejdStartup.instance) && Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront))
			{
				GameObject go = GUIManager.Instance.CreateButton(L.T("update", latestRelease), GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -40f), 480f, 36f);
				((UnityEvent)go.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
				{
					Application.OpenURL("https://github.com/RGB-Outl4w/SkillDrasil/releases/latest");
					Object.Destroy((Object)(object)go);
				});
			}
		}
	}
	internal class PointsCommand : ConsoleCommand
	{
		public override string Name => "skilldrasil_points";

		public override string Help => "Add free SkillDrasil points: skilldrasil_points <n>";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			TreeState local = Tree.Local;
			if (local == null)
			{
				Console.instance.Print("No local player.");
				return;
			}
			int result;
			int num = ((args.Length == 0 || !int.TryParse(args[0], out result)) ? 1 : result);
			local.FreeEarned += num;
			Console.instance.Print($"SkillDrasil: +{num} free points ({local.FreeAvailable} available).");
		}
	}
	internal static class TreeUI
	{
		private class NodeView
		{
			public Image Ring;

			public Image Frame;

			public Image Icon;

			public Image[] Pips;

			public Text Label;
		}

		private static readonly Vector2 Mid = new Vector2(0.5f, 0.5f);

		private static readonly Color Gold = new Color(1f, 0.74f, 0.28f, 1f);

		private static readonly Color Pale = new Color(0.88f, 0.82f, 0.68f, 1f);

		private static readonly Color Dim = new Color(0.38f, 0.36f, 0.33f, 1f);

		private static readonly Color DimFrame = new Color(0.6f, 0.6f, 0.6f, 1f);

		private static readonly Color Bark = new Color(0.28f, 0.2f, 0.13f, 1f);

		private static readonly Color Sap = new Color(0.95f, 0.62f, 0.18f, 1f);

		private static readonly Color Good = new Color(0.55f, 0.9f, 0.45f, 1f);

		private static readonly Color Bad = new Color(1f, 0.45f, 0.4f, 1f);

		private static readonly Color Shade = new Color(0f, 0f, 0f, 0.4f);

		private const float TrunkBase = -140f;

		private const float TrunkTop = 180f;

		private const float MaxGate = 15f;

		private static GameObject panel;

		private static string builtLanguage;

		private static RectTransform canvas;

		private static Text freeText;

		private static Text severedText;

		private static readonly Button[] tabs = (Button[])(object)new Button[5];

		private static readonly Text[] tabLabels = (Text[])(object)new Text[5];

		private static readonly Image[] tabIcons = (Image[])(object)new Image[5];

		private static readonly Dictionary<string, NodeView> views = new Dictionary<string, NodeView>();

		private static readonly List<(Image line, string node)> roots = new List<(Image, string)>();

		private static RectTransform trunkFill;

		private static Image cardFrame;

		private static Image cardIcon;

		private static Image xpFill;

		private static Text cardName;

		private static Text cardKind;

		private static Text cardEffect;

		private static Text cardLore;

		private static Text cardNow;

		private static Text cardReq;

		private static Text branchLine;

		private static Text xpText;

		private static Image[] cardPips;

		private static Button learn;

		private static Text learnLabel;

		private static Branch tab;

		private static string selected;

		private static string hovered;

		private static bool showingOther;

		private static Button otherButton;

		private static RectTransform tooltip;

		private static Text tooltipText;

		private static Text hudHint;

		private static float hudTimer;

		public static bool IsOpen
		{
			get
			{
				if (Object.op_Implicit((Object)(object)panel))
				{
					return panel.activeSelf;
				}
				return false;
			}
		}

		private static Font HeadingFont
		{
			get
			{
				if (!(L.Language == "English"))
				{
					return GUIManager.Instance.AveriaSerifBold;
				}
				return GUIManager.Instance.NorseBold;
			}
		}

		public static void Tick()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			//IL_009a: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			if (IsOpen && (ZInput.GetKeyDown((KeyCode)27, true) || ZInput.GetKeyDown(Plugin.OpenKey.Value, true) || !Object.op_Implicit((Object)(object)Player.m_localPlayer)))
			{
				Close();
			}
			else if (!IsOpen && ZInput.GetKeyDown(Plugin.OpenKey.Value, true) && CanOpen())
			{
				Open();
			}
			if (IsOpen && ((Component)tooltip).gameObject.activeSelf)
			{
				Vector2 val = default(Vector2);
				RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)panel.transform, Vector2.op_Implicit(ZInput.pointerPosition), (Camera)null, ref val);
				tooltip.anchoredPosition = val + new Vector2(18f, -18f);
			}
			if ((hudTimer -= Time.deltaTime) <= 0f)
			{
				hudTimer = 1f;
				UpdateHudHint();
			}
		}

		private static void UpdateHudHint()
		{
			//IL_013f: 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_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_00e4: 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)
			TreeState treeState = ((Object.op_Implicit((Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)Hud.instance)) ? Tree.Local : null);
			int num = 0;
			if (treeState != null)
			{
				for (int i = 0; i < 5; i++)
				{
					num += treeState.BranchAvailable((Branch)i);
				}
				num += Mathf.Max(0, treeState.FreeAvailable);
			}
			if (num <= 0 || IsOpen || !Plugin.ShowPointsHint.Value || !Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront))
			{
				if (Object.op_Implicit((Object)(object)hudHint))
				{
					((Component)hudHint).gameObject.SetActive(false);
				}
				return;
			}
			if (!Object.op_Implicit((Object)(object)hudHint))
			{
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(1f, 0f);
				hudHint = GUIManager.Instance.CreateText("", GUIManager.CustomGUIFront.transform, val, val, new Vector2(-200f, 34f), GUIManager.Instance.AveriaSerifBold, 18, GUIManager.Instance.ValheimOrange, true, Color.black, 380f, 30f, false).GetComponent<Text>();
				hudHint.alignment = (TextAnchor)5;
				((Graphic)hudHint).raycastTarget = false;
			}
			hudHint.text = L.T("hud_points", num, Plugin.OpenKey.Value);
			((Component)hudHint).gameObject.SetActive(true);
		}

		private static bool CanOpen()
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && !Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()))
			{
				return !InventoryGui.IsVisible();
			}
			return false;
		}

		public static void Open()
		{
			if (Tree.Local != null)
			{
				if (Object.op_Implicit((Object)(object)panel) && builtLanguage != L.Language)
				{
					Object.Destroy((Object)(object)panel);
				}
				if (!Object.op_Implicit((Object)(object)panel))
				{
					Build();
				}
				panel.SetActive(true);
				GUIManager.BlockInput(true);
				if (Object.op_Implicit((Object)(object)hudHint))
				{
					((Component)hudHint).gameObject.SetActive(false);
				}
				ShowTab(tab);
			}
		}

		public static void Close()
		{
			if (Object.op_Implicit((Object)(object)panel))
			{
				panel.SetActive(false);
				((Component)tooltip).gameObject.SetActive(false);
			}
			GUIManager.BlockInput(false);
		}

		private static RectTransform Rect(Transform parent, Vector2 pos, Vector2 size)
		{
			//IL_0018: 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_0027: 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_0030: 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_0036: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0052: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject("sd", new Type[1] { typeof(RectTransform) }).transform;
			((Transform)val).SetParent(parent, false);
			Vector2 anchorMin = (val.anchorMax = Mid);
			val.anchorMin = anchorMin;
			val.anchoredPosition = pos;
			val.sizeDelta = size;
			return val;
		}

		private static Image Img(Transform parent, Vector2 pos, Vector2 size, Color color, Sprite sprite = null, bool raycast = false)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)Rect(parent, pos, size)).gameObject.AddComponent<Image>();
			obj.sprite = sprite;
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = raycast;
			return obj;
		}

		private static Text Label(Transform parent, string text, Vector2 pos, float width, float height, int size, Color color, Font font = null, TextAnchor align = (TextAnchor)4, FontStyle style = (FontStyle)0)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0027: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			Text component = GUIManager.Instance.CreateText(text, parent, Mid, Mid, pos, font ?? GUIManager.Instance.AveriaSerifBold, size, color, true, Color.black, width, height, false).GetComponent<Text>();
			component.alignment = align;
			component.fontStyle = style;
			component.verticalOverflow = (VerticalWrapMode)1;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private static Button MakeButton(Transform parent, string text, Vector2 pos, float width, float height, Action onClick)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			Button component = GUIManager.Instance.CreateButton(text, parent, Mid, Mid, pos, width, height).GetComponent<Button>();
			((UnityEvent)component.onClick).AddListener((UnityAction)delegate
			{
				onClick();
			});
			return component;
		}

		private static Image Line(Transform parent, Vector2 a, Vector2 b, float thickness, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0048: 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)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			Image obj = Img(parent, a, new Vector2(Vector2.Distance(a, b), thickness), color);
			((Graphic)obj).rectTransform.pivot = new Vector2(0f, 0.5f);
			((Transform)((Graphic)obj).rectTransform).localRotation = Quaternion.Euler(0f, 0f, Mathf.Atan2(b.y - a.y, b.x - a.x) * 57.29578f);
			return obj;
		}

		private static Image[] Pips(Transform parent, Vector2 center, int count, float size)
		{
			//IL_0021: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Image[] array = (Image[])(object)new Image[count];
			float num = size + 2f;
			Sprite sprite = Icons.Get("pip");
			for (int i = 0; i < count; i++)
			{
				array[i] = Img(parent, center + new Vector2(((float)i - (float)(count - 1) / 2f) * num, 0f), new Vector2(size, size), Bark, sprite);
			}
			return array;
		}

		private static void SetPips(Image[] pips, int rank)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to inval