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 TalentPace v1.0.1
BepInEx/plugins/Cjayride.TalentPace.dll
Decompiled 3 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("Cjayride.TalentPace")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+a6000ee7633b4415a16149e3a41f8e391f6a523f")] [assembly: AssemblyProduct("Cjayride.TalentPace")] [assembly: AssemblyTitle("Cjayride.TalentPace")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Cjayride.TalentPace { internal static class CapHint { private const string LabelName = "TalentPaceCap"; private static FieldInfo _view; private static PropertyInfo _titleText; private static MethodInfo _getRuntime; private static MethodInfo _totalPoints; private static float _nextSample; private static bool _logged; public static void Apply(Harmony harmony) { //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown Type type = AccessTools.TypeByName("UI.TalentWindow"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "Refresh", (Type[])null, (Type[])null)); MethodInfo methodInfo2 = ((type == null) ? null : AccessTools.DeclaredMethod(type, "Tick", (Type[])null, (Type[])null)); if (methodInfo == null) { Plugin.Log("Talent window refresh was not found. The cap line is off."); return; } _view = AccessTools.Field(type, "_view"); Type type2 = AccessTools.TypeByName("UI.TalentWindowView"); _titleText = ((type2 == null) ? null : AccessTools.Property(type2, "TitleText")); Type type3 = AccessTools.TypeByName("Core.TalentRuntimeService"); Type type4 = AccessTools.TypeByName("Core.TalentRuntime"); Type type5 = AccessTools.TypeByName("Core.TalentPointBudgetService"); _getRuntime = ((type3 == null) ? null : AccessTools.DeclaredMethod(type3, "Get", new Type[1] { typeof(Player) }, (Type[])null)); _totalPoints = ((type5 == null || type4 == null) ? null : AccessTools.DeclaredMethod(type5, "GetTotalPoints", new Type[2] { typeof(Player), type4 }, (Type[])null)); HarmonyMethod val = new HarmonyMethod(typeof(CapHint), "AfterRefresh", (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); if (methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void AfterRefresh() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) Text val = Title(); if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { if (Time.unscaledTime >= _nextSample) { _nextSample = Time.unscaledTime + 0.5f; Sample(localPlayer); } string text = Pace.Hint(localPlayer); if (string.IsNullOrEmpty(text)) { text = "No talent cap."; } Text val2 = Ensure(val); val2.text = text; ((Graphic)val2).color = (text.StartsWith("Talent cap reached", StringComparison.Ordinal) ? new Color(1f, 0.55f, 0.2f, 1f) : new Color(1f, 0.82f, 0.35f, 1f)); ((Component)val2).transform.SetAsLastSibling(); if (!((Component)val2).gameObject.activeSelf) { ((Component)val2).gameObject.SetActive(true); } if (!_logged) { _logged = true; Plugin.Log("Talent window cap line: " + text); } } } private static void Sample(Player player) { if (_getRuntime == null || _totalPoints == null) { return; } try { object obj = _getRuntime.Invoke(null, new object[1] { player }); if (obj != null) { _totalPoints.Invoke(null, new object[2] { player, obj }); } } catch (Exception) { } } private static Text Title() { if (_view == null || _titleText == null) { return null; } object value = _view.GetValue(null); if (value != null) { object? value2 = _titleText.GetValue(value, null); return (Text)((value2 is Text) ? value2 : null); } return null; } private static Text Ensure(Text title) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0114: 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) //IL_012c: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Component)title).transform.parent; Transform val = parent.Find("TalentPaceCap"); Text val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Text>() : null); if ((Object)(object)val2 == (Object)null) { GameObject val3 = new GameObject("TalentPaceCap"); val3.transform.SetParent(parent, false); val2 = val3.AddComponent<Text>(); val2.font = title.font; val2.alignment = (TextAnchor)1; val2.horizontalOverflow = (HorizontalWrapMode)1; val2.verticalOverflow = (VerticalWrapMode)1; ((Graphic)val2).raycastTarget = false; val2.supportRichText = false; } RectTransform rectTransform = ((Graphic)title).rectTransform; RectTransform rectTransform2 = ((Graphic)val2).rectTransform; rectTransform2.anchorMin = rectTransform.anchorMin; rectTransform2.anchorMax = rectTransform.anchorMax; rectTransform2.pivot = new Vector2(0.5f, 1f); rectTransform2.sizeDelta = new Vector2(Mathf.Max(900f, rectTransform.sizeDelta.x), 36f); Rect rect = rectTransform.rect; float num; if (!(((Rect)(ref rect)).height > 1f)) { num = rectTransform.sizeDelta.y; } else { rect = rectTransform.rect; num = ((Rect)(ref rect)).height; } float num2 = num; rectTransform2.anchoredPosition = rectTransform.anchoredPosition + new Vector2(0f, 0f - num2 - 2f); val2.fontSize = Mathf.Clamp(Mathf.RoundToInt((float)title.fontSize * 0.62f), 16, 22); return val2; } } [BepInPlugin("cjayride.talentpace", "Talent Pace", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__11_0; internal void <Awake>b__11_0(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { args.Context.AddString("Talent Pace: no local player."); return; } Pace.Summary summary = Pace.Describe(localPlayer); args.Context.AddString(summary.Line); } } public const string PluginGUID = "cjayride.talentpace"; public const string PluginName = "Talent Pace"; public const string PluginVersion = "1.0.1"; internal static Plugin Instance; internal static ConfigEntry<int> CapNone; internal static ConfigEntry<int> CapEikthyr; internal static ConfigEntry<int> CapElder; internal static ConfigEntry<int> CapBonemass; internal static ConfigEntry<int> CapModer; internal static ConfigEntry<int> CapYagluth; internal static ConfigEntry<bool> QueenRemovesCap; private void Awake() { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown Instance = this; CapNone = BindCap("Before any boss", 9); CapEikthyr = BindCap("After Eikthyr", 15); CapElder = BindCap("After the Elder", 21); CapBonemass = BindCap("After Bonemass", 36); CapModer = BindCap("After Moder", 54); CapYagluth = BindCap("After Yagluth", 75); QueenRemovesCap = ((BaseUnityPlugin)this).Config.Bind<bool>("Caps", "Queen or Fader removes the cap", true, "Personal defeat of the Queen or Fader removes the point cap. The saved talents were never deleted."); if (AccessTools.TypeByName("Core.TalentPointBudgetService") == null || AccessTools.TypeByName("Core.TalentRebuildService") == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Talent Tree types were not found. Talent Pace is off."); return; } Harmony val = new Harmony("cjayride.talentpace"); val.PatchAll(typeof(BudgetPatch)); val.PatchAll(typeof(RebuildPatch)); val.PatchAll(typeof(BossKeyPatch)); CapHint.Apply(val); object obj = <>c.<>9__11_0; if (obj == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { args.Context.AddString("Talent Pace: no local player."); } else { Pace.Summary summary = Pace.Describe(localPlayer); args.Context.AddString(summary.Line); } }; <>c.<>9__11_0 = val2; obj = (object)val2; } new ConsoleCommand("talentpace", "Print the Talent Pace cap for your character.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Talent Pace 1.0.1 loaded."); } private ConfigEntry<int> BindCap(string key, int value) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown return ((BaseUnityPlugin)this).Config.Bind<int>("Caps", key, value, new ConfigDescription("Talent points that apply once this player has that boss key. Outer nodes past the cap stay saved and turn back on after the next boss.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 500), Array.Empty<object>())); } internal static void Log(string message) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)message); } } [HarmonyPatch] internal static class BudgetPatch { private static MethodBase TargetMethod() { return AccessTools.DeclaredMethod(AccessTools.TypeByName("Core.TalentPointBudgetService"), "GetTotalPoints", new Type[2] { typeof(Player), AccessTools.TypeByName("Core.TalentRuntime") }, (Type[])null); } private static void Postfix([HarmonyArgument(0)] Player player, ref int __result) { if (!((Object)(object)player == (Object)null)) { Pace.RememberNatural(player, __result); int num = Pace.CapFor(player); if (num < int.MaxValue && __result > num) { __result = num; } } } } [HarmonyPatch] internal static class RebuildPatch { private static MethodBase TargetMethod() { return AccessTools.DeclaredMethod(AccessTools.TypeByName("Core.TalentRebuildService"), "Rebuild", new Type[1] { AccessTools.TypeByName("Core.TalentRuntime") }, (Type[])null); } private static void Postfix([HarmonyArgument(0)] object runtime) { Pace.TrimCache(runtime); } } [HarmonyPatch(typeof(Player), "AddUniqueKey")] internal static class BossKeyPatch { private static void Postfix(Player __instance, string name) { if (!((Object)(object)__instance == (Object)null) && !string.IsNullOrEmpty(name) && name.IndexOf("defeated_", StringComparison.Ordinal) >= 0) { Pace.MarkDirty(__instance); } } } internal static class Pace { internal struct Summary { public string Line; } private struct Node { public string Id; public int Ring; public int Cost; public int Rank; public Node(string id, int ring, int cost, int rank) { Id = id; Ring = ring; Cost = cost; Rank = rank; } } private const int Minor = 0; private const int Keystone = 1; private const int Mid = 2; private static readonly string[] BossOrder = new string[5] { "defeated_eikthyr", "defeated_gdking", "defeated_bonemass", "defeated_dragon", "defeated_goblinking" }; private static MethodInfo _tryGet; private static MethodInfo _buildCache; private static MethodInfo _sync; private static MethodInfo _getRuntime; private static FieldInfo _runtimes; private static PropertyInfo _isDirty; private static bool _reflected; private static bool _reflectFailed; private static readonly string[] NextBoss = new string[5] { "Eikthyr", "the Elder", "Bonemass", "Moder", "Yagluth" }; private static readonly Dictionary<long, int> _natural = new Dictionary<long, int>(); internal static int CapFor(Player player) { if ((Object)(object)player == (Object)null) { return Plugin.CapNone.Value; } if (Plugin.QueenRemovesCap.Value && (((Humanoid)player).HaveUniqueKey("defeated_queen") || ((Humanoid)player).HaveUniqueKey("defeated_fader"))) { return int.MaxValue; } int value = Plugin.CapNone.Value; if (((Humanoid)player).HaveUniqueKey(BossOrder[0])) { value = Plugin.CapEikthyr.Value; } if (((Humanoid)player).HaveUniqueKey(BossOrder[1])) { value = Plugin.CapElder.Value; } if (((Humanoid)player).HaveUniqueKey(BossOrder[2])) { value = Plugin.CapBonemass.Value; } if (((Humanoid)player).HaveUniqueKey(BossOrder[3])) { value = Plugin.CapModer.Value; } if (((Humanoid)player).HaveUniqueKey(BossOrder[4])) { value = Plugin.CapYagluth.Value; } return value; } internal static int NaturalFor(Player player) { if ((Object)(object)player == (Object)null) { return 0; } long playerID = player.GetPlayerID(); if (!_natural.TryGetValue(playerID, out var value)) { return 0; } return value; } internal static void RememberNatural(Player player, int points) { if (!((Object)(object)player == (Object)null)) { _natural[player.GetPlayerID()] = points; } } internal static string Hint(Player player) { int num = CapFor(player); if (num == int.MaxValue) { return null; } int num2 = 0; if ((Object)(object)player != (Object)null && ((Humanoid)player).HaveUniqueKey(BossOrder[0])) { num2 = 1; } if ((Object)(object)player != (Object)null && ((Humanoid)player).HaveUniqueKey(BossOrder[1])) { num2 = 2; } if ((Object)(object)player != (Object)null && ((Humanoid)player).HaveUniqueKey(BossOrder[2])) { num2 = 3; } if ((Object)(object)player != (Object)null && ((Humanoid)player).HaveUniqueKey(BossOrder[3])) { num2 = 4; } if ((Object)(object)player != (Object)null && ((Humanoid)player).HaveUniqueKey(BossOrder[4])) { num2 = 5; } string text = ((NaturalFor(player) >= num) ? "Talent cap reached: " : "Talent cap: "); if (num2 >= 5) { if (!Plugin.QueenRemovesCap.Value) { return text + num + " points apply."; } return text + num + " points. Kill the Queen or Fader to remove the cap."; } int num3 = num2 switch { 3 => Plugin.CapModer.Value, 2 => Plugin.CapBonemass.Value, 1 => Plugin.CapElder.Value, 0 => Plugin.CapEikthyr.Value, _ => Plugin.CapYagluth.Value, }; return text + num + " points. Kill " + NextBoss[num2] + " to raise it to " + num3 + "."; } internal static Summary Describe(Player player) { string text = Hint(player); return new Summary { Line = (text ?? "Talent Pace: no cap.") }; } internal static void MarkDirty(Player player) { if (EnsureReflection() && _runtimes.GetValue(null) is IDictionary dictionary) { long playerID = player.GetPlayerID(); if (dictionary.Contains(playerID)) { object obj = dictionary[playerID]; _isDirty.SetValue(obj, true, null); _getRuntime.Invoke(null, new object[1] { player }); } } } internal static void TrimCache(object runtime) { if (runtime == null || !EnsureReflection()) { return; } object? value = runtime.GetType().GetProperty("Player").GetValue(runtime, null); Player val = (Player)((value is Player) ? value : null); if ((Object)(object)val == (Object)null) { return; } int num = CapFor(val); if (num == int.MaxValue) { return; } object value2 = runtime.GetType().GetProperty("State").GetValue(runtime, null); if (!(value2.GetType().GetProperty("Ranks").GetValue(value2, null) is IDictionary { Count: not 0 } dictionary)) { return; } List<Node> list = new List<Node>(dictionary.Count); foreach (DictionaryEntry item2 in dictionary) { string text = item2.Key as string; int num2 = ((item2.Value is int num3) ? num3 : 0); if (!string.IsNullOrEmpty(text) && num2 > 0) { object[] array = new object[2] { text, null }; if ((bool)_tryGet.Invoke(null, array) && array[1] != null) { object obj = array[1]; Type type = obj.GetType(); int ring = (int)type.GetProperty("Ring").GetValue(obj, null); int tier = (int)type.GetProperty("Tier").GetValue(obj, null); list.Add(new Node(text, ring, Cost(tier), num2)); } } } list.Sort(delegate(Node a, Node b) { int num8 = a.Ring.CompareTo(b.Ring); return (num8 == 0) ? string.CompareOrdinal(a.Id, b.Id) : num8; }); Dictionary<string, int> kept = new Dictionary<string, int>(list.Count); int num4 = num; int num5 = 0; bool flag = false; for (int num6 = 0; num6 < list.Count; num6++) { Node node = list[num6]; int val2 = ((node.Cost <= 0) ? node.Rank : (num4 / node.Cost)); int num7 = Math.Min(node.Rank, Math.Max(0, val2)); kept[node.Id] = num7; num4 -= num7 * node.Cost; num5 += num7 * node.Cost; if (num7 != node.Rank) { flag = true; } } if (!flag) { return; } List<string> list2 = new List<string>(dictionary.Count); foreach (DictionaryEntry item3 in dictionary) { if (item3.Key is string item) { list2.Add(item); } } int value4; Func<string, int> func = (string id) => kept.TryGetValue(id, out value4) ? value4 : 0; object value3 = _buildCache.Invoke(null, new object[2] { list2, func }); runtime.GetType().GetProperty("Cache").SetValue(runtime, value3, null); _sync.Invoke(null, new object[1] { runtime }); Plugin.Log(val.GetPlayerName() + " talent cap " + num + ", " + num5 + " points active."); } private static int Cost(int tier) { switch (tier) { case 0: return 1; case 2: return 2; default: _ = 1; return 3; } } private static bool EnsureReflection() { if (_reflected) { return !_reflectFailed; } _reflected = true; Type type = AccessTools.TypeByName("Core.TalentRegistry"); Type type2 = AccessTools.TypeByName("Core.TalentDefinition"); Type type3 = AccessTools.TypeByName("Core.TalentRebuildService"); Type type4 = AccessTools.TypeByName("Core.TalentRuntime"); Type type5 = AccessTools.TypeByName("Core.TalentRuntimeService"); Type type6 = AccessTools.TypeByName("Core.TalentPassivesService"); _tryGet = AccessTools.DeclaredMethod(type, "TryGet", new Type[2] { typeof(string), type2.MakeByRefType() }, (Type[])null); _buildCache = AccessTools.DeclaredMethod(type3, "BuildCache", (Type[])null, (Type[])null); _sync = AccessTools.DeclaredMethod(type6, "SyncToPlayer", new Type[1] { type4 }, (Type[])null); _getRuntime = AccessTools.DeclaredMethod(type5, "Get", new Type[1] { typeof(Player) }, (Type[])null); _runtimes = AccessTools.DeclaredField(type5, "_runtimes"); _isDirty = AccessTools.DeclaredProperty(type4, "IsDirty"); _reflectFailed = _tryGet == null || _buildCache == null || _sync == null || _getRuntime == null || _runtimes == null || _isDirty == null; if (_reflectFailed) { Plugin.Log("Talent Pace could not bind Talent Tree. Caps are off."); } return !_reflectFailed; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }