using System; 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.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Sparroh")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MoreSkillTrees")] [assembly: AssemblyTitle("MoreSkillTrees")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace MoreSkillTrees { [HarmonyPatch(typeof(GearData), "AddXP")] internal static class GearDataAddXPPatch { private static void Prefix(GearData __instance, out int __state) { __state = __instance.Level; } private static void Postfix(GearData __instance, int __state) { try { int level = __instance.Level; if (level <= __state) { return; } IUpgradable gear = __instance.Gear; if (gear == null || gear is Character || (Object)(object)gear.Info == (Object)null || !gear.Info.CanGainXP) { return; } int num = level - __state; int num2 = 0; if (level <= __instance.MaxLevel) { num2 = ((level > 10) ? (num * 2) : num); } if (num2 > 0 && PlayerData.Instance != null && !((Object)(object)Global.Instance == (Object)null)) { PlayerData.Instance.AddResource(Global.Instance.SkillPointResource, num2, true); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)($"[MoreSkillTrees] {gear.Info.Name} leveled {__state}->{level}, " + $"granted {num2} skill point(s).")); } } } catch (Exception arg) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)$"[MoreSkillTrees] AddXP postfix failed: {arg}"); } } } } [HarmonyPatch(typeof(GearDetailsWindow))] internal static class GearDetailsWindowPatches { private static readonly FieldInfo SkillTreeButtonField = AccessTools.Field(typeof(GearDetailsWindow), "skillTreeButton"); private static readonly FieldInfo SkillTreePointsTextField = AccessTools.Field(typeof(GearDetailsWindow), "skillTreePointsText"); [HarmonyPostfix] [HarmonyPatch("Setup")] private static void Setup_Postfix(GearDetailsWindow __instance, IUpgradable upgradable) { //IL_00bc: 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_00c5: Unknown result type (might be due to invalid IL or missing references) try { if (upgradable is Character) { return; } object? obj = SkillTreeButtonField?.GetValue(__instance); Component val = (Component)((obj is Component) ? obj : null); object? obj2 = SkillTreePointsTextField?.GetValue(__instance); Component val2 = (Component)((obj2 is Component) ? obj2 : null); bool flag = MoreSkillTreeData.HasTree(upgradable); if ((Object)(object)val != (Object)null) { val.gameObject.SetActive(flag); } if (!flag) { if ((Object)(object)val2 != (Object)null) { val2.gameObject.SetActive(false); } return; } int resource = PlayerData.Instance.GetResource(Global.Instance.SkillPointResource); bool flag2 = resource > 0 && HasAnyUnclaimedNode(upgradable); if ((Object)(object)val2 != (Object)null) { TextMeshProUGUI component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { if (flag2) { Color uIColor = Global.UIColor; ((Graphic)component).color = uIColor; ((TMP_Text)component).text = ((resource == 1) ? string.Format("{0} {1}", resource, TextBlocks.GetString("skillpoint", 0)) : string.Format("{0} {1}", resource, TextBlocks.GetString("skillpoints"))); ((Component)component).gameObject.SetActive(true); } else { ((Component)component).gameObject.SetActive(false); } } } if (!((Object)(object)val != (Object)null)) { return; } Graphic[] componentsInChildren = val.GetComponentsInChildren(true); foreach (Graphic val3 in componentsInChildren) { if (!((Object)(object)val3 == (Object)null)) { if (flag2 && (Object)(object)Global.Instance.UIShimmerClipMat != (Object)null) { val3.material = Global.Instance.UIShimmerClipMat; } else { val3.material = null; } break; } } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[MoreSkillTrees] GearDetails Setup postfix failed: {arg}"); } } } [HarmonyPostfix] [HarmonyPatch("ToggleSkillAndCraftingUI")] private static void ToggleSkillAndCraftingUI_Postfix(GearDetailsWindow __instance, bool enable) { try { IUpgradable upgradablePrefab = __instance.UpgradablePrefab; if (!(upgradablePrefab is Character) && upgradablePrefab != null) { object? obj = SkillTreeButtonField?.GetValue(__instance); Component val = (Component)((obj is Component) ? obj : null); object? obj2 = SkillTreePointsTextField?.GetValue(__instance); Component val2 = (Component)((obj2 is Component) ? obj2 : null); bool flag = enable && MoreSkillTreeData.HasTree(upgradablePrefab); if ((Object)(object)val != (Object)null) { val.gameObject.SetActive(flag); } if ((Object)(object)val2 != (Object)null) { int resource = PlayerData.Instance.GetResource(Global.Instance.SkillPointResource); bool active = flag && resource > 0 && HasAnyUnclaimedNode(upgradablePrefab); val2.gameObject.SetActive(active); } } } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[MoreSkillTrees] ToggleSkillAndCraftingUI postfix failed: {arg}"); } } } [HarmonyPrefix] [HarmonyPatch("ShowSkillTree")] private static bool ShowSkillTree_Prefix(GearDetailsWindow __instance) { try { IUpgradable upgradablePrefab = __instance.UpgradablePrefab; if (upgradablePrefab is Character) { return true; } if (!MoreSkillTreeData.HasTree(upgradablePrefab)) { return false; } MoreSkillTreeUI.Open(__instance, upgradablePrefab); return false; } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"[MoreSkillTrees] ShowSkillTree prefix failed: {arg}"); } return true; } } private static bool HasAnyUnclaimedNode(IUpgradable gear) { MoreSkillTreeData.Tree tree = MoreSkillTreeData.FindTreeForGear(gear); if (tree == null) { return false; } List list = MoreSkillTreeService.ResolveTree(gear, tree); for (int i = 0; i < list.Count; i++) { if (list[i].State == NodeState.Available || list[i].State == NodeState.CannotAfford) { return true; } } return false; } } public static class MoreSkillTreeData { public sealed class Branch { public readonly int Layer; public readonly string[] NodeNames; public Branch(int layer, params string[] nodeNames) { Layer = layer; NodeNames = nodeNames ?? Array.Empty(); } } public sealed class Tree { public readonly Branch[] Branches; public readonly string[] WeaponApiNames; public readonly string WeaponDisplayName; public Tree(string weaponDisplayName, string[] weaponApiNames, params Branch[] branches) { WeaponDisplayName = weaponDisplayName; WeaponApiNames = weaponApiNames ?? Array.Empty(); Branches = branches ?? Array.Empty(); } } private static Dictionary _byDisplayName; public static int MinSpentForLayer(int layer) { if (layer <= 0) { return 0; } return 2 + (layer - 1) * 5; } public static bool TryGetTree(string weaponDisplayName, out Tree tree) { EnsureIndex(); return _byDisplayName.TryGetValue(weaponDisplayName, out tree); } public static Tree FindTreeForGear(IUpgradable gear) { if ((Object)(object)((gear != null) ? gear.Info : null) == (Object)null) { return null; } EnsureIndex(); string name = gear.Info.Name; if (!string.IsNullOrEmpty(name) && _byDisplayName.TryGetValue(name, out var value)) { return value; } string aPIName = gear.Info.APIName; if (!string.IsNullOrEmpty(aPIName)) { if (_byDisplayName.TryGetValue(aPIName, out var value2)) { return value2; } string text = NameNormalize.Normalize(aPIName, stripRichTextTags: false); Tree[] allTrees = SkillTreeCatalog.AllTrees; foreach (Tree tree in allTrees) { if (NameNormalize.Normalize(tree.WeaponDisplayName, stripRichTextTags: false) == text) { return tree; } string[] weaponApiNames = tree.WeaponApiNames; foreach (string text2 in weaponApiNames) { if (string.Equals(text2, aPIName, StringComparison.OrdinalIgnoreCase)) { return tree; } if (NameNormalize.Normalize(text2, stripRichTextTags: false) == text) { return tree; } } } } string name2 = ((object)gear).GetType().Name; if (!string.IsNullOrEmpty(name2)) { string text3 = NameNormalize.Normalize(name2, stripRichTextTags: false); Tree[] allTrees = SkillTreeCatalog.AllTrees; foreach (Tree tree2 in allTrees) { if (NameNormalize.Normalize(tree2.WeaponDisplayName, stripRichTextTags: false) == text3) { return tree2; } string[] weaponApiNames = tree2.WeaponApiNames; for (int j = 0; j < weaponApiNames.Length; j++) { if (NameNormalize.Normalize(weaponApiNames[j], stripRichTextTags: false) == text3) { return tree2; } } } } return null; } public static bool HasTree(IUpgradable gear) { return FindTreeForGear(gear) != null; } private static void EnsureIndex() { if (_byDisplayName != null) { return; } _byDisplayName = new Dictionary(StringComparer.OrdinalIgnoreCase); Tree[] allTrees = SkillTreeCatalog.AllTrees; foreach (Tree tree in allTrees) { _byDisplayName[tree.WeaponDisplayName] = tree; string[] weaponApiNames = tree.WeaponApiNames; foreach (string text in weaponApiNames) { if (!string.IsNullOrEmpty(text) && !_byDisplayName.ContainsKey(text)) { _byDisplayName[text] = tree; } } } if (_byDisplayName.TryGetValue("Shock Grenade", out var value)) { _byDisplayName["Voltaic Grenade"] = value; } } } public static class MoreSkillTreeService { private static readonly Dictionary> UpgradeCache = new Dictionary>(); private static readonly HashSet LoggedMissing = new HashSet(StringComparer.OrdinalIgnoreCase); public static ManualLogSource Log { get; set; } public static bool IsClaimed(IUpgradable gear, Upgrade upgrade) { if (gear == null || (Object)(object)upgrade == (Object)null) { return false; } UpgradeInfo upgradeInfo = PlayerData.GetUpgradeInfo(gear, upgrade); if (upgradeInfo != null) { return upgradeInfo.TotalInstancesCollected > 0; } return false; } public static int GetMinSpentForLayer(int layer) { return MoreSkillTreeData.MinSpentForLayer(layer); } public static bool IsLayerUnlocked(int layer) { if (PlayerData.Instance == null) { return false; } return PlayerData.Instance.TotalSkillPointsSpent >= GetMinSpentForLayer(layer); } public static NodeState GetNodeState(IUpgradable gear, Upgrade upgrade, int layer) { if ((Object)(object)upgrade == (Object)null) { return NodeState.Unresolved; } if (IsClaimed(gear, upgrade)) { return NodeState.Claimed; } if (!IsLayerUnlocked(layer)) { return NodeState.LayerLocked; } if (PlayerData.Instance.GetResource(Global.Instance.SkillPointResource) <= 0) { return NodeState.CannotAfford; } return NodeState.Available; } public static bool TryUnlock(IUpgradable gear, Upgrade upgrade, int layer, out string message) { message = null; if (gear == null || (Object)(object)upgrade == (Object)null) { message = "Invalid upgrade."; return false; } if (IsClaimed(gear, upgrade)) { message = "Already owned."; return true; } if (!IsLayerUnlocked(layer)) { int num = GetMinSpentForLayer(layer) - PlayerData.Instance.TotalSkillPointsSpent; message = $"Spend {num} more skill point(s) first."; return false; } if (!PlayerData.Instance.TryRemoveResources(Global.Instance.SkillPointUnlockCost)) { message = "Not enough skill points."; return false; } UpgradeInstance val = PlayerData.CollectInstance(gear, upgrade, (UnlockFlags)0); if (val != null) { val.Seed = upgrade.NumberID; val.Unlock(false); } PlayerData instance = PlayerData.Instance; int totalSkillPointsSpent = instance.TotalSkillPointsSpent; instance.TotalSkillPointsSpent = totalSkillPointsSpent + 1; message = "Unlocked."; ManualLogSource log = Log; if (log != null) { string[] obj = new string[6] { "[MoreSkillTrees] Unlocked '", upgrade.Name, "' on '", null, null, null }; GearInfo info = gear.Info; obj[3] = ((info != null) ? info.Name : null); obj[4] = "' "; obj[5] = $"(spent total now {PlayerData.Instance.TotalSkillPointsSpent})."; log.LogInfo((object)string.Concat(obj)); } return true; } public static List ResolveTree(IUpgradable gear, MoreSkillTreeData.Tree tree) { List list = new List(); if (gear == null || tree == null) { return list; } Dictionary upgradeMap = GetUpgradeMap(gear); MoreSkillTreeData.Branch[] branches = tree.Branches; foreach (MoreSkillTreeData.Branch branch in branches) { string[] nodeNames = branch.NodeNames; foreach (string text in nodeNames) { upgradeMap.TryGetValue(NameNormalize.Normalize(text), out var value); if ((Object)(object)value == (Object)null) { value = FuzzyFind(upgradeMap, text); } if ((Object)(object)value == (Object)null && LoggedMissing.Add(tree.WeaponDisplayName + "::" + text)) { ManualLogSource log = Log; if (log != null) { string[] obj = new string[7] { "[MoreSkillTrees] Could not resolve '", text, "' on '", null, null, null, null }; GearInfo info = gear.Info; obj[3] = ((info != null) ? info.Name : null); obj[4] = "' (API: "; GearInfo info2 = gear.Info; obj[5] = ((info2 != null) ? info2.APIName : null); obj[6] = ")."; log.LogWarning((object)string.Concat(obj)); } } ResolvedNode item = new ResolvedNode { DisplayName = text, Layer = branch.Layer, Upgrade = value, MinSpentRequired = GetMinSpentForLayer(branch.Layer), State = (((Object)(object)value == (Object)null) ? NodeState.Unresolved : GetNodeState(gear, value, branch.Layer)) }; list.Add(item); } } return list; } public static void RefreshStates(IUpgradable gear, List nodes) { if (nodes == null) { return; } foreach (ResolvedNode node in nodes) { if ((Object)(object)node.Upgrade == (Object)null) { node.State = NodeState.Unresolved; } else { node.State = GetNodeState(gear, node.Upgrade, node.Layer); } } } public static Upgrade FindUpgrade(IUpgradable gear, string displayName) { if (gear == null || string.IsNullOrEmpty(displayName)) { return null; } Dictionary upgradeMap = GetUpgradeMap(gear); if (upgradeMap.TryGetValue(NameNormalize.Normalize(displayName), out var value)) { return value; } return FuzzyFind(upgradeMap, displayName); } public static void ClearCache() { UpgradeCache.Clear(); } private static Dictionary GetUpgradeMap(IUpgradable gear) { int iD = gear.Info.ID; if (UpgradeCache.TryGetValue(iD, out var value)) { return value; } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); List upgrades = gear.Info.Upgrades; if (upgrades != null) { for (int i = 0; i < upgrades.Count; i++) { Upgrade val = upgrades[i]; if (!((Object)(object)val == (Object)null) && !val.IsSkin()) { AddKey(dictionary, val.Name, val); AddKey(dictionary, val.APIName, val); AddKey(dictionary, NameNormalize.Normalize(val.Name), val); AddKey(dictionary, NameNormalize.Normalize(val.APIName), val); } } } UpgradeCache[iD] = dictionary; return dictionary; } private static void AddKey(Dictionary map, string key, Upgrade u) { if (!string.IsNullOrEmpty(key)) { if (!map.ContainsKey(key)) { map[key] = u; } string key2 = NameNormalize.Normalize(key); if (!map.ContainsKey(key2)) { map[key2] = u; } } } private static Upgrade FuzzyFind(Dictionary map, string displayName) { string text = NameNormalize.Normalize(displayName); Upgrade result = null; foreach (KeyValuePair item in map) { string text2 = NameNormalize.Normalize(item.Key); if (text2 == text) { return item.Value; } if (text2.Contains(text) || text.Contains(text2)) { result = item.Value; } } return result; } } public class MoreSkillTreeUI : MonoBehaviour { private static MoreSkillTreeUI _instance; private readonly List _buttons = new List(); private Transform _content; private IUpgradable _gear; private List _nodes; private GearDetailsWindow _parentWindow; private TextMeshProUGUI _pointsText; private RectTransform _root; private TextMeshProUGUI _statusText; private TextMeshProUGUI _titleText; private MoreSkillTreeData.Tree _tree; private void Update() { if (((Component)this).gameObject.activeSelf) { Keyboard current = Keyboard.current; if (current != null && ((ButtonControl)current.escapeKey).wasPressedThisFrame) { Hide(); } } } public static void Open(GearDetailsWindow parent, IUpgradable gear) { //IL_0044: 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_0059: Expected O, but got Unknown MoreSkillTreeData.Tree tree = MoreSkillTreeData.FindTreeForGear(gear); if (tree == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[MoreSkillTrees] Open called with no tree."); } return; } if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("MoreSkillTreeUI", new Type[1] { typeof(RectTransform) }); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)val); _instance.BuildShell(); } _instance._parentWindow = parent; _instance._gear = gear; _instance._tree = tree; _instance._nodes = MoreSkillTreeService.ResolveTree(gear, tree); _instance.Show(); } public static void CloseIfOpen() { if ((Object)(object)_instance != (Object)null && ((Component)_instance).gameObject.activeSelf) { _instance.Hide(); } } private void BuildShell() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_045f: Expected O, but got Unknown //IL_04a0: Unknown result type (might be due to invalid IL or missing references) //IL_04b5: Unknown result type (might be due to invalid IL or missing references) //IL_04ca: Unknown result type (might be due to invalid IL or missing references) //IL_04df: Unknown result type (might be due to invalid IL or missing references) //IL_053d: Unknown result type (might be due to invalid IL or missing references) //IL_057b: Unknown result type (might be due to invalid IL or missing references) //IL_0591: Unknown result type (might be due to invalid IL or missing references) //IL_05a7: Unknown result type (might be due to invalid IL or missing references) //IL_05b3: Unknown result type (might be due to invalid IL or missing references) //IL_05c9: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown GameObject gameObject = ((Component)this).gameObject; Canvas obj = gameObject.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 5000; gameObject.AddComponent().uiScaleMode = (ScaleMode)1; gameObject.GetComponent().referenceResolution = new Vector2(1920f, 1080f); gameObject.AddComponent(); if ((Object)(object)EventSystem.current == (Object)null) { Object.DontDestroyOnLoad((Object)new GameObject("EventSystem", new Type[2] { typeof(EventSystem), typeof(StandaloneInputModule) })); } _root = gameObject.GetComponent(); _root.anchorMin = Vector2.zero; _root.anchorMax = Vector2.one; _root.offsetMin = Vector2.zero; _root.offsetMax = Vector2.zero; RectTransform obj2 = SkillTreeUIHelpers.CreateUIObject("Dim", (Transform)(object)_root); ((Graphic)((Component)obj2).gameObject.AddComponent()).color = new Color(0f, 0f, 0f, 0.72f); SkillTreeUIHelpers.Stretch(obj2); Button obj3 = ((Component)obj2).gameObject.AddComponent