Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TalentForge v0.13.3
TalentForge.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
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))); }