Decompiled source of TalentForge v0.13.3

TalentForge.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using SkillsReworked.Api;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TalentForge
{
	public static class IconFactory
	{
		private delegate bool Shape(Vector2 p);

		private const int Size = 64;

		private static readonly Dictionary<TalentEffect, Sprite> Cache = new Dictionary<TalentEffect, Sprite>();

		public static Sprite Get(TalentEffect effect)
		{
			if (Cache.TryGetValue(effect, out var value))
			{
				return value;
			}
			value = Render(Describe(effect));
			Cache[effect] = value;
			return value;
		}

		private static Shape Seg(float x0, float y0, float x1, float y1, float width)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			Vector2 a = new Vector2(x0, y0);
			Vector2 b = new Vector2(x1, y1);
			return (Vector2 p) => DistanceToSegment(p, a, b) <= width / 2f;
		}

		private static Shape Circ(float cx, float cy, float r)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			Vector2 c = new Vector2(cx, cy);
			return delegate(Vector2 p)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				Vector2 val = p - c;
				return ((Vector2)(ref val)).sqrMagnitude <= r * r;
			};
		}

		private static Shape Ring(float cx, float cy, float r, float width)
		{
			//IL_0016: 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)
			Vector2 c = new Vector2(cx, cy);
			return delegate(Vector2 p)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				Vector2 val = p - c;
				return Mathf.Abs(((Vector2)(ref val)).magnitude - r) <= width / 2f;
			};
		}

		private static Shape Arc(float cx, float cy, float r, float width, float fromDeg, float toDeg)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			Vector2 c = new Vector2(cx, cy);
			return delegate(Vector2 p)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: 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_0036: Unknown result type (might be due to invalid IL or missing references)
				Vector2 val = p - c;
				if (Mathf.Abs(((Vector2)(ref val)).magnitude - r) > width / 2f)
				{
					return false;
				}
				float num = Mathf.Atan2(val.y, val.x) * 57.29578f;
				if (num < 0f)
				{
					num += 360f;
				}
				float num2 = ((fromDeg < 0f) ? (fromDeg + 360f) : fromDeg);
				float num3 = ((toDeg < 0f) ? (toDeg + 360f) : toDeg);
				if (!(num2 <= num3))
				{
					if (!(num >= num2))
					{
						return num <= num3;
					}
					return true;
				}
				return num >= num2 && num <= num3;
			};
		}

		private static Shape Poly(params float[] xy)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			Vector2[] pts = (Vector2[])(object)new Vector2[xy.Length / 2];
			for (int i = 0; i < pts.Length; i++)
			{
				pts[i] = new Vector2(xy[i * 2], xy[i * 2 + 1]);
			}
			return delegate(Vector2 p)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				//IL_0071: Unknown result type (might be due to invalid IL or missing references)
				bool flag = false;
				int num = 0;
				int num2 = pts.Length - 1;
				while (num < pts.Length)
				{
					if (pts[num].y > p.y != pts[num2].y > p.y && p.x < (pts[num2].x - pts[num].x) * (p.y - pts[num].y) / (pts[num2].y - pts[num].y) + pts[num].x)
					{
						flag = !flag;
					}
					num2 = num++;
				}
				return flag;
			};
		}

		private static Shape Rotate(Shape s, float degrees)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			Vector2 center = new Vector2(32f, 32f);
			float num = (0f - degrees) * ((float)Math.PI / 180f);
			float cos = Mathf.Cos(num);
			float sin = Mathf.Sin(num);
			return delegate(Vector2 p)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				//IL_004f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				Vector2 val = p - center;
				return s(new Vector2(val.x * cos - val.y * sin, val.x * sin + val.y * cos) + center);
			};
		}

		private static Shape Union(params Shape[] shapes)
		{
			return delegate(Vector2 p)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				Shape[] array = shapes;
				for (int i = 0; i < array.Length; i++)
				{
					if (array[i](p))
					{
						return true;
					}
				}
				return false;
			};
		}

		private static float DistanceToSegment(Vector2 p, Vector2 a, Vector2 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = b - a;
			float num = Mathf.Clamp01(Vector2.Dot(p - a, val) / ((Vector2)(ref val)).sqrMagnitude);
			return Vector2.Distance(p, a + val * num);
		}

		private static Shape SwordShape()
		{
			return Union(Poly(32f, 60f, 38f, 52f, 38f, 22f, 26f, 22f, 26f, 52f), Seg(17f, 22f, 47f, 22f, 5f), Seg(32f, 22f, 32f, 9f, 6f), Circ(32f, 7f, 4f));
		}

		private static Shape AxeShape()
		{
			return Union(Seg(26f, 6f, 26f, 58f, 5f), Poly(26f, 58f, 52f, 52f, 56f, 30f, 26f, 34f));
		}

		private static Shape Describe(TalentEffect effect)
		{
			return effect switch
			{
				TalentEffect.MeleeDamage => Union(Rotate(SwordShape(), 38f), Rotate(SwordShape(), -38f)), 
				TalentEffect.SwordDamage => SwordShape(), 
				TalentEffect.AxeDamage => AxeShape(), 
				TalentEffect.ClubDamage => Poly(29f, 4f, 35f, 4f, 40f, 34f, 49f, 46f, 45f, 58f, 32f, 62f, 19f, 58f, 15f, 46f, 24f, 34f), 
				TalentEffect.KnifeDamage => Union(Poly(32f, 60f, 40f, 36f, 24f, 36f), Seg(20f, 35f, 44f, 35f, 4f), Seg(32f, 35f, 32f, 14f, 7f), Circ(32f, 12f, 4f)), 
				TalentEffect.BowDamage => Union(Arc(18f, 32f, 26f, 5f, -62f, 62f), Seg(18f + 26f * Mathf.Cos(1.0821041f), 32f + 26f * Mathf.Sin(1.0821041f), 18f + 26f * Mathf.Cos(1.0821041f), 32f - 26f * Mathf.Sin(1.0821041f), 2f), Seg(14f, 32f, 52f, 32f, 3f), Poly(60f, 32f, 50f, 39f, 50f, 25f)), 
				TalentEffect.CrossbowDamage => Union(Arc(32f, 22f, 26f, 5f, 20f, 160f), Seg(32f, 8f, 32f, 50f, 6f), Seg(32f + 26f * Mathf.Cos(0.34906584f), 22f + 26f * Mathf.Sin(0.34906584f), 32f - 26f * Mathf.Cos(0.34906584f), 22f + 26f * Mathf.Sin(0.34906584f), 2f), Poly(32f, 62f, 26f, 50f, 38f, 50f)), 
				TalentEffect.RangedDamage => Union(Ring(32f, 32f, 26f, 5f), Ring(32f, 32f, 14f, 4f), Circ(32f, 32f, 4.5f), Seg(32f, 2f, 32f, 12f, 3f), Seg(32f, 52f, 32f, 62f, 3f), Seg(2f, 32f, 12f, 32f, 3f), Seg(52f, 32f, 62f, 32f, 3f)), 
				TalentEffect.MagicDamage => Poly(32f, 62f, 38f, 38f, 62f, 32f, 38f, 26f, 32f, 2f, 26f, 26f, 2f, 32f, 26f, 38f), 
				TalentEffect.ElementalDamage => Poly(32f, 62f, 41f, 48f, 47f, 36f, 45f, 22f, 37f, 6f, 32f, 15f, 27f, 3f, 19f, 18f, 17f, 32f, 23f, 46f), 
				TalentEffect.BloodDamage => Union(Circ(32f, 22f, 17f), Poly(16f, 28f, 48f, 28f, 32f, 62f)), 
				TalentEffect.AllDamage => BurstShape(12, 30f, 15f), 
				TalentEffect.MaxHealth => Union(Circ(21f, 40f, 14f), Circ(43f, 40f, 14f), Poly(8f, 36f, 56f, 36f, 32f, 4f)), 
				TalentEffect.MaxStamina => Poly(39f, 62f, 13f, 32f, 28f, 32f, 22f, 2f, 51f, 36f, 35f, 36f), 
				TalentEffect.MaxEitr => Union(Poly(32f, 62f, 52f, 36f, 32f, 2f, 12f, 36f), Rotate(Poly(32f, 62f, 52f, 36f, 32f, 2f, 12f, 36f), 0f)), 
				TalentEffect.RunSpeed => Union(Seg(10f, 52f, 28f, 32f, 7f), Seg(28f, 32f, 10f, 12f, 7f), Seg(32f, 52f, 50f, 32f, 7f), Seg(50f, 32f, 32f, 12f, 7f)), 
				TalentEffect.ArmorBonus => Poly(10f, 58f, 54f, 58f, 54f, 32f, 32f, 4f, 10f, 32f), 
				TalentEffect.Experience => BurstShape(5, 31f, 13f), 
				TalentEffect.MiningDamage => Rotate(Union(Seg(32f, 4f, 32f, 48f, 5f), Arc(32f, 24f, 26f, 8f, 15f, 165f), Poly(4f, 20f, 10f, 30f, 14f, 22f), Poly(60f, 20f, 54f, 30f, 50f, 22f)), 40f), 
				TalentEffect.ChoppingDamage => Union(Rotate(AxeShape(), -22f), Seg(6f, 6f, 58f, 6f, 7f)), 
				TalentEffect.HarvestYield => Union(Seg(32f, 4f, 32f, 46f, 3f), Circ(32f, 54f, 6f), Circ(23f, 44f, 6f), Circ(41f, 44f, 6f), Circ(24f, 31f, 5.5f), Circ(40f, 31f, 5.5f)), 
				TalentEffect.FoodDuration => Union(Circ(38f, 38f, 18f), Seg(26f, 26f, 10f, 10f, 8f), Circ(7f, 14f, 5f), Circ(14f, 7f, 5f)), 
				TalentEffect.AmmoSave => Union(Seg(10f, 10f, 48f, 48f, 5f), Poly(60f, 60f, 42f, 54f, 54f, 42f), Poly(10f, 10f, 8f, 24f, 24f, 8f), Arc(44f, 20f, 12f, 5f, 90f, 360f)), 
				TalentEffect.LifeSteal => Union(Poly(8f, 58f, 30f, 58f, 19f, 8f), Poly(34f, 58f, 56f, 58f, 45f, 8f)), 
				TalentEffect.HealthRegen => Union(Seg(32f, 8f, 32f, 56f, 13f), Seg(8f, 32f, 56f, 32f, 13f)), 
				TalentEffect.StaminaRegen => Union(Arc(32f, 32f, 21f, 7f, 40f, 320f), Poly(54f, 44f, 62f, 24f, 42f, 28f)), 
				TalentEffect.ElementalResist => Union(Seg(32f, 4f, 32f, 60f, 5f), Seg(6f, 18f, 58f, 46f, 5f), Seg(6f, 46f, 58f, 18f, 5f), Circ(32f, 32f, 7f)), 
				_ => Union(Poly(4f, 32f, 20f, 48f, 40f, 48f, 46f, 32f, 40f, 16f, 20f, 16f), Poly(44f, 32f, 60f, 50f, 60f, 14f)), 
			};
		}

		private static Shape BurstShape(int points, float outer, float inner)
		{
			float[] array = new float[points * 4];
			for (int i = 0; i < points * 2; i++)
			{
				float num = ((i % 2 == 0) ? outer : inner);
				float num2 = (float)i / ((float)points * 2f) * (float)Math.PI * 2f;
				array[i * 2] = 32f + Mathf.Cos(num2) * num;
				array[i * 2 + 1] = 32f + Mathf.Sin(num2) * num;
			}
			return Poly(array);
		}

		private static Sprite Render(Shape shape)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_00f4: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false);
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			Color[] array = (Color[])(object)new Color[4096];
			Vector2 p = default(Vector2);
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					int num = 0;
					for (int k = 0; k < 3; k++)
					{
						for (int l = 0; l < 3; l++)
						{
							((Vector2)(ref p))..ctor((float)j + ((float)l + 0.5f) / 3f, (float)i + ((float)k + 0.5f) / 3f);
							if (shape(p))
							{
								num++;
							}
						}
					}
					array[i * 64 + j] = new Color(1f, 1f, 1f, (float)num / 9f);
				}
			}
			val.SetPixels(array);
			val.Apply();
			return Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 100f);
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show")]
	public static class InventoryButtonPatch
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Toggle;
		}

		private static GameObject _button;

		private static InventoryGui _owner;

		private static bool _warned;

		private static Sprite _icon;

		[HarmonyPostfix]
		public static void Postfix(InventoryGui __instance)
		{
			try
			{
				Ensure(__instance);
			}
			catch (Exception ex)
			{
				if (!_warned)
				{
					_warned = true;
					TalentForgePlugin.Log.LogWarning((object)("Could not add the inventory button: " + ex.Message));
				}
			}
		}

		private static void Ensure(InventoryGui gui)
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: 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)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			if (!TalentForgePlugin.ShowInventoryButton.Value)
			{
				if ((Object)(object)_button != (Object)null)
				{
					_button.SetActive(false);
				}
				return;
			}
			if ((Object)(object)_button != (Object)null && (Object)(object)_owner == (Object)(object)gui)
			{
				_button.SetActive(true);
				return;
			}
			Button val = FindSkillsButton(gui);
			if ((Object)(object)val == (Object)null)
			{
				if (!_warned)
				{
					_warned = true;
					TalentForgePlugin.Log.LogWarning((object)"TalentForge: the Skills button was not found, so the Talents button was not added. Use the hotkey.");
				}
				return;
			}
			RectTransform component = ((Component)val).GetComponent<RectTransform>();
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent);
			((Object)val2).name = "TalentForgeButton";
			Component[] componentsInChildren = val2.GetComponentsInChildren<Component>(true);
			foreach (Component val3 in componentsInChildren)
			{
				if ((Object)(object)val3 != (Object)null && ((object)val3).GetType().Name == "Localize")
				{
					Object.DestroyImmediate((Object)(object)val3);
				}
			}
			Button component2 = val2.GetComponent<Button>();
			_ = ((Selectable)component2).transition;
			SpriteState spriteState = ((Selectable)component2).spriteState;
			Navigation navigation = ((Selectable)component2).navigation;
			Object.DestroyImmediate((Object)(object)component2);
			Button val4 = val2.AddComponent<Button>();
			((Selectable)val4).navigation = navigation;
			((Selectable)val4).spriteState = spriteState;
			ButtonClickedEvent onClick = val4.onClick;
			object obj = <>O.<0>__Toggle;
			if (obj == null)
			{
				UnityAction val5 = TalentWindow.Toggle;
				<>O.<0>__Toggle = val5;
				obj = (object)val5;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			StyleIcon(val2, val4);
			RectTransform component3 = val2.GetComponent<RectTransform>();
			PlaceInRow(component, component3);
			component3.anchoredPosition += new Vector2(TalentForgePlugin.InventoryButtonOffsetX.Value, TalentForgePlugin.InventoryButtonOffsetY.Value);
			_button = val2;
			_owner = gui;
			TalentForgePlugin.Log.LogInfo((object)"TalentForge: Talents button added at the end of the icon row.");
		}

		private static List<RectTransform> RowOf(RectTransform skills, RectTransform self)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			List<RectTransform> list = new List<RectTransform>();
			foreach (Transform item in ((Transform)skills).parent)
			{
				object obj = (object)item;
				RectTransform val = (RectTransform)((obj is RectTransform) ? obj : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)self) && ((Component)val).gameObject.activeSelf && ((Object)(object)((Component)val).GetComponent<Button>() != (Object)null || (Object)(object)((Component)val).GetComponent<Toggle>() != (Object)null) && Mathf.Abs(val.anchoredPosition.y - skills.anchoredPosition.y) < 30f)
				{
					list.Add(val);
				}
			}
			list.Sort((RectTransform a, RectTransform b) => a.anchoredPosition.x.CompareTo(b.anchoredPosition.x));
			return list;
		}

		private static void PlaceInRow(RectTransform skills, RectTransform self)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			List<RectTransform> list = RowOf(skills, self);
			Rect rect;
			if (list.Count < 2)
			{
				Vector2 anchoredPosition = skills.anchoredPosition;
				rect = skills.rect;
				self.anchoredPosition = anchoredPosition + new Vector2(((Rect)(ref rect)).width + 12f, 0f);
				return;
			}
			if ((list[list.Count - 1].anchoredPosition.x - list[0].anchoredPosition.x) / (float)(list.Count - 1) < 10f)
			{
				rect = skills.rect;
				_ = ((Rect)(ref rect)).width;
			}
			float x = list[0].anchoredPosition.x;
			float x2 = list[list.Count - 1].anchoredPosition.x;
			float num = (x2 - x) / (float)list.Count;
			for (int i = 0; i < list.Count; i++)
			{
				list[i].anchoredPosition = new Vector2(x + (float)i * num, list[i].anchoredPosition.y);
			}
			self.anchoredPosition = new Vector2(x2, skills.anchoredPosition.y);
		}

		private static void StyleIcon(GameObject go, Button button)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: 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_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			Color val = default(Color);
			((Color)(ref val))..ctor(0.93f, 0.62f, 0.2f, 1f);
			Image val2 = null;
			Image[] componentsInChildren = go.GetComponentsInChildren<Image>(true);
			foreach (Image val3 in componentsInChildren)
			{
				if ((Object)(object)val3.sprite != (Object)null)
				{
					val3.sprite = GetIcon();
					((Graphic)val3).color = Color.white;
					val3.preserveAspect = true;
					if ((Object)(object)val2 == (Object)null)
					{
						val2 = val3;
					}
				}
			}
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = go.GetComponentInChildren<Image>(true);
			}
			((Selectable)button).targetGraphic = (Graphic)(object)val2;
			((Selectable)button).transition = (Transition)1;
			ColorBlock colors = ((Selectable)button).colors;
			((ColorBlock)(ref colors)).normalColor = val;
			((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 0.82f, 0.45f, 1f);
			((ColorBlock)(ref colors)).selectedColor = val;
			((ColorBlock)(ref colors)).pressedColor = new Color(0.7f, 0.45f, 0.12f, 1f);
			((ColorBlock)(ref colors)).disabledColor = new Color(0.4f, 0.4f, 0.4f, 0.6f);
			((ColorBlock)(ref colors)).colorMultiplier = 1f;
			((ColorBlock)(ref colors)).fadeDuration = 0.1f;
			((Selectable)button).colors = colors;
		}

		private static Sprite GetIcon()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0043: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: 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_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_icon != (Object)null)
			{
				return _icon;
			}
			Texture2D val = new Texture2D(96, 96, (TextureFormat)4, false);
			((Texture)val).filterMode = (FilterMode)1;
			Color[] array = (Color[])(object)new Color[9216];
			Vector2[] array2 = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(48f, 74f),
				new Vector2(24f, 30f),
				new Vector2(72f, 30f),
				new Vector2(48f, 48f)
			};
			int[][] array3 = new int[4][]
			{
				new int[2] { 0, 3 },
				new int[2] { 3, 1 },
				new int[2] { 3, 2 },
				new int[2] { 1, 2 }
			};
			Vector2 val2 = default(Vector2);
			for (int i = 0; i < 96; i++)
			{
				for (int j = 0; j < 96; j++)
				{
					((Vector2)(ref val2))..ctor((float)j + 0.5f, (float)i + 0.5f);
					float num = 0f;
					for (int k = 0; k < array2.Length; k++)
					{
						float num2 = ((k == 3) ? 8f : 10f);
						num = Mathf.Max(num, Mathf.Clamp01(num2 - Vector2.Distance(val2, array2[k]) + 0.5f));
					}
					int[][] array4 = array3;
					foreach (int[] array5 in array4)
					{
						num = Mathf.Max(num, Mathf.Clamp01(3f - DistanceToSegment(val2, array2[array5[0]], array2[array5[1]]) + 0.5f));
					}
					array[i * 96 + j] = new Color(1f, 1f, 1f, num);
				}
			}
			val.SetPixels(array);
			val.Apply();
			_icon = Sprite.Create(val, new Rect(0f, 0f, 96f, 96f), new Vector2(0.5f, 0.5f), 100f);
			return _icon;
		}

		private static float DistanceToSegment(Vector2 p, Vector2 a, Vector2 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = b - a;
			float num = Mathf.Clamp01(Vector2.Dot(p - a, val) / ((Vector2)(ref val)).sqrMagnitude);
			return Vector2.Distance(p, a + val * num);
		}

		private static Button FindSkillsButton(InventoryGui gui)
		{
			Button[] componentsInChildren = ((Component)gui).GetComponentsInChildren<Button>(true);
			foreach (Button val in componentsInChildren)
			{
				if (((Object)((Component)val).gameObject).name == "TalentForgeButton")
				{
					continue;
				}
				for (int j = 0; j < ((UnityEventBase)val.onClick).GetPersistentEventCount(); j++)
				{
					if (((UnityEventBase)val.onClick).GetPersistentMethodName(j) == "OnOpenSkills")
					{
						return val;
					}
				}
			}
			return null;
		}
	}
	public static class Loc
	{
		private static readonly Dictionary<string, string> Map = new Dictionary<string, string>();

		private static bool _ready;

		private static string _mapLanguage;

		private static float _nextCheck;

		public static string Language
		{
			get
			{
				string text = ((TalentForgePlugin.Language != null) ? TalentForgePlugin.Language.Value : "auto");
				if (text == "auto")
				{
					string text2 = ((Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : null);
					return CodeOf(string.IsNullOrEmpty(text2) ? PlayerPrefs.GetString("language", "English") : text2);
				}
				return text;
			}
		}

		private static string CodeOf(string game)
		{
			if (game.StartsWith("Portuguese"))
			{
				return "pt";
			}
			if (game.StartsWith("Spanish"))
			{
				return "es";
			}
			if (game.StartsWith("French"))
			{
				return "fr";
			}
			if (game.StartsWith("German"))
			{
				return "de";
			}
			if (game.StartsWith("Italian"))
			{
				return "it";
			}
			if (game.StartsWith("Russian"))
			{
				return "ru";
			}
			if (game.StartsWith("Polish"))
			{
				return "pl";
			}
			if (game.StartsWith("Turkish"))
			{
				return "tr";
			}
			if (game.StartsWith("Chinese"))
			{
				return "zh";
			}
			if (game.StartsWith("Japanese"))
			{
				return "ja";
			}
			if (game.StartsWith("Korean"))
			{
				return "ko";
			}
			return "en";
		}

		public static void Reload()
		{
			Map.Clear();
			string text = (_mapLanguage = Language);
			if (text == "pt")
			{
				Parse("\n# window\nTalent Tree=Árvore de Talentos\nPoints: {0} available  /  {1} total  ({2} spent)=Pontos: {0} disponíveis  /  {1} no total  ({2} gastos)\nRespec=Redefinir\nCenter view=Centralizar\nClose=Fechar\nBuilds=Builds\nNext=Próximo\nTotals=Totais\nBranch: {0}=Ramo: {0}\nAll=Todos\nSearch...=Buscar...\nStart=Início\nAll nodes were refunded.=Todos os nós foram devolvidos.\nStart of the tree=Início da árvore\n(start of the tree)=(início da árvore)\nRank {0}/{1} - click to add a rank, right click to remove one=Rank {0}/{1} - clique para somar um rank, botão direito para remover um\nMax rank {0}/{1}=Rank máximo {0}/{1}\nTaken=Tomado\n (right click to refund)= (botão direito para devolver)\nAvailable - click to take=Disponível - clique para tomar\nLocked - take a connected node first=Bloqueado - tome antes um nó conectado\nMinor=Comum\nNotable=Raro\nKeystone=Chave\npoint=ponto\npoints=pontos\nper rank, up to rank {0}=por rank, até o rank {0}\nNothing to take right now.=Nada para tomar agora.\nTotals (hover a node to preview)=Totais (passe o mouse num nó para prévia)\n(nothing taken yet)=(nada tomado ainda)\n# messages\nThe origin is always yours.=A origem é sempre sua.\n{0} is already taken.={0} já foi tomado.\n{0} is already at its maximum rank ({1}).={0} já está no rank máximo ({1}).\n{0} is not connected to your tree yet.={0} ainda não está conectado à sua árvore.\nNot enough points for {0} (needs {1}, you have {2}).=Pontos insuficientes para {0} (precisa de {1}, você tem {2}).\n{0} taken: {1}.={0} tomado: {1}.\n{0} rank {1}/{2}: {3} per rank.={0} rank {1}/{2}: {3} por rank.\n{0} is not taken.={0} não foi tomado.\n{0} holds up other nodes. Refund those first, or use Respec.={0} sustenta outros nós. Devolva-os antes, ou use Redefinir.\n{0} refunded ({1} point(s) back).={0} devolvido ({1} ponto(s) de volta).\nThe server restored your talents.=O servidor restaurou seus talentos.\nTalentForge: talents were reset (points dropped below what was spent)=TalentForge: os talentos foram redefinidos (os pontos ficaram abaixo do que foi gasto)\n# builds\nBuild name=Nome da build\nSave=Salvar\nLoad=Carregar\nSlot {0}: {1}=Espaço {0}: {1}\n(empty)=(vazio)\n{0} ({1} nodes, {2} points)={0} ({1} nós, {2} pontos)\nExport=Exportar\nImport=Importar\nPaste a code here=Cole um código aqui\nSaved the build in slot {0}.=Build salva no espaço {0}.\nBuild loaded.=Build carregada.\nNot enough points for that build.=Pontos insuficientes para essa build.\nInvalid code.=Código inválido.\nCode ready: copy it from the box.=Código pronto: copie-o da caixa.\nThat slot is empty.=Esse espaço está vazio.\nBuild {0}=Build {0}\n# effects\nmelee damage=dano corpo a corpo\nbow/crossbow damage=dano de arco/besta\nspell damage=dano de magia\nall damage=todo o dano\nmax health=vida máxima\nmax stamina=estamina máxima\nmax eitr=eitr máximo\nrun speed=velocidade de corrida\nsword damage=dano de espada\naxe damage=dano de machado\nmace damage=dano de maça\nknife damage=dano de faca\nbow damage=dano de arco\ncrossbow damage=dano de besta\nelemental magic damage=dano de magia elemental\nblood magic damage=dano de magia de sangue\nexperience gained=experiência ganha\narmor from equipped items=armadura dos itens equipados\nmining damage=dano de mineração\nwoodcutting damage=dano de corte de madeira\nharvest yield=rendimento da colheita\nfood duration=duração da comida\nfishing skill levels=níveis de pesca\nlife steal=roubo de vida\nhealth per second=vida por segundo\nstamina regeneration=regeneração de estamina\nelemental resistance=resistência elemental\nchance to not use ammo=chance de não gastar munição\n# branches\nSwords=Espadas\nKnives=Facas\nAxes=Machados\nMaces=Maças\nBows=Arcos\nCrossbows=Bestas\nElemental Magic=Magia Elemental\nBlood Magic=Magia de Sangue\nMining=Mineração\nWoodcutting=Corte de Madeira\nFarming=Agricultura\nCooking=Culinária\nFishing=Pesca\nCrossroads=Encruzilhadas\n# perks\nEvery 5th sword hit deals +75% damage=Todo 5º golpe de espada causa +75% de dano\nMelee hits while crouching deal +40% damage=Golpes corpo a corpo agachado causam +40% de dano\nAxe hits deal +40% damage to enemies below 25% health=Golpes de machado causam +40% de dano a inimigos com menos de 25% de vida\nMace hits have +50% stagger power=Golpes de maça têm +50% de poder de atordoamento\nBow hits deal +30% damage to targets farther than 20 m=Tiros de arco causam +30% de dano a alvos além de 20 m\nCrossbow hits deal +25% damage to enemies above 90% health=Tiros de besta causam +25% de dano a inimigos com mais de 90% de vida\n15% chance for elemental spells to deal double damage=15% de chance de magias elementais causarem dano dobrado\nEach blood magic hit restores 2 eitr=Cada golpe de magia de sangue restaura 2 de eitr\nEach pickaxe hit restores 2 stamina=Cada golpe de picareta restaura 2 de estamina\nEach axe hit on a tree restores 2 health=Cada golpe de machado numa árvore restaura 2 de vida\n20% chance to double what you harvest=20% de chance de dobrar o que você colhe\nEating restores 10% of your max health at once=Comer restaura 10% da sua vida máxima de uma vez\n35% less stamina used while swimming=35% menos estamina gasta ao nadar\n# node names: swords\nFine Edge=Fio Fino\nSteel Nerve=Nervos de Aço\nDuelist=Duelista\nKeen Blade=Lâmina Afiada\nFootwork=Jogo de Pernas\nBlademaster=Mestre das Lâminas\nHoned Steel=Aço Polido\nSecond Wind=Segundo Fôlego\nSword Saint=Santo da Espada\nEdge Eternal=Gume Eterno\nParry=Aparar\nRiposte=Contra-ataque\nSwordmaster=Mestre Espadachim\nCleave=Talhar\nBlade Dancer=Dançarino de Lâminas\nGambeson=Gibão\nBreath Control=Controle da Respiração\nFencer Poise=Postura de Esgrimista\nChainmail=Cota de Malha\nVeteran Duelist=Duelista Veterano\n# knives\nQuick Stab=Estocada Rápida\nLight Foot=Pé Leve\nAssassin=Assassino\nBackstab=Facada nas Costas\nSilent Breath=Respiração Silenciosa\nShadow Blade=Lâmina da Sombra\nNick=Corte Leve\nPatience=Paciência\nCutthroat=Degolador\nPhantom=Fantasma\nSlice=Fatiar\nPrecision=Precisão\nDagger Dancer=Dançarino da Adaga\nDeep Cut=Corte Profundo\nThroat Slitter=Corta-Gargantas\nNimble=Ágil\nLungs=Pulmões\nEvasion=Evasão\nLeathers=Couros\nGhost Walk=Passo Fantasma\n# axes\nHatchet=Machadinha\nThick Neck=Pescoço Grosso\nAxeman=Machadeiro\nHeavy Swing=Golpe Pesado\nSteel Hide=Pele de Aço\nBerserker=Berserker\nReaver=Saqueador\nPlate Wall=Muralha de Placas\nSkullsplitter=Racha-Crânios\nAxe Lord=Senhor do Machado\nHew=Lascar\nSplitter=Rachador\nExecutioner=Carrasco\nBrutal Chop=Golpe Brutal\nBearded Axe=Machado Barbado\nHardy=Robusto\nArmored Hide=Pele Blindada\nBulwark=Baluarte\nIron Skin=Pele de Ferro\nWarlord Mail=Malha do Senhor da Guerra\n# maces\nMace Training=Treino de Maça\nSturdy=Resistente\nCrusher=Esmagador\nBlunt Force=Força Bruta\nGuard=Guarda\nPounder=Martelador\nSkull Cracker=Quebra-Crânios\nShrug Off=Ignorar\nHammer Fall=Queda do Martelo\nEarthshaker=Abalador\nBash=Pancada\nCrush=Esmagar\nPulveriser=Pulverizador\nStagger=Atordoar\nShatter=Estilhaçar\nVital=Vital\nIron Plates=Placas de Ferro\nFortitude=Fortitude\nPadded Armor=Armadura Acolchoada\nImmovable=Inabalável\n# bows\nSteady Aim=Mira Firme\nQuick Feet=Pés Ligeiros\nMarksman=Atirador\nDraw Strength=Força do Saque\nDeep Breath=Fôlego Profundo\nHawkeye=Olho de Falcão\nLongshot=Tiro Longo\nFleet=Veloz\nDeadeye=Olho Mortal\nApex Hunter=Caçador Supremo\nTaut String=Corda Tensa\nTracker=Rastreador\nBowmaster=Mestre do Arco\nBarbed Shot=Flecha Farpada\nSniper=Franco-Atirador\nLight Step=Passo Leve\nStamina Draw=Saque Resistente\nSkirmisher=Escaramuçador\nSprinter=Velocista\nWind Runner=Corredor do Vento\n# crossbows\nLoaded=Carregada\nPadded Vest=Colete Acolchoado\nBolt Thrower=Lança-Virotes\nHeavy Bolt=Virote Pesado\nStrong Back=Costas Fortes\nSiege Engineer=Engenheiro de Cerco\nRapid Reload=Recarga Rápida\nStand Firm=Firmeza\nBallista=Balista\nArbalist Supreme=Besteiro Supremo\nWindlass=Molinete\nPiercing Bolt=Virote Perfurante\nSapper=Sapador\nCrank=Manivela\nDoom Bolt=Virote da Perdição\nBrigandine=Brigantina\nStrong Arms=Braços Fortes\nSentry=Sentinela\nPlate Vest=Colete de Placas\nFortress=Fortaleza\n# elemental\nSpark=Faísca\nClear Mind=Mente Clara\nPyromancer=Piromante\nFrost Touch=Toque Gélido\nDeep Well=Poço Profundo\nStormcaller=Evocador de Tempestades\nEmbers=Brasas\nInner Calm=Calma Interior\nTempest=Tempestade\nArchmage=Arquimago\nEmber=Brasa\nChill=Calafrio\nElementalist=Elementalista\nLightning=Relâmpago\nInferno=Inferno\nEitr Pool=Reserva de Eitr\nVital Ward=Proteção Vital\nEitr Flow=Fluxo de Eitr\nHearty=Rijo\nEitr Font=Fonte de Eitr\n# blood\nBlood Rite=Rito de Sangue\nCrimson Mind=Mente Carmesim\nHemomancer=Hemomante\nSiphon=Sifão\nDark Well=Poço Sombrio\nBloodcaller=Invocador de Sangue\nSanguine=Sanguíneo\nCrimson Shell=Casca Carmesim\nExsanguinate=Exsanguinar\nSanguine Lord=Senhor Sanguíneo\nBleed=Sangrar\nLeech=Sanguessuga\nBloodcaster=Lançador de Sangue\nHex=Maldição\nCrimson Pact=Pacto Carmesim\nDark Eitr=Eitr Sombrio\nScab Armor=Armadura de Casca\nRed Font=Fonte Rubra\nBone Plating=Placas de Osso\nEternal Thirst=Sede Eterna\n# mining\nPick Swing=Golpe de Picareta\nStrong Lungs=Pulmões Fortes\nProspector=Prospector\nRock Breaker=Quebra-Rochas\nHardy Miner=Minerador Robusto\nDeep Delver=Explorador Profundo\nOre Sense=Faro de Minério\nCave Hardened=Curtido nas Cavernas\nMountain Heart=Coração da Montanha\nMine Lord=Senhor da Mina\nPickaxe Grip=Empunhadura da Picareta\nGeology=Geologia\nMaster Miner=Mestre Minerador\nVein Finder=Caçador de Veios\nStone Breaker=Quebra-Pedras\nEndurance=Resistência\nTunnel Rat=Rato de Túnel\nTough Hide=Pele Dura\nDwarf Blood=Sangue Anão\n# woodcutting\nChop=Cortar\nLumber Lungs=Pulmões de Lenhador\nWoodsman=Lenhador\nClean Cut=Corte Limpo\nBark Skin=Pele de Casca\nForester=Guarda Florestal\nTimber=Madeira\nThick Bark=Casca Grossa\nFell Giant=Derrubar Gigantes\nLumber Lord=Senhor da Lenha\nSharp Hatchet=Machadinha Afiada\nGrain Reader=Leitor de Veios\nMaster Logger=Mestre Lenhador\nAxe Rhythm=Ritmo do Machado\nGiant Slayer=Matador de Gigantes\nSteady Stride=Passada Firme\nBush Whacker=Abre-Trilhas\nWood Armor=Armadura de Madeira\nHardy Lumberjack=Lenhador Robusto\n# farming\nGreen Thumb=Mão Verde\nHardy Farmer=Fazendeiro Robusto\nGardener=Jardineiro\nRich Soil=Solo Rico\nBrisk Walk=Caminhada Ágil\nHarvester=Colhedor\nBountiful=Farto\nField Stride=Passo no Campo\nAbundance=Abundância\nHarvest Lord=Senhor da Colheita\nSeed Sower=Semeador\nFertilizer=Fertilizante\nMaster Farmer=Mestre Fazendeiro\nForager=Coletor\nCornucopia=Cornucópia\nFarm Health=Saúde do Campo\nQuick Steps=Passos Rápidos\nSturdy Boots=Botas Firmes\nHealthy Diet=Dieta Saudável\nCountry Legs=Pernas do Campo\n# cooking\nSimmer=Ferver Lento\nHearty Meals=Refeições Fartas\nCook=Cozinheiro\nSlow Roast=Assado Lento\nFull Belly=Barriga Cheia\nChef=Chef\nSeasoning=Tempero\nSecond Helping=Repetição\nFeast=Banquete\nHead Chef=Chef Executivo\nStew Pot=Caldeirão\nRecipe=Receita\nMaster Cook=Mestre Cozinheiro\nSpice Rack=Prateleira de Temperos\nBanquet=Festim\nAppetite=Apetite\nEnergy Bites=Petiscos de Energia\nSturdy Stomach=Estômago Firme\nFilling Meal=Refeição Substancial\nIron Stomach=Estômago de Ferro\n# fishing\nCast=Lançar\nLight Feet=Pés Leves\nAngler=Pescador\nSteady Hands=Mãos Firmes\nFisherman=Pescador Experiente\nLucky Bait=Isca da Sorte\nCalm Waters=Águas Calmas\nDeep Sea=Mar Profundo\nFish Whisperer=Sussurrador de Peixes\nBait Craft=Arte da Isca\nReel In=Recolher\nMaster Angler=Mestre Pescador\nHooked=Fisgado\nLegendary Catch=Pesca Lendária\nShore Walker=Andarilho da Costa\nLong Cast=Lançamento Longo\nDock Runner=Corredor do Cais\nTireless Angler=Pescador Incansável\nSea Legs=Pernas de Marinheiro\n# crossroads\nVeteran=Veterano\nVigor=Vigor\nPlating=Blindagem\nPathfinder=Desbravador\nResolve=Determinação\nInsight=Percepção\nScholar=Estudioso\nToughness=Dureza\nHardened=Calejado\nWanderer=Andarilho\nLore=Saber\nAdventurer=Aventureiro\nSurvivor=Sobrevivente\nTireless=Incansável\nIronclad=Encouraçado\nTrailblazer=Pioneiro\nWarbringer=Belicista\nArcane Scholar=Estudioso Arcano\nVampiric=Vampírico\nStalwart=Leal\nDelver=Explorador\nHomesteader=Colono\nGourmand=Gourmet\nOld Hand=Mão Experiente\nHale=Saudável\nVigorous=Vigoroso\nMindful=Atento\nLithe=Esguio\nFocused=Focado\nRobust=Robusto\nSpry=Lépido\nAttuned=Sintonizado\nEnergetic=Energético\nClear Head=Cabeça Limpa\nStout=Parrudo\nMending=Cura\nSecond Breath=Novo Fôlego\nSwift=Rápido\nWarded=Protegido\nPadded=Acolchoado\nRecovery=Recuperação\nCatch Breath=Recobrar Fôlego\nFleet Foot=Pé Veloz\nResilient=Resiliente\nSteadfast=Firme\nRejuvenation=Rejuvenescimento\nStamina Flow=Fluxo de Estamina\nQuick Step=Passo Rápido\n");
			}
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "TalentForge." + text + ".txt");
				if (File.Exists(path))
				{
					Parse(File.ReadAllText(path));
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("TalentForge: could not read the translation file: " + ex.Message));
			}
			_ready = true;
		}

		private static void Parse(string text)
		{
			string[] array = text.Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim(new char[1] { '\r' });
				if (text2.Length != 0 && text2[0] != '#')
				{
					int num = text2.IndexOf('=');
					if (num > 0)
					{
						Map[text2.Substring(0, num)] = text2.Substring(num + 1).Replace("\\n", "\n");
					}
				}
			}
		}

		public static string T(string english)
		{
			if (!_ready || Time.unscaledTime > _nextCheck)
			{
				_nextCheck = Time.unscaledTime + 1f;
				if (!_ready || Language != _mapLanguage)
				{
					Reload();
				}
			}
			if (english == null || !Map.TryGetValue(english, out var value))
			{
				return english;
			}
			return value;
		}

		public static string F(string english, params object[] args)
		{
			return string.Format(T(english), args);
		}
	}
	public static class LocPt
	{
		public const string Data = "\n# window\nTalent Tree=Árvore de Talentos\nPoints: {0} available  /  {1} total  ({2} spent)=Pontos: {0} disponíveis  /  {1} no total  ({2} gastos)\nRespec=Redefinir\nCenter view=Centralizar\nClose=Fechar\nBuilds=Builds\nNext=Próximo\nTotals=Totais\nBranch: {0}=Ramo: {0}\nAll=Todos\nSearch...=Buscar...\nStart=Início\nAll nodes were refunded.=Todos os nós foram devolvidos.\nStart of the tree=Início da árvore\n(start of the tree)=(início da árvore)\nRank {0}/{1} - click to add a rank, right click to remove one=Rank {0}/{1} - clique para somar um rank, botão direito para remover um\nMax rank {0}/{1}=Rank máximo {0}/{1}\nTaken=Tomado\n (right click to refund)= (botão direito para devolver)\nAvailable - click to take=Disponível - clique para tomar\nLocked - take a connected node first=Bloqueado - tome antes um nó conectado\nMinor=Comum\nNotable=Raro\nKeystone=Chave\npoint=ponto\npoints=pontos\nper rank, up to rank {0}=por rank, até o rank {0}\nNothing to take right now.=Nada para tomar agora.\nTotals (hover a node to preview)=Totais (passe o mouse num nó para prévia)\n(nothing taken yet)=(nada tomado ainda)\n# messages\nThe origin is always yours.=A origem é sempre sua.\n{0} is already taken.={0} já foi tomado.\n{0} is already at its maximum rank ({1}).={0} já está no rank máximo ({1}).\n{0} is not connected to your tree yet.={0} ainda não está conectado à sua árvore.\nNot enough points for {0} (needs {1}, you have {2}).=Pontos insuficientes para {0} (precisa de {1}, você tem {2}).\n{0} taken: {1}.={0} tomado: {1}.\n{0} rank {1}/{2}: {3} per rank.={0} rank {1}/{2}: {3} por rank.\n{0} is not taken.={0} não foi tomado.\n{0} holds up other nodes. Refund those first, or use Respec.={0} sustenta outros nós. Devolva-os antes, ou use Redefinir.\n{0} refunded ({1} point(s) back).={0} devolvido ({1} ponto(s) de volta).\nThe server restored your talents.=O servidor restaurou seus talentos.\nTalentForge: talents were reset (points dropped below what was spent)=TalentForge: os talentos foram redefinidos (os pontos ficaram abaixo do que foi gasto)\n# builds\nBuild name=Nome da build\nSave=Salvar\nLoad=Carregar\nSlot {0}: {1}=Espaço {0}: {1}\n(empty)=(vazio)\n{0} ({1} nodes, {2} points)={0} ({1} nós, {2} pontos)\nExport=Exportar\nImport=Importar\nPaste a code here=Cole um código aqui\nSaved the build in slot {0}.=Build salva no espaço {0}.\nBuild loaded.=Build carregada.\nNot enough points for that build.=Pontos insuficientes para essa build.\nInvalid code.=Código inválido.\nCode ready: copy it from the box.=Código pronto: copie-o da caixa.\nThat slot is empty.=Esse espaço está vazio.\nBuild {0}=Build {0}\n# effects\nmelee damage=dano corpo a corpo\nbow/crossbow damage=dano de arco/besta\nspell damage=dano de magia\nall damage=todo o dano\nmax health=vida máxima\nmax stamina=estamina máxima\nmax eitr=eitr máximo\nrun speed=velocidade de corrida\nsword damage=dano de espada\naxe damage=dano de machado\nmace damage=dano de maça\nknife damage=dano de faca\nbow damage=dano de arco\ncrossbow damage=dano de besta\nelemental magic damage=dano de magia elemental\nblood magic damage=dano de magia de sangue\nexperience gained=experiência ganha\narmor from equipped items=armadura dos itens equipados\nmining damage=dano de mineração\nwoodcutting damage=dano de corte de madeira\nharvest yield=rendimento da colheita\nfood duration=duração da comida\nfishing skill levels=níveis de pesca\nlife steal=roubo de vida\nhealth per second=vida por segundo\nstamina regeneration=regeneração de estamina\nelemental resistance=resistência elemental\nchance to not use ammo=chance de não gastar munição\n# branches\nSwords=Espadas\nKnives=Facas\nAxes=Machados\nMaces=Maças\nBows=Arcos\nCrossbows=Bestas\nElemental Magic=Magia Elemental\nBlood Magic=Magia de Sangue\nMining=Mineração\nWoodcutting=Corte de Madeira\nFarming=Agricultura\nCooking=Culinária\nFishing=Pesca\nCrossroads=Encruzilhadas\n# perks\nEvery 5th sword hit deals +75% damage=Todo 5º golpe de espada causa +75% de dano\nMelee hits while crouching deal +40% damage=Golpes corpo a corpo agachado causam +40% de dano\nAxe hits deal +40% damage to enemies below 25% health=Golpes de machado causam +40% de dano a inimigos com menos de 25% de vida\nMace hits have +50% stagger power=Golpes de maça têm +50% de poder de atordoamento\nBow hits deal +30% damage to targets farther than 20 m=Tiros de arco causam +30% de dano a alvos além de 20 m\nCrossbow hits deal +25% damage to enemies above 90% health=Tiros de besta causam +25% de dano a inimigos com mais de 90% de vida\n15% chance for elemental spells to deal double damage=15% de chance de magias elementais causarem dano dobrado\nEach blood magic hit restores 2 eitr=Cada golpe de magia de sangue restaura 2 de eitr\nEach pickaxe hit restores 2 stamina=Cada golpe de picareta restaura 2 de estamina\nEach axe hit on a tree restores 2 health=Cada golpe de machado numa árvore restaura 2 de vida\n20% chance to double what you harvest=20% de chance de dobrar o que você colhe\nEating restores 10% of your max health at once=Comer restaura 10% da sua vida máxima de uma vez\n35% less stamina used while swimming=35% menos estamina gasta ao nadar\n# node names: swords\nFine Edge=Fio Fino\nSteel Nerve=Nervos de Aço\nDuelist=Duelista\nKeen Blade=Lâmina Afiada\nFootwork=Jogo de Pernas\nBlademaster=Mestre das Lâminas\nHoned Steel=Aço Polido\nSecond Wind=Segundo Fôlego\nSword Saint=Santo da Espada\nEdge Eternal=Gume Eterno\nParry=Aparar\nRiposte=Contra-ataque\nSwordmaster=Mestre Espadachim\nCleave=Talhar\nBlade Dancer=Dançarino de Lâminas\nGambeson=Gibão\nBreath Control=Controle da Respiração\nFencer Poise=Postura de Esgrimista\nChainmail=Cota de Malha\nVeteran Duelist=Duelista Veterano\n# knives\nQuick Stab=Estocada Rápida\nLight Foot=Pé Leve\nAssassin=Assassino\nBackstab=Facada nas Costas\nSilent Breath=Respiração Silenciosa\nShadow Blade=Lâmina da Sombra\nNick=Corte Leve\nPatience=Paciência\nCutthroat=Degolador\nPhantom=Fantasma\nSlice=Fatiar\nPrecision=Precisão\nDagger Dancer=Dançarino da Adaga\nDeep Cut=Corte Profundo\nThroat Slitter=Corta-Gargantas\nNimble=Ágil\nLungs=Pulmões\nEvasion=Evasão\nLeathers=Couros\nGhost Walk=Passo Fantasma\n# axes\nHatchet=Machadinha\nThick Neck=Pescoço Grosso\nAxeman=Machadeiro\nHeavy Swing=Golpe Pesado\nSteel Hide=Pele de Aço\nBerserker=Berserker\nReaver=Saqueador\nPlate Wall=Muralha de Placas\nSkullsplitter=Racha-Crânios\nAxe Lord=Senhor do Machado\nHew=Lascar\nSplitter=Rachador\nExecutioner=Carrasco\nBrutal Chop=Golpe Brutal\nBearded Axe=Machado Barbado\nHardy=Robusto\nArmored Hide=Pele Blindada\nBulwark=Baluarte\nIron Skin=Pele de Ferro\nWarlord Mail=Malha do Senhor da Guerra\n# maces\nMace Training=Treino de Maça\nSturdy=Resistente\nCrusher=Esmagador\nBlunt Force=Força Bruta\nGuard=Guarda\nPounder=Martelador\nSkull Cracker=Quebra-Crânios\nShrug Off=Ignorar\nHammer Fall=Queda do Martelo\nEarthshaker=Abalador\nBash=Pancada\nCrush=Esmagar\nPulveriser=Pulverizador\nStagger=Atordoar\nShatter=Estilhaçar\nVital=Vital\nIron Plates=Placas de Ferro\nFortitude=Fortitude\nPadded Armor=Armadura Acolchoada\nImmovable=Inabalável\n# bows\nSteady Aim=Mira Firme\nQuick Feet=Pés Ligeiros\nMarksman=Atirador\nDraw Strength=Força do Saque\nDeep Breath=Fôlego Profundo\nHawkeye=Olho de Falcão\nLongshot=Tiro Longo\nFleet=Veloz\nDeadeye=Olho Mortal\nApex Hunter=Caçador Supremo\nTaut String=Corda Tensa\nTracker=Rastreador\nBowmaster=Mestre do Arco\nBarbed Shot=Flecha Farpada\nSniper=Franco-Atirador\nLight Step=Passo Leve\nStamina Draw=Saque Resistente\nSkirmisher=Escaramuçador\nSprinter=Velocista\nWind Runner=Corredor do Vento\n# crossbows\nLoaded=Carregada\nPadded Vest=Colete Acolchoado\nBolt Thrower=Lança-Virotes\nHeavy Bolt=Virote Pesado\nStrong Back=Costas Fortes\nSiege Engineer=Engenheiro de Cerco\nRapid Reload=Recarga Rápida\nStand Firm=Firmeza\nBallista=Balista\nArbalist Supreme=Besteiro Supremo\nWindlass=Molinete\nPiercing Bolt=Virote Perfurante\nSapper=Sapador\nCrank=Manivela\nDoom Bolt=Virote da Perdição\nBrigandine=Brigantina\nStrong Arms=Braços Fortes\nSentry=Sentinela\nPlate Vest=Colete de Placas\nFortress=Fortaleza\n# elemental\nSpark=Faísca\nClear Mind=Mente Clara\nPyromancer=Piromante\nFrost Touch=Toque Gélido\nDeep Well=Poço Profundo\nStormcaller=Evocador de Tempestades\nEmbers=Brasas\nInner Calm=Calma Interior\nTempest=Tempestade\nArchmage=Arquimago\nEmber=Brasa\nChill=Calafrio\nElementalist=Elementalista\nLightning=Relâmpago\nInferno=Inferno\nEitr Pool=Reserva de Eitr\nVital Ward=Proteção Vital\nEitr Flow=Fluxo de Eitr\nHearty=Rijo\nEitr Font=Fonte de Eitr\n# blood\nBlood Rite=Rito de Sangue\nCrimson Mind=Mente Carmesim\nHemomancer=Hemomante\nSiphon=Sifão\nDark Well=Poço Sombrio\nBloodcaller=Invocador de Sangue\nSanguine=Sanguíneo\nCrimson Shell=Casca Carmesim\nExsanguinate=Exsanguinar\nSanguine Lord=Senhor Sanguíneo\nBleed=Sangrar\nLeech=Sanguessuga\nBloodcaster=Lançador de Sangue\nHex=Maldição\nCrimson Pact=Pacto Carmesim\nDark Eitr=Eitr Sombrio\nScab Armor=Armadura de Casca\nRed Font=Fonte Rubra\nBone Plating=Placas de Osso\nEternal Thirst=Sede Eterna\n# mining\nPick Swing=Golpe de Picareta\nStrong Lungs=Pulmões Fortes\nProspector=Prospector\nRock Breaker=Quebra-Rochas\nHardy Miner=Minerador Robusto\nDeep Delver=Explorador Profundo\nOre Sense=Faro de Minério\nCave Hardened=Curtido nas Cavernas\nMountain Heart=Coração da Montanha\nMine Lord=Senhor da Mina\nPickaxe Grip=Empunhadura da Picareta\nGeology=Geologia\nMaster Miner=Mestre Minerador\nVein Finder=Caçador de Veios\nStone Breaker=Quebra-Pedras\nEndurance=Resistência\nTunnel Rat=Rato de Túnel\nTough Hide=Pele Dura\nDwarf Blood=Sangue Anão\n# woodcutting\nChop=Cortar\nLumber Lungs=Pulmões de Lenhador\nWoodsman=Lenhador\nClean Cut=Corte Limpo\nBark Skin=Pele de Casca\nForester=Guarda Florestal\nTimber=Madeira\nThick Bark=Casca Grossa\nFell Giant=Derrubar Gigantes\nLumber Lord=Senhor da Lenha\nSharp Hatchet=Machadinha Afiada\nGrain Reader=Leitor de Veios\nMaster Logger=Mestre Lenhador\nAxe Rhythm=Ritmo do Machado\nGiant Slayer=Matador de Gigantes\nSteady Stride=Passada Firme\nBush Whacker=Abre-Trilhas\nWood Armor=Armadura de Madeira\nHardy Lumberjack=Lenhador Robusto\n# farming\nGreen Thumb=Mão Verde\nHardy Farmer=Fazendeiro Robusto\nGardener=Jardineiro\nRich Soil=Solo Rico\nBrisk Walk=Caminhada Ágil\nHarvester=Colhedor\nBountiful=Farto\nField Stride=Passo no Campo\nAbundance=Abundância\nHarvest Lord=Senhor da Colheita\nSeed Sower=Semeador\nFertilizer=Fertilizante\nMaster Farmer=Mestre Fazendeiro\nForager=Coletor\nCornucopia=Cornucópia\nFarm Health=Saúde do Campo\nQuick Steps=Passos Rápidos\nSturdy Boots=Botas Firmes\nHealthy Diet=Dieta Saudável\nCountry Legs=Pernas do Campo\n# cooking\nSimmer=Ferver Lento\nHearty Meals=Refeições Fartas\nCook=Cozinheiro\nSlow Roast=Assado Lento\nFull Belly=Barriga Cheia\nChef=Chef\nSeasoning=Tempero\nSecond Helping=Repetição\nFeast=Banquete\nHead Chef=Chef Executivo\nStew Pot=Caldeirão\nRecipe=Receita\nMaster Cook=Mestre Cozinheiro\nSpice Rack=Prateleira de Temperos\nBanquet=Festim\nAppetite=Apetite\nEnergy Bites=Petiscos de Energia\nSturdy Stomach=Estômago Firme\nFilling Meal=Refeição Substancial\nIron Stomach=Estômago de Ferro\n# fishing\nCast=Lançar\nLight Feet=Pés Leves\nAngler=Pescador\nSteady Hands=Mãos Firmes\nFisherman=Pescador Experiente\nLucky Bait=Isca da Sorte\nCalm Waters=Águas Calmas\nDeep Sea=Mar Profundo\nFish Whisperer=Sussurrador de Peixes\nBait Craft=Arte da Isca\nReel In=Recolher\nMaster Angler=Mestre Pescador\nHooked=Fisgado\nLegendary Catch=Pesca Lendária\nShore Walker=Andarilho da Costa\nLong Cast=Lançamento Longo\nDock Runner=Corredor do Cais\nTireless Angler=Pescador Incansável\nSea Legs=Pernas de Marinheiro\n# crossroads\nVeteran=Veterano\nVigor=Vigor\nPlating=Blindagem\nPathfinder=Desbravador\nResolve=Determinação\nInsight=Percepção\nScholar=Estudioso\nToughness=Dureza\nHardened=Calejado\nWanderer=Andarilho\nLore=Saber\nAdventurer=Aventureiro\nSurvivor=Sobrevivente\nTireless=Incansável\nIronclad=Encouraçado\nTrailblazer=Pioneiro\nWarbringer=Belicista\nArcane Scholar=Estudioso Arcano\nVampiric=Vampírico\nStalwart=Leal\nDelver=Explorador\nHomesteader=Colono\nGourmand=Gourmet\nOld Hand=Mão Experiente\nHale=Saudável\nVigorous=Vigoroso\nMindful=Atento\nLithe=Esguio\nFocused=Focado\nRobust=Robusto\nSpry=Lépido\nAttuned=Sintonizado\nEnergetic=Energético\nClear Head=Cabeça Limpa\nStout=Parrudo\nMending=Cura\nSecond Breath=Novo Fôlego\nSwift=Rápido\nWarded=Protegido\nPadded=Acolchoado\nRecovery=Recuperação\nCatch Breath=Recobrar Fôlego\nFleet Foot=Pé Veloz\nResilient=Resiliente\nSteadfast=Firme\nRejuvenation=Rejuvenescimento\nStamina Flow=Fluxo de Estamina\nQuick Step=Passo Rápido\n";
	}
	[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
	public static class FoodTotalsPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr)
		{
			try
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					TalentState.ValidateBudget(__instance);
					hp += TalentState.GetBonus((Character)(object)__instance, TalentEffect.MaxHealth);
					stamina += TalentState.GetBonus((Character)(object)__instance, TalentEffect.MaxStamina);
					eitr += TalentState.GetBonus((Character)(object)__instance, TalentEffect.MaxEitr);
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("FoodTotalsPatch: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetRunSpeedFactor")]
	public static class RunSpeedPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player __instance, ref float __result)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				__result *= 1f + TalentState.GetBonus((Character)(object)__instance, TalentEffect.RunSpeed) / 100f;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetJogSpeedFactor")]
	public static class JogSpeedPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player __instance, ref float __result)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				__result *= 1f + TalentState.GetBonus((Character)(object)__instance, TalentEffect.RunSpeed) / 100f;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetBodyArmor")]
	public static class ArmorPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player __instance, ref float __result)
		{
			float bonus = TalentState.GetBonus((Character)(object)__instance, TalentEffect.ArmorBonus);
			if (bonus > 0f)
			{
				__result *= 1f + bonus / 100f;
			}
		}
	}
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	public static class ExperiencePatch
	{
		[HarmonyPrefix]
		public static void Prefix(Skills __instance, ref float factor)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)((Character)localPlayer).GetSkills() != (Object)(object)__instance))
			{
				float bonus = TalentState.GetBonus((Character)(object)localPlayer, TalentEffect.Experience);
				if (bonus > 0f)
				{
					factor *= 1f + bonus / 100f;
				}
			}
		}
	}
	[HarmonyPatch]
	public static class GatherDamagePatch
	{
		public static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(TreeBase), "RPC_Damage", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(TreeLog), "RPC_Damage", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Destructible), "RPC_Damage", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(MineRock), "RPC_Hit", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(MineRock5), "RPC_Damage", (Type[])null, (Type[])null);
		}

		[HarmonyPrefix]
		public static void Prefix(HitData hit)
		{
			try
			{
				Character attacker = hit.GetAttacker();
				Player val = (Player)(object)((attacker is Player) ? attacker : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				float bonus = TalentState.GetBonus((Character)(object)val, TalentEffect.MiningDamage);
				if (bonus > 0f && hit.m_damage.m_pickaxe > 0f)
				{
					hit.m_damage.m_pickaxe *= 1f + bonus / 100f;
				}
				if ((Object)(object)val == (Object)(object)Player.m_localPlayer)
				{
					int num = ((hit.m_damage.m_pickaxe > 0f) ? TalentState.PerkRank((Character)(object)val, TalentPerk.Prospector) : 0);
					if (num > 0)
					{
						((Character)val).AddStamina(Perks.Value(TalentPerk.Prospector) * (float)num);
					}
					int num2 = ((hit.m_damage.m_chop > 0f) ? TalentState.PerkRank((Character)(object)val, TalentPerk.Woodsman) : 0);
					if (num2 > 0)
					{
						((Character)val).Heal(Perks.Value(TalentPerk.Woodsman) * (float)num2, false);
					}
				}
				float bonus2 = TalentState.GetBonus((Character)(object)val, TalentEffect.ChoppingDamage);
				if (bonus2 > 0f && hit.m_damage.m_chop > 0f)
				{
					hit.m_damage.m_chop *= 1f + bonus2 / 100f;
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("GatherDamagePatch: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Pickable), "Interact")]
	public static class HarvestPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Pickable __instance, Humanoid character, bool __result)
		{
			try
			{
				Player val = (Player)(object)((character is Player) ? character : null);
				if (!__result || (Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
				{
					return;
				}
				float bonus = TalentState.GetBonus((Character)(object)val, TalentEffect.HarvestYield);
				int num = TalentState.PerkRank((Character)(object)val, TalentPerk.Bountiful);
				if ((!(bonus <= 0f) || num != 0) && !((Object)(object)__instance.m_itemPrefab == (Object)null) && __instance.m_amount > 0)
				{
					float num2 = (float)__instance.m_amount * bonus / 100f;
					if (num > 0 && Random.value * 100f < Perks.Value(TalentPerk.Bountiful) * (float)num)
					{
						num2 += (float)__instance.m_amount;
					}
					int num3 = (int)num2;
					if (Random.value < num2 - (float)num3)
					{
						num3++;
					}
					if (num3 > 0)
					{
						Traverse.Create((object)__instance).Method("Drop", new object[3] { __instance.m_itemPrefab, 2, num3 }).GetValue();
					}
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("HarvestPatch: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "EatFood")]
	public static class FoodDurationPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player __instance, ItemData item, bool __result)
		{
			try
			{
				if (!__result || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
				{
					return;
				}
				int num = TalentState.PerkRank((Character)(object)__instance, TalentPerk.Feast);
				if (num > 0)
				{
					((Character)__instance).Heal(((Character)__instance).GetMaxHealth() * Perks.Value(TalentPerk.Feast) * (float)num / 100f, true);
				}
				float bonus = TalentState.GetBonus((Character)(object)__instance, TalentEffect.FoodDuration);
				if (bonus <= 0f)
				{
					return;
				}
				IList value = Traverse.Create((object)__instance).Field("m_foods").GetValue<IList>();
				if (value == null)
				{
					return;
				}
				foreach (object item2 in value)
				{
					Traverse val = Traverse.Create(item2);
					ItemData value2 = val.Field("m_item").GetValue<ItemData>();
					if (value2 != null && value2.m_shared.m_name == item.m_shared.m_name)
					{
						val.Field("m_time").SetValue((object)(val.Field("m_time").GetValue<float>() * (1f + bonus / 100f)));
					}
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("FoodDurationPatch: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Skills), "GetSkillFactor")]
	public static class FishingSkillPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Skills __instance, SkillType skillType, ref float __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			if ((int)skillType != 104)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)((Character)localPlayer).GetSkills() != (Object)(object)__instance))
			{
				float bonus = TalentState.GetBonus((Character)(object)localPlayer, TalentEffect.FishingSkill);
				if (bonus > 0f)
				{
					__result = Mathf.Min(1f, __result + bonus / 100f);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	public static class DamagePatch
	{
		[HarmonyPrefix]
		public static void Prefix(Character __instance, HitData hit)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				float num = 1f;
				Character attacker = hit.GetAttacker();
				Player val = (Player)(object)((attacker is Player) ? attacker : null);
				if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)__instance)
				{
					float bonus = TalentState.GetBonus((Character)(object)val, TalentEffect.AllDamage);
					bonus += SkillBonus(val, hit.m_skill);
					num *= 1f + bonus / 100f;
					num *= Perks.DamageMultiplier(val, __instance, hit);
				}
				Player val2 = (Player)(object)((__instance is Player) ? __instance : null);
				if ((Object)(object)val2 != (Object)null)
				{
					float num2 = Mathf.Min(TalentState.GetBonus((Character)(object)val2, TalentEffect.ElementalResist), 75f);
					if (num2 > 0f)
					{
						float num3 = 1f - num2 / 100f;
						hit.m_damage.m_fire *= num3;
						hit.m_damage.m_frost *= num3;
						hit.m_damage.m_lightning *= num3;
						hit.m_damage.m_poison *= num3;
						hit.m_damage.m_spirit *= num3;
					}
				}
				if (!Mathf.Approximately(num, 1f))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(num);
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("DamagePatch: " + ex.Message));
			}
		}

		private static float SkillBonus(Player attacker, SkillType skill)
		{
			//IL_0000: 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_0040: Expected I4, but got Unknown
			return (skill - 1) switch
			{
				0 => Both(attacker, TalentEffect.MeleeDamage, TalentEffect.SwordDamage), 
				6 => Both(attacker, TalentEffect.MeleeDamage, TalentEffect.AxeDamage), 
				2 => Both(attacker, TalentEffect.MeleeDamage, TalentEffect.ClubDamage), 
				1 => Both(attacker, TalentEffect.MeleeDamage, TalentEffect.KnifeDamage), 
				7 => Both(attacker, TalentEffect.RangedDamage, TalentEffect.BowDamage), 
				13 => Both(attacker, TalentEffect.RangedDamage, TalentEffect.CrossbowDamage), 
				8 => Both(attacker, TalentEffect.MagicDamage, TalentEffect.ElementalDamage), 
				9 => Both(attacker, TalentEffect.MagicDamage, TalentEffect.BloodDamage), 
				_ => 0f, 
			};
		}

		private static float Both(Player attacker, TalentEffect category, TalentEffect specific)
		{
			return TalentState.GetBonus((Character)(object)attacker, category) + TalentState.GetBonus((Character)(object)attacker, specific);
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	public static class LifeStealPatch
	{
		[HarmonyPrefix]
		public static void Prefix(Character __instance, HitData hit)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || __instance is Player || (Object)(object)hit.GetAttacker() != (Object)(object)localPlayer)
				{
					return;
				}
				if ((int)hit.m_skill == 10)
				{
					int num = TalentState.PerkRank((Character)(object)localPlayer, TalentPerk.Siphon);
					if (num > 0)
					{
						((Character)localPlayer).AddEitr(Perks.Value(TalentPerk.Siphon) * (float)num);
					}
				}
				float bonus = TalentState.GetBonus((Character)(object)localPlayer, TalentEffect.LifeSteal);
				if (!(bonus <= 0f))
				{
					float num2 = Mathf.Min(hit.GetTotalDamage() * Mathf.Min(bonus, 50f) / 100f, ((Character)localPlayer).GetMaxHealth() * 0.25f);
					if (num2 >= 0.5f)
					{
						((Character)localPlayer).Heal(num2, true);
					}
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("LifeStealPatch: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Attack), "UseAmmo")]
	public static class AmmoSavePatch
	{
		[HarmonyPostfix]
		public static void Postfix(Attack __instance, ItemData ammoItem, bool __result)
		{
			try
			{
				Humanoid value = Traverse.Create((object)__instance).Field("m_character").GetValue<Humanoid>();
				Player val = (Player)(object)((value is Player) ? value : null);
				if (__result && ammoItem != null && !((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !((Object)(object)ammoItem.m_dropPrefab == (Object)null))
				{
					float num = Mathf.Min(TalentState.GetBonus((Character)(object)val, TalentEffect.AmmoSave), 75f);
					if (num > 0f && Random.value * 100f < num)
					{
						((Humanoid)val).GetInventory().AddItem(ammoItem.m_dropPrefab, 1);
					}
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("AmmoSavePatch: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UseStamina")]
	public static class SwimStaminaPatch
	{
		[HarmonyPrefix]
		public static void Prefix(Player __instance, ref float v)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !(v <= 0f) && ((Character)__instance).IsSwimming())
			{
				int num = TalentState.PerkRank((Character)(object)__instance, TalentPerk.Swimmer);
				if (num > 0)
				{
					v *= Mathf.Max(0.1f, 1f - Perks.Value(TalentPerk.Swimmer) * (float)num / 100f);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "FixedUpdate")]
	public static class RegenPatch
	{
		private static float _timer;

		[HarmonyPostfix]
		public static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsDead())
			{
				return;
			}
			_timer += Time.fixedDeltaTime;
			if (!(_timer < 1f))
			{
				float timer = _timer;
				_timer = 0f;
				float bonus = TalentState.GetBonus((Character)(object)__instance, TalentEffect.HealthRegen);
				if (bonus > 0f && ((Character)__instance).GetHealth() < ((Character)__instance).GetMaxHealth())
				{
					((Character)__instance).Heal(bonus * timer, false);
				}
				float bonus2 = TalentState.GetBonus((Character)(object)__instance, TalentEffect.StaminaRegen);
				if (bonus2 > 0f && !((Character)__instance).IsRunning() && __instance.GetStamina() < ((Character)__instance).GetMaxStamina())
				{
					((Character)__instance).AddStamina(5f * bonus2 / 100f * timer);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	public static class SpawnPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				TalentState.Mirror(__instance);
				ServerSync.SendHello(__instance);
			}
		}
	}
	public enum TalentPerk
	{
		None,
		Rhythm,
		Assassin,
		Execute,
		Stagger,
		Sniper,
		Opener,
		Echo,
		Siphon,
		Prospector,
		Woodsman,
		Bountiful,
		Feast,
		Swimmer
	}
	public static class Perks
	{
		private static readonly Dictionary<ZDOID, int> SwordHits = new Dictionary<ZDOID, int>();

		public static string Text(TalentPerk p)
		{
			return p switch
			{
				TalentPerk.Rhythm => "Every 5th sword hit deals +75% damage", 
				TalentPerk.Assassin => "Melee hits while crouching deal +40% damage", 
				TalentPerk.Execute => "Axe hits deal +40% damage to enemies below 25% health", 
				TalentPerk.Stagger => "Mace hits have +50% stagger power", 
				TalentPerk.Sniper => "Bow hits deal +30% damage to targets farther than 20 m", 
				TalentPerk.Opener => "Crossbow hits deal +25% damage to enemies above 90% health", 
				TalentPerk.Echo => "15% chance for elemental spells to deal double damage", 
				TalentPerk.Siphon => "Each blood magic hit restores 2 eitr", 
				TalentPerk.Prospector => "Each pickaxe hit restores 2 stamina", 
				TalentPerk.Woodsman => "Each axe hit on a tree restores 2 health", 
				TalentPerk.Bountiful => "20% chance to double what you harvest", 
				TalentPerk.Feast => "Eating restores 10% of your max health at once", 
				TalentPerk.Swimmer => "35% less stamina used while swimming", 
				_ => string.Empty, 
			};
		}

		public static float Value(TalentPerk p)
		{
			return p switch
			{
				TalentPerk.Rhythm => 75f, 
				TalentPerk.Assassin => 40f, 
				TalentPerk.Execute => 40f, 
				TalentPerk.Stagger => 50f, 
				TalentPerk.Sniper => 30f, 
				TalentPerk.Opener => 25f, 
				TalentPerk.Echo => 15f, 
				TalentPerk.Siphon => 2f, 
				TalentPerk.Prospector => 2f, 
				TalentPerk.Woodsman => 2f, 
				TalentPerk.Bountiful => 20f, 
				TalentPerk.Feast => 10f, 
				TalentPerk.Swimmer => 35f, 
				_ => 0f, 
			};
		}

		public static TalentPerk ForGroup(string group)
		{
			return group switch
			{
				"Swords" => TalentPerk.Rhythm, 
				"Knives" => TalentPerk.Assassin, 
				"Axes" => TalentPerk.Execute, 
				"Maces" => TalentPerk.Stagger, 
				"Bows" => TalentPerk.Sniper, 
				"Crossbows" => TalentPerk.Opener, 
				"Elemental Magic" => TalentPerk.Echo, 
				"Blood Magic" => TalentPerk.Siphon, 
				"Mining" => TalentPerk.Prospector, 
				"Woodcutting" => TalentPerk.Woodsman, 
				"Farming" => TalentPerk.Bountiful, 
				"Cooking" => TalentPerk.Feast, 
				"Fishing" => TalentPerk.Swimmer, 
				_ => TalentPerk.None, 
			};
		}

		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_0013: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0019: 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 != 2 && (int)s != 7 && (int)s != 3 && (int)s != 11 && (int)s != 4)
			{
				return (int)s == 5;
			}
			return true;
		}

		public static float DamageMultiplier(Player attacker, Character victim, HitData hit)
		{
			//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_000e: Invalid comparison between Unknown and I4
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Invalid comparison between Unknown and I4
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Invalid comparison between Unknown and I4
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Invalid comparison between Unknown and I4
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Invalid comparison between Unknown and I4
			//IL_0194: Invalid comparison between Unknown and I4
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			float num = 1f;
			SkillType skill = hit.m_skill;
			int num2;
			if ((int)skill == 1 && (num2 = TalentState.PerkRank((Character)(object)attacker, TalentPerk.Rhythm)) > 0)
			{
				ZDOID zDOID = ((Character)attacker).GetZDOID();
				SwordHits.TryGetValue(zDOID, out var value);
				value++;
				if (value >= 5)
				{
					value = 0;
					num *= 1f + Value(TalentPerk.Rhythm) * (float)num2 / 100f;
				}
				if (SwordHits.Count > 64)
				{
					SwordHits.Clear();
				}
				SwordHits[zDOID] = value;
			}
			if (IsMelee(skill) && (num2 = TalentState.PerkRank((Character)(object)attacker, TalentPerk.Assassin)) > 0 && ((Character)attacker).IsCrouching())
			{
				num *= 1f + Value(TalentPerk.Assassin) * (float)num2 / 100f;
			}
			if ((int)skill == 7 && (num2 = TalentState.PerkRank((Character)(object)attacker, TalentPerk.Execute)) > 0 && victim.GetHealthPercentage() < 0.25f)
			{
				num *= 1f + Value(TalentPerk.Execute) * (float)num2 / 100f;
			}
			if ((int)skill == 3 && (num2 = TalentState.PerkRank((Character)(object)attacker, TalentPerk.Stagger)) > 0)
			{
				hit.m_staggerMultiplier *= 1f + Value(TalentPerk.Stagger) * (float)num2 / 100f;
			}
			if ((int)skill == 8 && (num2 = TalentState.PerkRank((Character)(object)attacker, TalentPerk.Sniper)) > 0 && Vector3.Distance(((Component)attacker).transform.position, ((Component)victim).transform.position) > 20f)
			{
				num *= 1f + Value(TalentPerk.Sniper) * (float)num2 / 100f;
			}
			if ((int)skill == 14 && (num2 = TalentState.PerkRank((Character)(object)attacker, TalentPerk.Opener)) > 0 && victim.GetHealthPercentage() >= 0.9f)
			{
				num *= 1f + Value(TalentPerk.Opener) * (float)num2 / 100f;
			}
			if ((int)skill == 9 && (num2 = TalentState.PerkRank((Character)(object)attacker, TalentPerk.Echo)) > 0 && Random.value * 100f < Value(TalentPerk.Echo) * (float)num2)
			{
				num *= 2f;
			}
			return num;
		}
	}
	[BepInPlugin("cronus.talentforge", "TalentForge", "0.13.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class TalentForgePlugin : BaseUnityPlugin
	{
		public const string ModGuid = "cronus.talentforge";

		public const string ModName = "TalentForge";

		public const string ModVersion = "0.13.3";

		public static ConfigEntry<int> PointsPerLevel;

		public static ConfigEntry<int> PointsPerBoss;

		public static ConfigEntry<int> PointsPerPrestigeRank;

		public static ConfigEntry<int> CostMinor;

		public static ConfigEntry<int> CostNotable;

		public static ConfigEntry<int> CostKeystone;

		public static ConfigEntry<float> NotableMultiplier;

		public static ConfigEntry<float> KeystoneMultiplier;

		public static ConfigEntry<bool> LockToServer;

		public static ConfigEntry<int> MaxRankNotable;

		public static ConfigEntry<int> MaxRankKeystone;

		public static ConfigEntry<KeyboardShortcut> OpenWindowKey;

		public static ConfigEntry<bool> ShowInventoryButton;

		public static ConfigEntry<string> Language;

		public static ConfigEntry<float> InventoryButtonOffsetX;

		public static ConfigEntry<float> InventoryButtonOffsetY;

		private Harmony _harmony;

		public static TalentForgePlugin Instance { get; private set; }

		public static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		private void Awake()
		{
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Expected O, but got Unknown
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Expected O, but got Unknown
			Instance = this;
			PointsPerLevel = BindSynced("1 - Points", "Points per level", 1, 0, 20, "Talent points granted per level.");
			PointsPerBoss = BindSynced("1 - Points", "Points per boss", 5, 0, 50, "Talent points granted for each defeated boss.");
			PointsPerPrestigeRank = BindSynced("1 - Points", "Points per prestige rank", 10, 0, 500, "Extra talent points granted for each prestige rank. Raise this to make every prestige worth more.");
			CostMinor = BindSynced("1 - Points", "Point cost: minor node", 1, 1, 20, "Points needed to take a minor node.");
			CostNotable = BindSynced("1 - Points", "Point cost: notable node", 2, 1, 20, "Points needed to take a notable node.");
			CostKeystone = BindSynced("1 - Points", "Point cost: keystone node", 3, 1, 20, "Points needed to take a keystone node.");
			TalentGraph.BindConfig();
			NotableMultiplier = BindSynced("3 - Tiers", "Notable node multiplier", 3f, 1f, 20f, "A notable node gives this many times the value of a minor node.");
			KeystoneMultiplier = BindSynced("3 - Tiers", "Keystone node multiplier", 6f, 1f, 30f, "A keystone node gives this many times the value of a minor node.");
			MaxRankNotable = BindSynced("3 - Tiers", "Notable node max rank", 3, 1, 10, "How many points can be put in one notable node. Each rank costs the node's point cost and adds its full value again.");
			MaxRankKeystone = BindSynced("3 - Tiers", "Keystone node max rank", 2, 1, 10, "How many points can be put in one keystone node. Each rank costs the node's point cost and adds its full value again.");
			LockToServer = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Server", "Lock talents to server", false, new ConfigDescription("When true, the server keeps every player's talent allocation: players get it back when they join (their own save is overwritten) and each change is checked and stored by the server. [Synced with Server]", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			OpenWindowKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("4 - Interface", "Open window", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Key that opens and closes the talent tree. This setting is personal and not synced.");
			Language = ((BaseUnityPlugin)this).Config.Bind<string>("4 - Interface", "Language", "auto", new ConfigDescription("Language of the talent window: auto (follows the game), en or pt. Other languages: add a file TalentForge.<code>.txt to the config folder (one line 'English text=Translation') and type its code here. Personal, not synced.", (AcceptableValueBase)null, Array.Empty<object>()));
			ShowInventoryButton = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Interface", "Show inventory button", true, "Adds a Talents button next to the Skills button in the inventory. Personal, not synced.");
			InventoryButtonOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Interface", "Inventory button extra offset X", 0f, "Extra horizontal nudge for the Talents button after it is placed at the end of the icon row. Personal, not synced.");
			InventoryButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Interface", "Inventory button extra offset Y", 0f, "Extra vertical nudge for the Talents button (negative is down). Personal, not synced.");
			TalentGraph.Build();
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				TalentState.ClearCache();
				Loc.Reload();
			};
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new PointsCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new NodesCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new TakeCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new FreeCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new RespecCommand());
			_harmony = new Harmony("cronus.talentforge");
			_harmony.PatchAll(typeof(TalentForgePlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("TalentForge 0.13.3 loaded. " + TalentGraph.All.Count + " nodes. Points per level=" + PointsPerLevel.Value + ", per boss=" + PointsPerBoss.Value + ", per prestige rank=" + PointsPerPrestigeRank.Value));
		}

		private void Update()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				KeyboardShortcut value = OpenWindowKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					TalentWindow.Toggle();
				}
			}
			TalentWindow.Tick();
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}

		public static ConfigEntry<T> BindSynced<T>(string section, string key, T defaultValue, T min, T max, string description) where T : IComparable
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			return ((BaseUnityPlugin)Instance).Config.Bind<T>(section, key, defaultValue, new ConfigDescription(description + " [Synced with Server]", (AcceptableValueBase)(object)new AcceptableValueRange<T>(min, max), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}
	}
	public abstract class TfCommand : ConsoleCommand
	{
		public override bool IsCheat => false;

		protected static Player RequirePlayer()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				Console.instance.Print("TalentForge: enter a world first.");
			}
			return localPlayer;
		}
	}
	public class PointsCommand : TfCommand
	{
		public override string Name => "tf_points";

		public override string Help => "Shows your TalentForge talent points: level, bosses, prestige and the total.";

		public override void Run(string[] args)
		{
			Player val = TfCommand.RequirePlayer();
			if (!((Object)(object)val == (Object)null))
			{
				TalentBudget.Breakdown breakdown = TalentBudget.Get(val);
				Console.instance.Print("TalentForge points: " + breakdown.Available + " available of " + breakdown.Total + " total (" + breakdown.Spent + " spent)");
				Console.instance.Print("  level " + breakdown.Level + " x " + TalentForgePlugin.PointsPerLevel.Value + " = " + breakdown.FromLevel);
				Console.instance.Print("  bosses " + breakdown.Bosses + " x " + TalentForgePlugin.PointsPerBoss.Value + " = " + breakdown.FromBosses);
				Console.instance.Print("  prestige rank " + breakdown.Prestige + " x " + TalentForgePlugin.PointsPerPrestigeRank.Value + " = " + breakdown.FromPrestige);
			}
		}
	}
	public class NodesCommand : TfCommand
	{
		public override string Name => "tf_nodes";

		public override string Help => "Lists the nodes you own and the nodes you can take next (with their ids).";

		public override void Run(string[] args)
		{
			Player val = TfCommand.RequirePlayer();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			HashSet<string> hashSet = TalentState.Get((Character)(object)val);
			Console.instance.Print("TalentForge nodes owned: " + hashSet.Count + ". Use tf_take <id> and tf_free <id>.");
			foreach (TalentNode item in TalentGraph.All)
			{
				if (hashSet.Contains(item.Id))
				{
					Console.instance.Print("  [x] " + item.Id + "  " + item.Name + "  " + item.Describe());
				}
			}
			Console.instance.Print("Available next:");
			foreach (TalentNode item2 in TalentGraph.All)
			{
				if (TalentGraph.IsAvailable(item2, hashSet))
				{
					Console.instance.Print("  [ ] " + item2.Id + "  " + item2.Name + " (" + item2.Tier.ToString() + ", " + item2.Cost + " pt)  " + item2.Describe());
				}
			}
		}
	}
	public class TakeCommand : TfCommand
	{
		public override string Name => "tf_take";

		public override string Help => "tf_take <node id> - takes a node that touches your tree. See tf_nodes.";

		public override void Run(string[] args)
		{
			Player val = TfCommand.RequirePlayer();
			if (!((Object)(object)val == (Object)null))
			{
				if (args.Length < 1 || !TalentGraph.Nodes.TryGetValue(args[0].ToLowerInvariant(), out var value))
				{
					Console.instance.Print("Usage: tf_take <node id>. Use tf_nodes to see the ids.");
					return;
				}
				TalentActions.TryAllocate(val, value, out var message);
				Console.instance.Print("TalentForge: " + message);
				TalentWindow.Refresh(null);
			}
		}
	}
	public class FreeCommand : TfCommand
	{
		public override string Name => "tf_free";

		public override string Help => "tf_free <node id> - refunds a node, as long as it does not hold up other nodes.";

		public override void Run(string[] args)
		{
			Player val = TfCommand.RequirePlayer();
			if (!((Object)(object)val == (Object)null))
			{
				if (args.Length < 1 || !TalentGraph.Nodes.TryGetValue(args[0].ToLowerInvariant(), out var value))
				{
					Console.instance.Print("Usage: tf_free <node id>. Use tf_nodes to see the ids.");
					return;
				}
				TalentActions.TryRefund(val, value, out var message);
				Console.instance.Print("TalentForge: " + message);
				TalentWindow.Refresh(null);
			}
		}
	}
	public class RespecCommand : TfCommand
	{
		public override string Name => "tf_respec";

		public override string Help => "Refunds every TalentForge node.";

		public override void Run(string[] args)
		{
			Player val = TfCommand.RequirePlayer();
			if (!((Object)(object)val == (Object)null))
			{
				TalentActions.Respec(val);
				Console.instance.Print("TalentForge: all nodes were refunded.");
				TalentWindow.Refresh(null);
			}
		}
	}
	public static class ServerSync
	{
		private const string HelloRpc = "TalentForge_Hello";

		private const string SaveRpc = "TalentForge_Save";

		private const string SetRpc = "TalentForge_Set";

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

		private static bool _loaded;

		private static bool _applying;

		private static string StorePath => Path.Combine(Paths.ConfigPath, "TalentForge.server-data.txt");

		private static bool Active
		{
			get
			{
				if (TalentForgePlugin.LockToServer != null && TalentForgePlugin.LockToServer.Value && ZRoutedRpc.instance != null)
				{
					return (Object)(object)ZNet.instance != (Object)null;
				}
				return false;
			}
		}

		private static bool ServerActive
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && TalentForgePlugin.LockToServer != null)
				{
					return TalentForgePlugin.LockToServer.Value;
				}
				return false;
			}
		}

		private static long ServerId()
		{
			return Traverse.Create((object)ZRoutedRpc.instance).Method("GetServerPeerID", Array.Empty<object>()).GetValue<long>();
		}

		public static void Register()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				instance.Register<string>("TalentForge_Hello", (Action<long, string>)OnHello);
				instance.Register<string>("TalentForge_Save", (Action<long, string>)OnSave);
				instance.Register<string>("TalentForge_Set", (Action<long, string>)OnSet);
			}
		}

		public static void SendHello(Player player)
		{
			if (Active)
			{
				player.m_customData.TryGetValue("TalentForge.Nodes", out var value);
				ZRoutedRpc.instance.InvokeRoutedRPC(ServerId(), "TalentForge_Hello", new object[1] { value ?? string.Empty });
			}
		}

		public static void OnLocalSave(string raw)
		{
			if (!_applying && Active)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(ServerId(), "TalentForge_Save", new object[1] { raw ?? string.Empty });
			}
		}

		private static void OnSet(long sender, string raw)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			_applying = true;
			try
			{
				if (raw.Length == 0)
				{
					localPlayer.m_customData.Remove("TalentForge.Nodes");
				}
				else
				{
					localPlayer.m_customData["TalentForge.Nodes"] = raw;
				}
				TalentState.ClearCache();
				TalentState.Mirror(localPlayer);
				TalentWindow.Refresh(Loc.T("The server restored your talents."));
			}
			finally
			{
				_applying = false;
			}
		}

		private static string KeyOf(long sender)
		{
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			if (peer == null || peer.m_socket == null)
			{
				return "host";
			}
			return peer.m_socket.GetHostName();
		}

		private static void Load()
		{
			if (_loaded)
			{
				return;
			}
			_loaded = true;
			try
			{
				if (!File.Exists(StorePath))
				{
					return;
				}
				string[] array = File.ReadAllLines(StorePath);
				foreach (string text in array)
				{
					int num = text.IndexOf('\t');
					if (num > 0)
					{
						Store[text.Substring(0, num)] = text.Substring(num + 1);
					}
				}
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("TalentForge: could not read the server data: " + ex.Message));
			}
		}

		private static void Persist()
		{
			try
			{
				List<string> list = new List<string>();
				foreach (KeyValuePair<string, string> item in Store)
				{
					list.Add(item.Key + "\t" + item.Value);
				}
				File.WriteAllLines(StorePath, list.ToArray());
			}
			catch (Exception ex)
			{
				TalentForgePlugin.Log.LogWarning((object)("TalentForge: could not write the server data: " + ex.Message));
			}
		}

		private static void OnHello(long sender, string raw)
		{
			if (ServerActive)
			{
				Load();
				string key = KeyOf(sender);
				if (!Store.TryGetValue(key, out var value))
				{
					value = TalentState.Sanitize(raw);
					Store[key] = value;
					Persist();
				}
				if (!TalentState.SameAllocation(value, raw))
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(sender, "TalentForge_Set", new object[1] { value });
				}
			}
		}

		private static void OnSave(long sender, string raw)
		{
			if (ServerActive)
			{
				Load();
				string text = TalentState.Sanitize(raw);
				Store[KeyOf(sender)] = text;
				Persist();
				if (!TalentState.SameAllocation(text, raw))
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(sender, "TalentForge_Set", new object[1] { text });
				}
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	public static class GameStartPatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			ServerSync.Register();
		}
	}
	public static class TalentActions
	{
		public static bool TryAllocate(Player player, TalentNode node, out string message)
		{
			if (node.IsOrigin)
			{
				message = Loc.T("The origin is always yours.");
				return false;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>(TalentState.GetRanks((Character)(object)player));
			dictionary.TryGetValue(node.Id, out var value);
			if (value >= node.MaxRank)
			{
				message = ((node.MaxRank > 1) ? Loc.F("{0} is already at its maximum rank ({1}).", Loc.T(node.Name), node.MaxRank) : Loc.F("{0} is already taken.", Loc.T(node.Name)));
				return false;
			}
			if (value == 0 && !TalentGraph.IsAvailable(node, new HashSet<string>(dictionary.Keys)))
			{
				message = Loc.F("{0} is not connected to your tree yet.", Loc.T(node.Name));
				return false;
			}
			TalentBudget.Breakdown breakdown = TalentBudget.Get(player);
			if (breakdown.Available < node.Cost)
			{
				message = Loc.F("Not enough points for {0} (needs {1}, you have {2}).", Loc.T(node.Name), node.Cost, breakdown.Available);
				return false;
			}
			dictionary[node.Id] = value + 1;
			TalentState.Save(player, dictionary);
			message = ((node.MaxRank > 1) ? Loc.F("{0} rank {1}/{2}: {3} per rank.", Loc.T(node.Name), value + 1, node.MaxRank, node.Describe()) : Loc.F("{0} taken: {1}.", Loc.T(node.Name), node.Describe()));
			return true;
		}

		public static bool TryRefund(Player player, TalentNode node, out string message)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(TalentState.GetRanks((Character)(object)player));
			if (!dictionary.TryGetValue(node.Id, out var value))
			{
				message = Loc.F("{0} is not taken.", Loc.T(node.Name));
				return false;
			}
			if (value > 1)
			{
				dictionary[node.Id] = value - 1;
			}
			else
			{
				if (!TalentGraph.CanRefund(node, new HashSet<string>(dictionary.Keys)))
				{
					message = Loc.F("{0} holds up other nodes. Refund those first, or use Respec.", Loc.T(node.Name));
					return false;
				}
				dictionary.Remove(node.Id);
			}
			TalentState.Save(player, dictionary);
			message = Loc.F("{0} refunded ({1} point(s) back).", Loc.T(node.Name), node.Cost);
			return true;
		}

		public static void Respec(Player player)
		{
			TalentState.Save(player, new Dictionary<string, int>());
		}
	}
	public static class TalentBudget
	{
		public struct Breakdown
		{
			public int Level;

			public int Bosses;

			public int Prestige;

			public int FromLevel;

			public int FromBosses;

			public int FromPrestige;

			public int Total;

			public int Spent;

			public int Available => Total - Spent;
		}

		public static Breakdown Get(Player player)
		{
			Breakdown result = default(Breakdown);
			if ((Object)(object)player == (Object)null)
			{
				return result;
			}
			result.Level = SkillsReworkedApi.GetLevel(player);
			result.Bosses = SkillsReworkedApi.GetDefeatedBossCount(player);
			result.Prestige = SkillsReworkedApi.GetPrestigeLevel(player);
			result.FromLevel = result.Level * TalentForgePlugin.PointsPerLevel.Value;
			result.FromBosses = result.Bosses * TalentForgePlugin.PointsPerBoss.Value;
			result.FromPrestige = result.Prestige * TalentForgePlugin.PointsPerPrestigeRank.Value;
			result.Total = result.FromLevel + result.FromBosses + result.FromPrestige;
			result.Spent = TalentState.SpentPoints(TalentState.GetRanks((Character)(object)player));
			return result;
		}
	}
	public enum TalentEffect
	{
		MeleeDamage,
		RangedDamage,
		MagicDamage,
		AllDamage,
		MaxHealth,
		MaxStamina,
		MaxEitr,
		RunSpeed,
		SwordDamage,
		AxeDamage,
		ClubDamage,
		KnifeDamage,
		BowDamage,
		CrossbowDamage,
		ElementalDamage,
		BloodDamage,
		Experience,
		ArmorBonus,
		MiningDamage,
		ChoppingDamage,
		HarvestYield,
		FoodDuration,
		FishingSkill,
		LifeSteal,
		HealthRegen,
		StaminaRegen,
		ElementalResist,
		AmmoSave
	}
	public enum NodeTier
	{
		Minor,
		Notable,
		Keystone
	}
	public class EffectAmount
	{
		public TalentEffect Effect;

		public float Factor;
	}
	public class TalentNode
	{
		public string Id;

		public string Name;

		public string Group;

		public NodeTier Tier;

		public Vector2 Pos;

		public bool IsOrigin;

		public readonly List<EffectAmount> Effects = new List<EffectAmount>();

		public readonly List<string> Links = new List<string>();

		public TalentPerk Perk;

		public int Cost
		{
			get
			{
				if (IsOrigin)
				{
					return 0;
				}
				return Tier switch
				{
					NodeTier.Notable => TalentForgePlugin.CostNotable.Value, 
					NodeTier.Keystone => TalentForgePlugin.CostKeystone.Value, 
					_ => TalentForgePlugin.CostMinor.Value, 
				};
			}
		}

		public int MaxRank
		{
			get
			{
				if (IsOrigin)
				{
					return 1;
				}
				return Tier switch
				{
					NodeTier.Notable => TalentForgePlugin.MaxRankNotable.Value, 
					NodeTier.Keystone => TalentForgePlugin.MaxRankKeystone.Value, 
					_ => 1, 
				};
			}
		}

		public float TierMultiplier => Tier switch
		{
			NodeTier.Notable => TalentForgePlugin.NotableMultiplier.Value, 
			NodeTier.Keystone => TalentForgePlugin.KeystoneMultiplier.Value, 
			_ => 1f, 
		};

		public string Describe()
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (EffectAmount effect in Effects)
			{
				if (stringBuilder.Length > 0)
				{
					stringBuilder.Append(", ");
				}
				float num = effect.Factor * TalentGraph.BaseValue[effect.Effect].Value * TierMultiplier;
				stringBuilder.Append('+').Append(num.ToString("0.#")).Append(TalentGraph.IsPercent(effect.Effect) ? "% " : " ")
					.Append(Loc.T(TalentGraph.EffectName(effect.Effect)));
			}