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 Workstation Search v0.6.13
BepInEx/plugins/WorkstationSearch/WorkstationSearch.dll
Decompiled a day 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.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.TextCore; 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: AssemblyCompany("WorkstationSearch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.6.13.0")] [assembly: AssemblyInformationalVersion("0.6.13+4ffcaab1e4f98228d20a3ecb8f71f43710e21361")] [assembly: AssemblyProduct("WorkstationSearch")] [assembly: AssemblyTitle("WorkstationSearch")] [assembly: AssemblyVersion("0.6.13.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 WorkstationSearch { public sealed class CategoryEditor : MonoBehaviour { [CompilerGenerated] private static class <>O { public static Func<ItemCategory, string> <0>__Friendly; public static UnityAction <1>__Close; } private static CategoryEditor current; private static readonly FieldInfo CraftTimer = AccessTools.Field(typeof(InventoryGui), "m_craftTimer"); private InventoryGui gui; private SpellingEntry item; private CategoryOverride draft; private TMP_InputField terms; private TMP_Text status; private readonly Dictionary<ItemCategory, Toggle> toggles = new Dictionary<ItemCategory, Toggle>(); private Sprite checkSprite; private Texture2D checkTexture; private readonly Dictionary<ItemCategory, TMP_Text> labels = new Dictionary<ItemCategory, TMP_Text>(); internal static bool IsOpen => Object.op_Implicit((Object)(object)current); internal static void Open(InventoryGui owner, SpellingEntry entry) { //IL_0093: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)owner) || entry == null || string.IsNullOrEmpty(entry.Prefab) || (float)CraftTimer.GetValue(owner) >= 0f) { return; } Canvas componentInParent = ((Component)owner.m_recipeListRoot).GetComponentInParent<Canvas>(); if (Object.op_Implicit((Object)(object)componentInParent)) { Close(); if (Object.op_Implicit((Object)(object)EventSystem.current)) { EventSystem.current.SetSelectedGameObject((GameObject)null); } GameObject val = new GameObject("WorkstationSearchCategories", new Type[3] { typeof(RectTransform), typeof(Image), typeof(CategoryEditor) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(((Component)componentInParent).transform, false); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; Vector2 offsetMin = (val2.offsetMax = Vector2.zero); val2.offsetMin = offsetMin; ((Graphic)val.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.8f); current = val.GetComponent<CategoryEditor>(); current.gui = owner; current.item = entry; current.draft = Plugin.Overrides.Get(entry.Prefab); current.Build(componentInParent); SearchPanel.SuppressInputFrame(); } } internal static void Close() { if (Object.op_Implicit((Object)(object)current)) { CategoryEditor categoryEditor = current; current = null; ((Component)categoryEditor).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)categoryEditor).gameObject); if (Object.op_Implicit((Object)(object)EventSystem.current)) { EventSystem.current.SetSelectedGameObject((GameObject)null); } SearchPanel.SuppressInputFrame(); } } private void Build(Canvas canvas) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_00bb: 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_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_0440: Unknown result type (might be due to invalid IL or missing references) //IL_04da: Unknown result type (might be due to invalid IL or missing references) //IL_05e9: Unknown result type (might be due to invalid IL or missing references) //IL_05f3: Expected O, but got Unknown //IL_0652: Unknown result type (might be due to invalid IL or missing references) //IL_065c: Expected O, but got Unknown //IL_061f: Unknown result type (might be due to invalid IL or missing references) //IL_0624: Unknown result type (might be due to invalid IL or missing references) //IL_062a: Expected O, but got Unknown RectTransform val = Rect("Editor", ((Component)this).transform, 0f, 0f, 760f, 670f); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); val.pivot = val2; Vector2 anchorMin = (val.anchorMax = val2); val.anchorMin = anchorMin; val.anchoredPosition = Vector2.zero; Rect rect = ((RectTransform)((Component)canvas).transform).rect; Vector2 size = ((Rect)(ref rect)).size; float num = Mathf.Min(1f, Mathf.Min((size.x - 24f) / 760f, (size.y - 24f) / 670f)); ((Transform)val).localScale = Vector3.one * Mathf.Max(0.2f, num); ((Graphic)((Component)val).gameObject.AddComponent<Image>()).color = new Color(0.12f, 0.105f, 0.085f, 1f); Text((Transform)(object)val, "Search categories", 20f, 14f, 720f, 32f, 24f); Text((Transform)(object)val, item.Name, 20f, 49f, 720f, 30f, 20f).overflowMode = (TextOverflowModes)1; Text((Transform)(object)val, "Check categories to enable them. Categories marked auto were detected.\nChanges apply to this item type in Craft and Upgrade.", 20f, 86f, 720f, 48f, 16f); RectTransform val4 = Rect("Categories", (Transform)(object)val, 20f, 144f, 704f, 292f); ((Graphic)((Component)val4).gameObject.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.2f); ((Component)val4).gameObject.AddComponent<RectMask2D>(); RectTransform val5 = Rect("Content", (Transform)(object)val4, 0f, 0f, 704f, 0f); ScrollRect val6 = ((Component)val4).gameObject.AddComponent<ScrollRect>(); val6.viewport = val4; val6.content = val5; val6.horizontal = false; val6.movementType = (MovementType)2; val6.scrollSensitivity = 350f; ItemCategory[] array = Enum.GetValues(typeof(ItemCategory)).Cast<ItemCategory>().OrderBy(Friendly) .ToArray(); val5.sizeDelta = new Vector2(704f, (float)((array.Length + 2) / 3 * 42)); for (int i = 0; i < array.Length; i++) { ItemCategory category = array[i]; AddCategoryToggle((Transform)(object)val5, category, i % 3 * 235, i / 3 * 42); } RectTransform val7 = Rect("Scrollbar", (Transform)(object)val, 730f, 144f, 10f, 292f); ((Graphic)((Component)val7).gameObject.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.5f); RectTransform val8 = Rect("Sliding Area", (Transform)(object)val7, 0f, 0f, 10f, 292f); SearchPanel.Stretch(val8); RectTransform val9 = Rect("Handle", (Transform)(object)val8, 0f, 0f, 0f, 0f); SearchPanel.Stretch(val9); Image val10 = ((Component)val9).gameObject.AddComponent<Image>(); ((Graphic)val10).color = new Color(0.8f, 0.58f, 0.25f); Scrollbar val11 = ((Component)val7).gameObject.AddComponent<Scrollbar>(); val11.handleRect = val9; ((Selectable)val11).targetGraphic = (Graphic)(object)val10; val11.direction = (Direction)2; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)val11).navigation = navigation; val6.verticalScrollbar = val11; val6.verticalNormalizedPosition = 1f; Text((Transform)(object)val, "Custom search words", 20f, 449f, 720f, 24f, 18f); RectTransform val12 = Rect("CustomWords", (Transform)(object)val, 20f, 480f, 720f, 36f); Image val13 = ((Component)val12).gameObject.AddComponent<Image>(); ((Graphic)val13).color = new Color(0.25f, 0.23f, 0.2f); RectTransform val14 = Rect("Text Area", (Transform)(object)val12, 10f, 0f, 700f, 36f); ((Component)val14).gameObject.AddComponent<RectMask2D>(); TMP_Text textComponent = Text((Transform)(object)val14, "", 0f, 0f, 700f, 36f, 18f); TMP_Text val15 = Text((Transform)(object)val14, "Example: dragon, winter, sailing", 0f, 0f, 700f, 36f, 18f); ((Graphic)val15).color = new Color(0.65f, 0.62f, 0.57f); terms = ((Component)val12).gameObject.AddComponent<TMP_InputField>(); terms.textViewport = val14; terms.textComponent = textComponent; terms.placeholder = (Graphic)(object)val15; ((Selectable)terms).targetGraphic = (Graphic)(object)val13; terms.lineType = (LineType)0; string text = string.Join(", ", draft.Terms); terms.characterLimit = Mathf.Max(512, text.Length); terms.text = text; Text((Transform)(object)val, "Separate words with spaces or commas. Names remain searchable when categories are disabled.\nSettings stay saved even if the item's mod is removed.", 20f, 525f, 720f, 42f, 15f); status = Text((Transform)(object)val, "", 20f, 574f, 720f, 30f, 15f); Button((Transform)(object)val, "Reset to automatic", 20f, 620f, 240f, 34f, (UnityAction)delegate { draft = new CategoryOverride(); terms.text = ""; status.text = "Automatic settings restored. Save to apply."; RefreshLabels(); }); object obj = <>O.<1>__Close; if (obj == null) { UnityAction val16 = Close; <>O.<1>__Close = val16; obj = (object)val16; } Button((Transform)(object)val, "Cancel", 490f, 620f, 115f, 34f, (UnityAction)obj); Button((Transform)(object)val, "Save", 625f, 620f, 115f, 34f, new UnityAction(Save)); RefreshLabels(); } private void Save() { if (Object.op_Implicit((Object)(object)gui) && !((float)CraftTimer.GetValue(gui) >= 0f)) { draft.Terms = CategoryOverride.ParseTerms(terms.text); if (!Plugin.SaveOverride(item.Prefab, draft)) { status.text = "Could not save. See the BepInEx log; existing settings were kept."; return; } FavoriteRows.RefreshOverrides(item.Prefab); Close(); } } private void RefreshLabels() { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) HashSet<ItemCategory> hashSet = new HashSet<ItemCategory>(draft.Apply(item.Categories)); foreach (KeyValuePair<ItemCategory, TMP_Text> label in labels) { bool flag = hashSet.Contains(label.Key); toggles[label.Key].SetIsOnWithoutNotify(flag); label.Value.text = Friendly(label.Key) + (item.Categories.Contains(label.Key) ? " (auto)" : ""); ((Graphic)label.Value).color = (flag ? new Color(1f, 0.8f, 0.35f) : new Color(0.68f, 0.66f, 0.62f)); } } private void AddCategoryToggle(Transform parent, ItemCategory category, float x, float y) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) RectTransform val = Rect(category.ToString(), parent, x, y, 228f, 38f); ((Graphic)((Component)val).gameObject.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.18f); Toggle val2 = ((Component)val).gameObject.AddComponent<Toggle>(); RectTransform val3 = Rect("Checkbox", (Transform)(object)val, 5f, 8f, 22f, 22f); ((Graphic)((Component)val3).gameObject.AddComponent<Image>()).color = new Color(0.7f, 0.56f, 0.32f); Image val4 = ((Component)Rect("Background", (Transform)(object)val3, 2f, 2f, 18f, 18f)).gameObject.AddComponent<Image>(); ((Graphic)val4).color = new Color(0.18f, 0.16f, 0.12f); Image val5 = ((Component)Rect("Checkmark", (Transform)(object)val3, 1f, 1f, 20f, 20f)).gameObject.AddComponent<Image>(); if (!Object.op_Implicit((Object)(object)checkSprite)) { CreateCheckmark(); } val5.sprite = checkSprite; ((Graphic)val5).color = new Color(1f, 0.8f, 0.35f); ((Graphic)val5).raycastTarget = false; SearchPanel.CopySelectable((Selectable)(object)gui.m_craftCancelButton, (Selectable)(object)val2); ((Selectable)val2).targetGraphic = (Graphic)(object)val4; val2.graphic = (Graphic)(object)val5; val2.toggleTransition = (ToggleTransition)0; labels.Add(category, Text((Transform)(object)val, "", 34f, 0f, 190f, 38f, 14f)); toggles.Add(category, val2); ((UnityEvent<bool>)(object)val2.onValueChanged).AddListener((UnityAction<bool>)delegate(bool enabled) { if (draft.Apply(item.Categories).Contains(category) != enabled) { draft.Toggle(category, item.Categories); } RefreshLabels(); }); } private void CreateCheckmark() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0112: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) checkTexture = new Texture2D(32, 32, (TextureFormat)4, false); ((Object)checkTexture).name = "WorkstationSearchCheckmark"; ((Texture)checkTexture).wrapMode = (TextureWrapMode)1; ((Texture)checkTexture).filterMode = (FilterMode)1; Vector2 a = default(Vector2); ((Vector2)(ref a))..ctor(5f, 16f); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(13f, 8f); Vector2 b = default(Vector2); ((Vector2)(ref b))..ctor(27f, 25f); Vector2 point = default(Vector2); for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { ((Vector2)(ref point))..ctor((float)j + 0.5f, (float)i + 0.5f); float num = Mathf.Min(SegmentDistance(point, a, val), SegmentDistance(point, val, b)); checkTexture.SetPixel(j, i, new Color(1f, 1f, 1f, Mathf.Clamp01(2.6f - num))); } } checkTexture.Apply(false, true); checkSprite = Sprite.Create(checkTexture, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 32f); } private static float SegmentDistance(Vector2 point, 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(point - a, val) / ((Vector2)(ref val)).sqrMagnitude); return Vector2.Distance(point, a + val * num); } private static string Friendly(ItemCategory category) { return Regex.Replace(category.ToString(), "([a-z])([A-Z])", "$1 $2"); } private TMP_Text Button(Transform parent, string title, float x, float y, float width, float height, UnityAction action) { RectTransform val = Rect((title.Length == 0) ? "Category" : title, parent, x, y, width, height); Image val2 = ((Component)val).gameObject.AddComponent<Image>(); Button val3 = ((Component)val).gameObject.AddComponent<Button>(); Graphic targetGraphic = ((Selectable)gui.m_craftCancelButton).targetGraphic; SearchPanel.CopyImage((Image)(object)((targetGraphic is Image) ? targetGraphic : null), val2); SearchPanel.CopySelectable((Selectable)(object)gui.m_craftCancelButton, (Selectable)(object)val3); ((Selectable)val3).targetGraphic = (Graphic)(object)val2; ((UnityEvent)val3.onClick).AddListener(action); TMP_Text obj = Text((Transform)(object)val, title, 5f, 0f, width - 10f, height, 16f); obj.alignment = (TextAlignmentOptions)514; return obj; } private TMP_Text Text(Transform parent, string value, float x, float y, float width, float height, float size) { TextMeshProUGUI obj = SearchPanel.Label("Label", parent, gui.m_recipeName, value); Position(((TMP_Text)obj).rectTransform, x, y, width, height); ((TMP_Text)obj).fontSize = size; ((TMP_Text)obj).richText = false; ((TMP_Text)obj).alignment = (TextAlignmentOptions)4097; return (TMP_Text)(object)obj; } private static RectTransform Rect(string name, Transform parent, float x, float y, float width, float height) { //IL_0014: 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_0023: 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) //IL_0037: Expected O, but got Unknown //IL_0038: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent(parent, false); Position(val, x, y, width, height); return val; } private static void Position(RectTransform rect, float x, float y, float width, float height) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0f, 1f); rect.pivot = val; Vector2 anchorMin = (rect.anchorMax = val); rect.anchorMin = anchorMin; rect.anchoredPosition = new Vector2(x, 0f - y); rect.sizeDelta = new Vector2(width, height); } private void Update() { if (!Object.op_Implicit((Object)(object)gui) || !InventoryGui.IsVisible() || (float)CraftTimer.GetValue(gui) >= 0f || Input.GetKeyDown((KeyCode)27)) { Close(); } } private void OnDestroy() { if ((Object)(object)current == (Object)(object)this) { current = null; } if (Object.op_Implicit((Object)(object)checkSprite)) { Object.Destroy((Object)(object)checkSprite); } if (Object.op_Implicit((Object)(object)checkTexture)) { Object.Destroy((Object)(object)checkTexture); } } } internal sealed class CategoryOverride { internal readonly HashSet<string> Added = new HashSet<string>(StringComparer.Ordinal); internal readonly HashSet<string> Disabled = new HashSet<string>(StringComparer.Ordinal); internal string[] Terms = new string[0]; internal bool IsEmpty { get { if (Added.Count == 0 && Disabled.Count == 0) { return Terms.Length == 0; } return false; } } internal CategoryOverride Copy() { CategoryOverride categoryOverride = new CategoryOverride(); categoryOverride.Terms = (string[])Terms.Clone(); categoryOverride.Added.UnionWith(Added); categoryOverride.Disabled.UnionWith(Disabled); return categoryOverride; } internal ItemCategory[] Apply(IEnumerable<ItemCategory> detected) { IEnumerable<ItemCategory> second = from ItemCategory c in Enum.GetValues(typeof(ItemCategory)) where Added.Contains(c.ToString()) select c; return (from c in CategoryHierarchy.Expand(detected.Concat(second)) where !Disabled.Contains(c.ToString()) select c).ToArray(); } internal void Toggle(ItemCategory category, ItemCategory[] detected) { string item = category.ToString(); if (Apply(detected).Contains(category)) { Added.Remove(item); Disabled.Add(item); return; } Disabled.Remove(item); if (!Apply(detected).Contains(category)) { Added.Add(item); } } internal static string[] ParseTerms(string text) { return (text ?? "").Split(new char[5] { ' ', ',', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); } } internal sealed class CategoryOverrides { private readonly Dictionary<string, CategoryOverride> items = new Dictionary<string, CategoryOverride>(StringComparer.Ordinal); internal CategoryOverride Get(string prefab) { if (!items.TryGetValue(prefab, out var value)) { return new CategoryOverride(); } return value.Copy(); } internal void Set(string prefab, CategoryOverride value) { if (string.IsNullOrEmpty(prefab)) { throw new ArgumentException("Missing item prefab ID.", "prefab"); } if (value.IsEmpty) { items.Remove(prefab); } else { items[prefab] = value.Copy(); } } internal SpellingEntry Apply(SpellingEntry entry) { if (entry == null || !items.TryGetValue(entry.Prefab, out var value)) { return entry; } return entry.WithSearchCategories(value.Apply(entry.Categories), value.Terms); } internal string Save() { return "1:" + string.Join(";", from p in items.OrderBy<KeyValuePair<string, CategoryOverride>, string>((KeyValuePair<string, CategoryOverride> p) => p.Key, StringComparer.Ordinal) select Encode(p.Key) + "|" + Join(p.Value.Added.OrderBy<string, string>((string x) => x, StringComparer.Ordinal)) + "|" + Join(p.Value.Disabled.OrderBy<string, string>((string x) => x, StringComparer.Ordinal)) + "|" + Join(p.Value.Terms)); } internal static CategoryOverrides Load(string text) { CategoryOverrides categoryOverrides = new CategoryOverrides(); if (string.IsNullOrEmpty(text)) { return categoryOverrides; } if (!text.StartsWith("1:", StringComparison.Ordinal)) { throw new FormatException("Unsupported category override format."); } string[] array = text.Substring(2).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '|' }); if (array2.Length != 4) { throw new FormatException("Invalid category override record."); } CategoryOverride categoryOverride = new CategoryOverride { Terms = Split(array2[3]) }; categoryOverride.Added.UnionWith(Split(array2[1])); categoryOverride.Disabled.UnionWith(Split(array2[2])); categoryOverrides.Set(Uri.UnescapeDataString(array2[0]), categoryOverride); } return categoryOverrides; } private static string Encode(string value) { return Uri.EscapeDataString(value); } private static string Join(IEnumerable<string> values) { return string.Join(",", values.Select(Encode)); } private static string[] Split(string text) { return text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(Uri.UnescapeDataString).ToArray(); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] internal static class CraftRowReuse { private static InventoryGui owner; private static readonly RebuildPool<Recipe, GameObject> pool = new RebuildPool<Recipe, GameObject>(); internal static bool ReleaseHookReady; internal static bool CreationHookReady; private static readonly Dictionary<UnityEvent, CraftRowIdentity> clicks = new Dictionary<UnityEvent, CraftRowIdentity>(); internal static void Begin(InventoryGui __instance, IEnumerable<GameObject> rows) { Finish(); if (!ReleaseHookReady || !CreationHookReady || !__instance.InCraftTab()) { return; } owner = __instance; foreach (GameObject row in rows) { CraftRowIdentity craftRowIdentity = (Object.op_Implicit((Object)(object)row) ? row.GetComponent<CraftRowIdentity>() : null); if (Object.op_Implicit((Object)(object)craftRowIdentity) && Object.op_Implicit((Object)(object)craftRowIdentity.Recipe) && !((Object)(object)craftRowIdentity.Prefab != (Object)(object)__instance.m_recipeElementPrefab)) { pool.Add(craftRowIdentity.Recipe, row); } } } private static void Finalizer() { Finish(); } internal static void Finish() { foreach (GameObject item in pool.Unused) { if (Object.op_Implicit((Object)(object)item)) { item.SetActive(false); Object.Destroy((Object)(object)item); } } pool.Clear(); clicks.Clear(); owner = null; } private static void ReleaseRow(Object value) { GameObject val = (GameObject)(object)((value is GameObject) ? value : null); if (val == null || !pool.Contains(val)) { Object.Destroy(value); } } internal static GameObject GetRow(GameObject prefab, Transform parent, Recipe recipe) { if (Object.op_Implicit((Object)(object)owner) && (Object)(object)parent == (Object)(object)owner.m_recipeListRoot) { GameObject val; while ((val = pool.Take(recipe)) != null) { if (Object.op_Implicit((Object)(object)val)) { CraftRowIdentity component = val.GetComponent<CraftRowIdentity>(); if (component.Click != null) { ((UnityEvent)val.GetComponent<Button>().onClick).RemoveListener(component.Click); } component.Click = null; clicks[(UnityEvent)(object)val.GetComponent<Button>().onClick] = component; Transform val2 = val.transform.Find("selected"); if (Object.op_Implicit((Object)(object)val2)) { ((Component)val2).gameObject.SetActive(false); } return val; } } } GameObject val3 = Object.Instantiate<GameObject>(prefab, parent); if (Object.op_Implicit((Object)(object)owner) && (Object)(object)parent == (Object)(object)owner.m_recipeListRoot) { CraftRowIdentity craftRowIdentity = val3.AddComponent<CraftRowIdentity>(); craftRowIdentity.Recipe = recipe; craftRowIdentity.Prefab = prefab; clicks[(UnityEvent)(object)val3.GetComponent<Button>().onClick] = craftRowIdentity; } return val3; } internal static void AddClick(UnityEvent clickEvent, UnityAction listener) { clickEvent.AddListener(listener); if (clicks.TryGetValue(clickEvent, out var value)) { value.Click = listener; clicks.Remove(clickEvent); } } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo destroy = AccessTools.Method(typeof(Object), "Destroy", new Type[1] { typeof(Object) }, (Type[])null); List<CodeInstruction> list = instructions.ToList(); ReleaseHookReady = list.Count((CodeInstruction instruction) => CodeInstructionExtensions.Calls(instruction, destroy)) == 1; foreach (CodeInstruction item in list) { if (ReleaseHookReady && CodeInstructionExtensions.Calls(item, destroy)) { item.operand = AccessTools.Method(typeof(CraftRowReuse), "ReleaseRow", (Type[])null, (Type[])null); } yield return item; } } } internal sealed class CraftRowIdentity : MonoBehaviour { internal Recipe Recipe; internal GameObject Prefab; internal UnityAction Click; } [HarmonyPatch(typeof(InventoryGui), "AddRecipeToList")] internal static class CraftRowCreation { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown List<CodeInstruction> list = instructions.ToList(); MethodInfo addListener = AccessTools.Method(typeof(UnityEvent), "AddListener", new Type[1] { typeof(UnityAction) }, (Type[])null); CraftRowReuse.CreationHookReady = list.Count(IsRowCreation) == 1 && list.Count((CodeInstruction instruction) => CodeInstructionExtensions.Calls(instruction, addListener)) == 1; if (!CraftRowReuse.CreationHookReady) { return list; } List<CodeInstruction> list2 = new List<CodeInstruction>(); foreach (CodeInstruction item in list) { if (IsRowCreation(item)) { CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_2, (object)null); val.labels.AddRange(item.labels); item.labels.Clear(); val.blocks.AddRange(item.blocks); item.blocks.Clear(); list2.Add(val); item.operand = AccessTools.Method(typeof(CraftRowReuse), "GetRow", (Type[])null, (Type[])null); } else if (CodeInstructionExtensions.Calls(item, addListener)) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(CraftRowReuse), "AddClick", (Type[])null, (Type[])null); } list2.Add(item); } return list2; } private static bool IsRowCreation(CodeInstruction instruction) { if (instruction.operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(Object) && methodInfo.Name == "Instantiate" && methodInfo.IsGenericMethod && methodInfo.GetGenericArguments()[0] == typeof(GameObject) && methodInfo.GetParameters().Length == 2) { return methodInfo.GetParameters()[1].ParameterType == typeof(Transform); } return false; } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] internal static class FavoriteRows { private static readonly FieldInfo Available = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes"); private static readonly Type Pair = Available.FieldType.GetGenericArguments()[0]; private static readonly PropertyInfo RecipeProperty = Pair.GetProperty("Recipe"); private static readonly PropertyInfo Element = Pair.GetProperty("InterfaceElement"); private static readonly FieldInfo BaseSize = AccessTools.Field(typeof(InventoryGui), "m_recipeListBaseSize"); private static readonly MethodInfo SelectedIndex = AccessTools.Method(typeof(InventoryGui), "GetSelectedRecipeIndex", (Type[])null, (Type[])null); private static readonly MethodInfo SetRecipe = AccessTools.Method(typeof(InventoryGui), "SetRecipe", (Type[])null, (Type[])null); private static readonly FieldInfo Selected = AccessTools.Field(typeof(InventoryGui), "m_selectedRecipe"); private static InventoryGui owner; private static List<object> cachedRows; private static List<object> appliedRows; private static bool preserveIndices; private static readonly RowEntryCache SearchEntries = new RowEntryCache(); private static readonly Dictionary<GameObject, SpellingEntry> RowEntries = new Dictionary<GameObject, SpellingEntry>(); private static SpellingEntry RowEntry(object pair) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown GameObject val = (GameObject)Element.GetValue(pair, null); if (!Object.op_Implicit((Object)(object)val)) { return null; } if (!RowEntries.TryGetValue(val, out var value)) { Transform val2 = val.transform.Find("name"); TMP_Text val3 = (Object.op_Implicit((Object)(object)val2) ? ((Component)val2).GetComponent<TMP_Text>() : null); value = SearchEntries.Get(SearchCatalog.Get((Recipe)RecipeProperty.GetValue(pair, null)), Object.op_Implicit((Object)(object)val3) ? val3.text : null, Plugin.Overrides); RowEntries[val] = value; } return value; } internal static string Key(Recipe recipe) { if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item)) { return null; } return ((Object)recipe.m_item).name; } [HarmonyPriority(800)] private static void Prefix(InventoryGui __instance) { BeforeRebuild(__instance); CraftRowReuse.Begin(__instance, ((IList)Available.GetValue(__instance)).Cast<object>().Select((Func<object, GameObject>)((object pair) => (GameObject)Element.GetValue(pair, null)))); } internal static void BeforeRebuild(InventoryGui __instance) { if ((Object)(object)owner != (Object)(object)__instance || cachedRows == null) { return; } IList list = (IList)Available.GetValue(__instance); if (OwnsCurrentRows(list)) { list.Clear(); foreach (object cachedRow in cachedRows) { if (IsAlive(cachedRow)) { list.Add(cachedRow); } } } else { DiscardDetachedRows(list); } cachedRows = null; appliedRows = null; RowEntries.Clear(); } [HarmonyPriority(0)] private static void Postfix(InventoryGui __instance) { CraftRowReuse.Finish(); Capture(__instance, keepIndices: false); } internal static void Capture(InventoryGui __instance, bool keepIndices) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown owner = __instance; preserveIndices = keepIndices; RowEntries.Clear(); cachedRows = ((IList)Available.GetValue(__instance)).Cast<object>().Where(IsAlive).ToList(); appliedRows = ((IList)Available.GetValue(__instance)).Cast<object>().ToList(); foreach (object cachedRow in cachedRows) { GameObject val = (GameObject)Element.GetValue(cachedRow, null); string text = Key((Recipe)RecipeProperty.GetValue(cachedRow, null)); if (!string.IsNullOrEmpty(text)) { (val.GetComponent<FavoriteRow>() ?? val.AddComponent<FavoriteRow>()).Initialize(__instance, text); } } if ((Object.op_Implicit((Object)(object)Plugin.Panel) && !Plugin.Panel.Query.IsEmpty) || !Plugin.Favorites.IsEmpty) { Apply(__instance, updateSelection: false); } } private static bool IsAlive(object pair) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown return Object.op_Implicit((Object)(GameObject)Element.GetValue(pair, null)); } private static bool OwnsCurrentRows(IList rows) { return RowListPolicy.MatchesSnapshot(rows, appliedRows); } private static void DiscardDetachedRows(IList rows) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown HashSet<GameObject> hashSet = new HashSet<GameObject>(rows.Cast<object>().Select((Func<object, GameObject>)((object pair) => (GameObject)Element.GetValue(pair, null)))); foreach (object cachedRow in cachedRows) { GameObject val = (GameObject)Element.GetValue(cachedRow, null); if (Object.op_Implicit((Object)(object)val) && !hashSet.Contains(val)) { val.SetActive(false); Object.Destroy((Object)(object)val); } } } private static SpellingEntry BaseEntry(object pair) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown GameObject val = (GameObject)Element.GetValue(pair, null); SpellingEntry spellingEntry = SearchCatalog.Get((Recipe)RecipeProperty.GetValue(pair, null)); Transform val2 = (Object.op_Implicit((Object)(object)val) ? val.transform.Find("name") : null); TMP_Text val3 = (Object.op_Implicit((Object)(object)val2) ? ((Component)val2).GetComponent<TMP_Text>() : null); if (!Object.op_Implicit((Object)(object)val3) || spellingEntry == null) { return spellingEntry; } return spellingEntry.WithDisplayName(val3.text); } internal static void Edit(GameObject row) { if (cachedRows != null) { object obj = cachedRows.Find((object value) => (Object)(GameObject)Element.GetValue(value, null) == (Object)(object)row); if (obj != null) { CategoryEditor.Open(owner, BaseEntry(obj)); } } } internal static void RefreshOverrides(string prefab) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown if (cachedRows == null) { return; } foreach (object cachedRow in cachedRows) { if (!(Key((Recipe)RecipeProperty.GetValue(cachedRow, null)) != prefab)) { GameObject val = (GameObject)Element.GetValue(cachedRow, null); if (Object.op_Implicit((Object)(object)val)) { RowEntries[val] = Plugin.Overrides.Apply(BaseEntry(cachedRow)); } } } if (Object.op_Implicit((Object)(object)Plugin.Panel)) { Plugin.Panel.RequestRefresh(); } } internal static void Apply(InventoryGui __instance, bool updateSelection = true) { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)owner != (Object)(object)__instance || cachedRows == null) { return; } IList list = (IList)Available.GetValue(__instance); if (!OwnsCurrentRows(list) || cachedRows.Any((object pair) => !IsAlive(pair))) { DiscardDetachedRows(list); Release(__instance); return; } SearchQuery searchQuery = (Object.op_Implicit((Object)(object)Plugin.Panel) ? Plugin.Panel.Query : new SearchQuery("")); if (!searchQuery.IsEmpty) { SearchCatalog.Ensure(); } bool approximate; List<object> list2 = searchQuery.SelectMatches(cachedRows, RowEntry, out approximate); HashSet<object> hashSet = new HashSet<object>(list2); foreach (object cachedRow in cachedRows) { GameObject val = (GameObject)Element.GetValue(cachedRow, null); bool flag = hashSet.Contains(cachedRow); if (val.activeSelf != flag) { val.SetActive(flag); } FavoriteRow component = val.GetComponent<FavoriteRow>(); if (Object.op_Implicit((Object)(object)component)) { component.RefreshMarker(); } } float num = (float)BaseSize.GetValue(__instance); __instance.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, Mathf.Max(num, (float)list2.Count * __instance.m_recipeListSpace)); List<object> list3 = FavoriteSet.First(searchQuery.RankMatches(list2, RowEntry), (object row) => Plugin.Favorites.Contains(Key((Recipe)RecipeProperty.GetValue(row, null)))); Vector2 val3 = default(Vector2); for (int num2 = 0; num2 < list3.Count; num2++) { RectTransform val2 = (RectTransform)((GameObject)Element.GetValue(list3[num2], null)).transform; ((Vector2)(ref val3))..ctor(0f, (float)(-num2) * __instance.m_recipeListSpace); if (val2.anchoredPosition != val3) { val2.anchoredPosition = val3; } } appliedRows = RowListPolicy.ApplyVisibleOrder(list, list3, preserveIndices); if (updateSelection && !preserveIndices) { int num3 = ((list.Count == 0) ? (-1) : ((int)SelectedIndex.Invoke(__instance, new object[1] { true }))); object value = Selected.GetValue(__instance); if ((num3 >= 0) ? (!value.Equals(list[num3])) : (RecipeProperty.GetValue(value, null) != null)) { SetRecipe.Invoke(__instance, new object[2] { num3, false }); } } SyncSelection(__instance); } internal static void SyncSelection(InventoryGui gui) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown if ((Object)(object)owner != (Object)(object)gui || cachedRows == null) { return; } GameObject val = (GameObject)Element.GetValue(Selected.GetValue(gui), null); if (preserveIndices && Object.op_Implicit((Object)(object)val) && !val.activeSelf) { SetRecipe.Invoke(gui, new object[2] { -1, false }); return; } foreach (object cachedRow in cachedRows) { GameObject val2 = (GameObject)Element.GetValue(cachedRow, null); if (!Object.op_Implicit((Object)(object)val2)) { continue; } Transform val3 = val2.transform.Find("selected"); if (Object.op_Implicit((Object)(object)val3)) { bool flag = val2.activeSelf && (Object)(object)val2 == (Object)(object)val; if (((Component)val3).gameObject.activeSelf != flag) { ((Component)val3).gameObject.SetActive(flag); } } } } internal static void Release(InventoryGui gui) { if (!((Object)(object)owner != (Object)(object)gui)) { owner = null; cachedRows = null; appliedRows = null; preserveIndices = false; RowEntries.Clear(); } } } [HarmonyPatch(typeof(InventoryGui), "SetRecipe")] internal static class RecipeSelectionVisuals { [HarmonyPriority(0)] private static void Postfix(InventoryGui __instance) { FavoriteRows.SyncSelection(__instance); } } public sealed class FavoriteRow : MonoBehaviour, IPointerClickHandler, IEventSystemHandler { private static readonly FieldInfo CraftTimer = AccessTools.Field(typeof(InventoryGui), "m_craftTimer"); private InventoryGui gui; private string key; private GameObject marker; internal void Initialize(InventoryGui owner, string itemKey) { gui = owner; key = itemKey; RefreshMarker(); } private void CreateMarker() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_004a: 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_0070: 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) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: 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) if (Object.op_Implicit((Object)(object)marker)) { return; } marker = new GameObject("CraftSearchFavorite", new Type[1] { typeof(RectTransform) }); marker.SetActive(false); RectTransform val = (RectTransform)marker.transform; ((Transform)val).SetParent(((Component)this).transform.Find("icon") ?? ((Component)this).transform, false); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(1f, 1f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = Vector2.one; val.anchoredPosition = Vector2.zero; val.sizeDelta = new Vector2(14f, 14f); Image val3 = null; if (Object.op_Implicit((Object)(object)Hud.instance) && Object.op_Implicit((Object)(object)Hud.instance.m_buildUi)) { object? value = AccessTools.Field(typeof(BuildUi), "m_pieceButtonPrefab").GetValue(Hud.instance.m_buildUi); GameObject val4 = (GameObject)((value is GameObject) ? value : null); BuildUiPieceButton val5 = (Object.op_Implicit((Object)(object)val4) ? val4.GetComponent<BuildUiPieceButton>() : null); if (Object.op_Implicit((Object)(object)val5)) { object? value2 = AccessTools.Field(typeof(BuildUiPieceButton), "m_favoriteStar").GetValue(val5); val3 = (Image)((value2 is Image) ? value2 : null); } } if (Object.op_Implicit((Object)(object)val3) && Object.op_Implicit((Object)(object)val3.sprite)) { Image obj = marker.AddComponent<Image>(); obj.sprite = val3.sprite; ((Graphic)obj).color = ((Graphic)val3).color; ((Graphic)obj).material = ((Graphic)val3).material; obj.preserveAspect = true; ((Graphic)obj).raycastTarget = false; } else { TextMeshProUGUI obj2 = marker.AddComponent<TextMeshProUGUI>(); ((TMP_Text)obj2).font = gui.m_recipeName.font; ((TMP_Text)obj2).fontSharedMaterial = gui.m_recipeName.fontSharedMaterial; ((TMP_Text)obj2).text = "★"; ((TMP_Text)obj2).fontSize = 14f; ((Graphic)obj2).color = new Color(1f, 0.8f, 0.3f); ((TMP_Text)obj2).alignment = (TextAlignmentOptions)514; ((Graphic)obj2).raycastTarget = false; } } internal void RefreshMarker() { bool flag = Plugin.Favorites.Contains(key); if (flag && !Object.op_Implicit((Object)(object)marker)) { CreateMarker(); } if (Object.op_Implicit((Object)(object)marker) && marker.activeSelf != flag) { marker.SetActive(flag); } } public void OnPointerClick(PointerEventData eventData) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)eventData.button != 2 || !Object.op_Implicit((Object)(object)gui) || (float)CraftTimer.GetValue(gui) >= 0f || CategoryEditor.IsOpen) { return; } if (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) { FavoriteRows.Edit(((Component)this).gameObject); ((AbstractEventData)eventData).Use(); return; } Plugin.ToggleFavorite(key); RefreshMarker(); if (Object.op_Implicit((Object)(object)Plugin.Panel)) { Plugin.Panel.RequestRefresh(); } } } internal sealed class FavoriteSet { private readonly HashSet<string> items = new HashSet<string>(StringComparer.Ordinal); internal bool IsEmpty => items.Count == 0; internal FavoriteSet(string saved) { string[] array = (saved ?? "").Split(new char[1] { ';' }); foreach (string text in array) { if (text.Length > 0) { items.Add(Uri.UnescapeDataString(text)); } } } internal bool Contains(string key) { if (key != null) { return items.Contains(key); } return false; } internal void Toggle(string key) { if (!string.IsNullOrEmpty(key) && !items.Remove(key)) { items.Add(key); } } internal string Save() { return string.Join(";", items.OrderBy<string, string>((string x) => x, StringComparer.Ordinal).Select(Uri.EscapeDataString)); } internal static List<T> First<T>(IEnumerable<T> source, Func<T, bool> favorite) { List<T> list = new List<T>(); List<T> list2 = new List<T>(); foreach (T item in source) { (favorite(item) ? list : list2).Add(item); } list.AddRange(list2); return list; } } [HarmonyPatch(typeof(InventoryGui), "Update")] internal static class InventoryInputGuard { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo consoleVisible = AccessTools.Method(typeof(Console), "IsVisible", (Type[])null, (Type[])null); MethodInfo inputActive = AccessTools.Method(typeof(InventoryInputGuard), "IsConsoleOrSearchInputActive", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, consoleVisible)) { instruction.opcode = OpCodes.Call; instruction.operand = inputActive; } yield return instruction; } } private static bool IsConsoleOrSearchInputActive() { if (!Console.IsVisible()) { return SearchPanel.BlockInput; } return true; } } internal enum ItemCategory { Legs, Helmet, Chest, Shoulder, Hands, Shield, Utility, Trinket, Ammo, Material, Consumable, Trophy, Fish, Tool, Torch, Swords, Knives, Clubs, Polearms, Spears, Axes, Bows, Crossbows, Pickaxes, ElementalMagic, BloodMagic, Fishing, Armour, Accessory, Arrow, Bolt, Magic, Weapon, Food, Blunt, ResistBlunt, Slash, ResistSlash, Pierce, ResistPierce, ResistChop, ResistPickaxe, Fire, ResistFire, Frost, ResistFrost, Lightning, ResistLightning, Poison, ResistPoison, Spirit, ResistSpirit, ResistDamage, ResistNonPlayer, ResistPhysical, ResistElemental } internal static class CategoryHierarchy { internal static ItemCategory[] Expand(IEnumerable<ItemCategory> categories) { HashSet<ItemCategory> hashSet = new HashSet<ItemCategory>(categories); using (IEnumerator<ItemCategory> enumerator = categories.GetEnumerator()) { while (enumerator.MoveNext()) { switch (enumerator.Current) { case ItemCategory.Legs: hashSet.Add(ItemCategory.Armour); break; case ItemCategory.Helmet: hashSet.Add(ItemCategory.Armour); break; case ItemCategory.Chest: hashSet.Add(ItemCategory.Armour); break; case ItemCategory.Utility: hashSet.Add(ItemCategory.Accessory); break; case ItemCategory.Trinket: hashSet.Add(ItemCategory.Accessory); break; case ItemCategory.Arrow: hashSet.Add(ItemCategory.Ammo); break; case ItemCategory.Bolt: hashSet.Add(ItemCategory.Ammo); break; case ItemCategory.ElementalMagic: hashSet.Add(ItemCategory.Magic); break; case ItemCategory.BloodMagic: hashSet.Add(ItemCategory.Magic); break; } } } return hashSet.OrderBy((ItemCategory category) => category).ToArray(); } } internal static class ItemTagRules { internal static ItemCategory[] Build(ItemType type, SkillType skill, bool food, IEnumerable<ItemCategory> properties = null, string ammoType = null) { //IL_0011: 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_0079: Expected I4, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Invalid comparison between Unknown and I4 //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Invalid comparison between Unknown and I4 //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Invalid comparison between Unknown and I4 //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Invalid comparison between Unknown and I4 //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Invalid comparison between Unknown and I4 //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Invalid comparison between Unknown and I4 //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Expected I4, but got Unknown //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Invalid comparison between Unknown and I4 //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Invalid comparison between Unknown and I4 //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Invalid comparison between Unknown and I4 List<ItemCategory> list = ((properties == null) ? new List<ItemCategory>() : new List<ItemCategory>(properties)); switch (type - 1) { case 10: list.Add(ItemCategory.Legs); break; case 5: list.Add(ItemCategory.Helmet); break; case 6: list.Add(ItemCategory.Chest); break; case 16: list.Add(ItemCategory.Shoulder); break; case 11: list.Add(ItemCategory.Hands); break; case 4: list.Add(ItemCategory.Shield); break; case 17: list.Add(ItemCategory.Utility); break; case 23: list.Add(ItemCategory.Trinket); break; case 8: list.Add(ItemCategory.Ammo); break; case 22: list.Add(ItemCategory.Ammo); break; case 0: list.Add(ItemCategory.Material); break; case 1: list.Add(ItemCategory.Consumable); break; case 12: list.Add(ItemCategory.Trophy); break; case 20: list.Add(ItemCategory.Fish); break; case 18: list.Add(ItemCategory.Tool); break; case 14: list.Add(ItemCategory.Torch); break; } if ((int)type == 9 || (int)type == 23) { if (ammoType == "$ammo_arrows") { list.Add(ItemCategory.Arrow); } else if (ammoType == "$ammo_bolts") { list.Add(ItemCategory.Bolt); } } int num; if ((int)type != 3 && (int)type != 14 && (int)type != 22 && (int)type != 4) { num = (((int)type == 20) ? 1 : 0); if (num == 0) { goto IL_0175; } } else { num = 1; } list.Add(ItemCategory.Weapon); goto IL_0175; IL_0175: if (num != 0 || (int)type == 19) { switch (skill - 1) { default: if ((int)skill == 104) { list.Add(ItemCategory.Fishing); } break; case 0: list.Add(ItemCategory.Swords); break; case 1: list.Add(ItemCategory.Knives); break; case 2: list.Add(ItemCategory.Clubs); break; case 3: list.Add(ItemCategory.Polearms); break; case 4: list.Add(ItemCategory.Spears); break; case 6: list.Add(ItemCategory.Axes); break; case 7: list.Add(ItemCategory.Bows); break; case 13: list.Add(ItemCategory.Crossbows); break; case 11: list.Add(ItemCategory.Pickaxes); break; case 8: list.Add(ItemCategory.ElementalMagic); break; case 9: list.Add(ItemCategory.BloodMagic); break; case 5: case 10: case 12: break; } } if (food) { list.Add(ItemCategory.Food); } return CategoryHierarchy.Expand(list); } } internal static class ItemTags { internal static ItemCategory[] Get(SharedData data) { //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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Invalid comparison between Unknown and I4 //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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_00f4: Invalid comparison between Unknown and I4 //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Invalid comparison between Unknown and I4 //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Invalid comparison between Unknown and I4 //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Invalid comparison between Unknown and I4 //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Invalid comparison between Unknown and I4 //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Invalid comparison between Unknown and I4 //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Invalid comparison between Unknown and I4 //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Invalid comparison between Unknown and I4 //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected I4, but got Unknown //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Invalid comparison between Unknown and I4 //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Invalid comparison between Unknown and I4 //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Invalid comparison between Unknown and I4 //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Invalid comparison between Unknown and I4 //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Invalid comparison between Unknown and I4 //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Invalid comparison between Unknown and I4 //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Invalid comparison between Unknown and I4 //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Invalid comparison between Unknown and I4 List<ItemCategory> list = new List<ItemCategory>(); DamageTypes damages = data.m_damages; if (damages.m_fire > 0f) { list.Add(ItemCategory.Fire); } if (damages.m_frost > 0f) { list.Add(ItemCategory.Frost); } if (damages.m_poison > 0f) { list.Add(ItemCategory.Poison); } if (damages.m_lightning > 0f) { list.Add(ItemCategory.Lightning); } if (damages.m_spirit > 0f) { list.Add(ItemCategory.Spirit); } if (damages.m_blunt > 0f) { list.Add(ItemCategory.Blunt); } if (damages.m_slash > 0f) { list.Add(ItemCategory.Slash); } if (damages.m_pierce > 0f) { list.Add(ItemCategory.Pierce); } foreach (DamageModPair damageModifier in data.m_damageModifiers) { if ((int)damageModifier.m_modifier != 1 && (int)damageModifier.m_modifier != 5 && (int)damageModifier.m_modifier != 3) { continue; } DamageType type = damageModifier.m_type; if ((int)type <= 64) { if ((int)type <= 16) { switch (type - 1) { case 0: list.Add(ItemCategory.ResistBlunt); continue; case 1: list.Add(ItemCategory.ResistSlash); continue; case 3: list.Add(ItemCategory.ResistPierce); continue; case 2: continue; } if ((int)type != 8) { if ((int)type == 16) { list.Add(ItemCategory.ResistPickaxe); } } else { list.Add(ItemCategory.ResistChop); } } else if ((int)type != 31) { if ((int)type != 32) { if ((int)type == 64) { list.Add(ItemCategory.ResistFrost); } } else { list.Add(ItemCategory.ResistFire); } } else { list.Add(ItemCategory.ResistPhysical); } } else if ((int)type <= 256) { if ((int)type != 128) { if ((int)type != 224) { if ((int)type == 256) { list.Add(ItemCategory.ResistPoison); } } else { list.Add(ItemCategory.ResistElemental); } } else { list.Add(ItemCategory.ResistLightning); } } else if ((int)type != 512) { if ((int)type != 1024) { if ((int)type == 2048) { list.Add(ItemCategory.ResistNonPlayer); } } else { list.Add(ItemCategory.ResistDamage); } } else { list.Add(ItemCategory.ResistSpirit); } } return ItemTagRules.Build(data.m_itemType, data.m_skillType, data.m_food > 0f || data.m_foodStamina > 0f || data.m_foodEitr > 0f, list, data.m_ammoType); } } [BepInPlugin("local.valheim.craftsearch", "Workstation Search", "0.6.13")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(InventoryGui), "Awake")] private static class CreatePanel { private static void Postfix(InventoryGui __instance) { Panel = ((Component)__instance).gameObject.AddComponent<SearchPanel>(); Panel.Initialize(__instance); } } [HarmonyPatch(typeof(InventoryGui), "Show")] private static class RefreshCatalogOnOpen { private static void Prefix() { SearchCatalog.Rebuild(); } } [HarmonyPatch(typeof(InventoryGui), "Hide")] private static class ClearSearch { private static void Postfix() { CategoryEditor.Close(); if (Object.op_Implicit((Object)(object)Panel)) { Panel.ResetSearch(); } } } [HarmonyPatch(typeof(TextInput), "IsVisible")] private static class TextFocus { private static void Postfix(ref bool __result) { __result |= SearchPanel.BlockInput; } } [HarmonyPatch] private static class GameInput { private static IEnumerable<MethodBase> TargetMethods() { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(ZInput))) { if (declaredMethod.ReturnType == typeof(bool) && (declaredMethod.Name == "GetButtonDown" || declaredMethod.Name == "GetButton" || declaredMethod.Name == "GetKeyDown")) { yield return declaredMethod; } } } private static bool Prefix(ref bool __result) { if (!SearchPanel.BlockInput) { return true; } __result = false; return false; } } internal const string Id = "local.valheim.craftsearch"; private Harmony harmony; internal static SearchPanel Panel; private static ConfigEntry<string> savedFavorites; internal static FavoriteSet Favorites; internal static CategoryOverrides Overrides = new CategoryOverrides(); private static ConfigEntry<string> savedOverrides; private static bool overridesReadable = true; private static Plugin instance; private Player indexedPlayer; internal static bool SaveOverride(string prefab, CategoryOverride value) { if (!overridesReadable) { return false; } CategoryOverrides categoryOverrides = CategoryOverrides.Load(Overrides.Save()); categoryOverrides.Set(prefab, value); string value2 = savedOverrides.Value; bool saveOnConfigSet = ((BaseUnityPlugin)instance).Config.SaveOnConfigSet; try { ((BaseUnityPlugin)instance).Config.SaveOnConfigSet = false; savedOverrides.Value = categoryOverrides.Save(); ((BaseUnityPlugin)instance).Config.Save(); Overrides = categoryOverrides; return true; } catch (Exception ex) { savedOverrides.Value = value2; ((BaseUnityPlugin)instance).Logger.LogError((object)("Could not save search categories: " + ex.Message)); return false; } finally { ((BaseUnityPlugin)instance).Config.SaveOnConfigSet = saveOnConfigSet; } } internal static void ToggleFavorite(string key) { Favorites.Toggle(key); savedFavorites.Value = Favorites.Save(); } private void Awake() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown instance = this; savedOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("Search categories", "Overrides", "", "Per-prefab category overrides and custom search terms. Kept when items or mods are absent. Edit in game with Ctrl + middle-click."); try { Overrides = CategoryOverrides.Load(savedOverrides.Value); } catch (Exception ex) { overridesReadable = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Search category overrides could not be read; saved data will be preserved. " + ex.Message)); } savedFavorites = ((BaseUnityPlugin)this).Config.Bind<string>("Favorites", "Items", "", "Favourite item prefab names. Shared by Craft and Upgrade in this profile."); Favorites = new FavoriteSet(savedFavorites.Value); harmony = new Harmony("local.valheim.craftsearch"); harmony.PatchAll(); if (!CraftRowReuse.ReleaseHookReady || !CraftRowReuse.CreationHookReady) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Craft row reuse is unavailable for this recipe implementation; using normal rebuilding."); } ReclaimCompatibility.Install(harmony, ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Workstation Search 0.6.13 loaded"); } private void OnDestroy() { CategoryEditor.Close(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if (Object.op_Implicit((Object)(object)Panel)) { Object.Destroy((Object)(object)Panel); } } private void Update() { if ((Object)(object)Player.m_localPlayer != (Object)(object)indexedPlayer) { indexedPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)indexedPlayer)) { SearchCatalog.Rebuild(); } } } } public sealed class SearchPanel : MonoBehaviour { private static readonly FieldInfo CraftTimer = AccessTools.Field(typeof(InventoryGui), "m_craftTimer"); internal InventoryGui Gui; internal ScrollRect RecipeScroll; internal SearchQuery Query = new SearchQuery(""); private TMP_InputField input; private GameObject bar; private RectTransform scroll; private Vector2 originalOffset; private bool pending; private bool styled; private static int blockedFrame = -1; internal static bool BlockInput { get { if (!CategoryEditor.IsOpen && Time.frameCount != blockedFrame) { if (Object.op_Implicit((Object)(object)Plugin.Panel) && Object.op_Implicit((Object)(object)Plugin.Panel.input) && Plugin.Panel.input.isFocused) { return ((Component)Plugin.Panel.input).gameObject.activeInHierarchy; } return false; } return true; } } internal void RequestRefresh() { pending = true; } internal static void SuppressInputFrame() { blockedFrame = Time.frameCount; } internal void Initialize(InventoryGui gui) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010e: 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_013b: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0160: 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_0174: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Expected O, but got Unknown //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Expected O, but got Unknown //IL_0338: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Expected O, but got Unknown //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_0481: Unknown result type (might be due to invalid IL or missing references) //IL_048b: Expected O, but got Unknown Gui = gui; ScrollRect val = (RecipeScroll = ((Component)gui.m_recipeListRoot).GetComponentInParent<ScrollRect>()); ((Component)gui.m_recipeListRoot).gameObject.AddComponent<RecipeWheelScroll>(); ((Component)gui.m_recipeListScroll).gameObject.AddComponent<RecipeWheelScroll>(); scroll = (RectTransform)((!Object.op_Implicit((Object)(object)val)) ? ((object)/*isinst with value type is only supported in some contexts*/) : ((object)(RectTransform)((Component)val).transform)); originalOffset = scroll.offsetMax; bar = new GameObject("CraftSearch", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val2 = (RectTransform)bar.transform; ((Transform)val2).SetParent(((Transform)scroll).parent, false); val2.anchorMin = new Vector2(scroll.anchorMin.x, scroll.anchorMax.y); val2.anchorMax = scroll.anchorMax; val2.offsetMin = new Vector2(scroll.offsetMin.x, originalOffset.y - 6f - 34f); val2.offsetMax = originalOffset - new Vector2(0f, 6f); scroll.offsetMax = originalOffset - new Vector2(0f, 46f); ((Graphic)bar.GetComponent<Image>()).color = new Color(0.08f, 0.07f, 0.06f, 0.95f); RectTransform val3 = (RectTransform)new GameObject("Text Area", new Type[2] { typeof(RectTransform), typeof(RectMask2D) }).transform; ((Transform)val3).SetParent((Transform)(object)val2, false); Stretch(val3, 9f, 38f); TextMeshProUGUI val4 = Label("Text", (Transform)(object)val3, gui.m_recipeName, ""); ((TMP_Text)val4).richText = false; TextMeshProUGUI val5 = Label("Placeholder", (Transform)(object)val3, gui.m_recipeName, "Search recipes..."); ((Graphic)val5).color = new Color(0.7f, 0.67f, 0.6f); input = bar.AddComponent<TMP_InputField>(); input.textViewport = val3; input.textComponent = (TMP_Text)(object)val4; input.placeholder = (Graphic)(object)val5; ((Selectable)input).targetGraphic = (Graphic)(object)bar.GetComponent<Image>(); input.lineType = (LineType)0; input.characterLimit = 128; ((UnityEvent<string>)(object)input.onValueChanged).AddListener((UnityAction<string>)delegate { pending = true; }); ((UnityEvent<string>)(object)input.onEndEdit).AddListener((UnityAction<string>)delegate { blockedFrame = Time.frameCount; }); GameObject val6 = new GameObject("Clear", new Type[3] { typeof(RectTransform), typeof(Image), typeof(Button) }); RectTransform val7 = (RectTransform)val6.transform; ((Transform)val7).SetParent((Transform)(object)val2, false); val7.anchorMin = new Vector2(1f, 0f); val7.anchorMax = Vector2.one; val7.offsetMin = new Vector2(-34f, 0f); val7.offsetMax = Vector2.zero; Button component = val6.GetComponent<Button>(); Button craftCancelButton = gui.m_craftCancelButton; Graphic targetGraphic = ((Selectable)craftCancelButton).targetGraphic; CopyImage((Image)(object)((targetGraphic is Image) ? targetGraphic : null), val6.GetComponent<Image>()); CopySelectable((Selectable)(object)craftCancelButton, (Selectable)(object)component); ((Selectable)component).targetGraphic = (Graphic)(object)val6.GetComponent<Image>(); TMP_Text componentInChildren = ((Component)gui.m_tabCraft).GetComponentInChildren<TMP_Text>(true); TextMeshProUGUI val8 = Label("Label", (Transform)(object)val7, Object.op_Implicit((Object)(object)componentInChildren) ? componentInChildren : gui.m_recipeName, "X"); if (Object.op_Implicit((Object)(object)componentInChildren)) { CopyTextStyle(componentInChildren, (TMP_Text)(object)val8); } ((TMP_Text)val8).fontSize = Mathf.Max(26f, ((TMP_Text)val8).fontSize) * 0.5f; ((TMP_Text)val8).alignment = (TextAlignmentOptions)514; ((TMP_Text)val8).rectTransform.offsetMin = Vector2.zero; ((TMP_Text)val8).rectTransform.offsetMax = Vector2.zero; val6.AddComponent<ClearButtonText>().Initialize((TMP_Text)(object)val8, gui.m_tabCraft, gui.m_tabUpgrade); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { input.text = ""; }); TryApplyBuildFilterStyle(); } private void TryApplyBuildFilterStyle() { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_0108: Expected O, but got Unknown if (!styled && Object.op_Implicit((Object)(object)Hud.instance) && Object.op_Implicit((Object)(object)Hud.instance.m_buildUi)) { object? value = AccessTools.Field(typeof(BuildUi), "m_searchField").GetValue(Hud.instance.m_buildUi); TMP_InputField val = (TMP_InputField)((value is TMP_InputField) ? value : null); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.textComponent)) { Graphic targetGraphic = ((Selectable)val).targetGraphic; CopyImage((Image)(object)((targetGraphic is Image) ? targetGraphic : null), bar.GetComponent<Image>()); CopySelectable((Selectable)(object)val, (Selectable)(object)input); input.caretColor = val.caretColor; input.customCaretColor = val.customCaretColor; input.caretWidth = val.caretWidth; input.selectionColor = val.selectionColor; CopyTextStyle(val.textComponent, input.textComponent); FixBaseline(input.textComponent); TMP_Text val2 = (TMP_Text)input.placeholder; Graphic placeholder = val.placeholder; CopyTextStyle((TMP_Text)(((object)((placeholder is TMP_Text) ? placeholder : null)) ?? ((object)val.textComponent)), val2); FixBaseline(val2); styled = true; } } } internal static void CopyImage(Image source, Image target) { //IL_0017: 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) if (Object.op_Implicit((Object)(object)source)) { target.sprite = source.sprite; target.type = source.type; ((Graphic)target).color = ((Graphic)source).color; ((Graphic)target).material = ((Graphic)source).material; target.preserveAspect = source.preserveAspect; target.fillCenter = source.fillCenter; target.pixelsPerUnitMultiplier = source.pixelsPerUnitMultiplier; } } internal static void CopySelectable(Selectable source, Selectable target) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) target.colors = source.colors; target.spriteState = source.spriteState; target.transition = (Transition)(((int)source.transition == 3) ? 1 : ((int)source.transition)); Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; target.navigation = navigation; } private static void CopyTextStyle(TMP_Text source, TMP_Text target) { //IL_0026: 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) target.font = source.font; target.fontSharedMaterial = source.fontSharedMaterial; target.fontSize = source.fontSize; target.fontStyle = source.fontStyle; ((Graphic)target).color = ((Graphic)source).color; target.characterSpacing = source.characterSpacing; } private static void FixBaseline(TMP_Text text, bool centered = false) { //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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) text.enableAutoSizing = false; text.textWrappingMode = (TextWrappingModes)0; text.alignment = (TextAlignmentOptions)(centered ? 2050 : 2049); FaceInfo faceInfo = ((TMP_Asset)text.font).faceInfo; float num = text.fontSize * ((FaceInfo)(ref faceInfo)).scale / ((FaceInfo)(ref faceInfo)).pointSize; float num2 = (0f - (((FaceInfo)(ref faceInfo)).ascentLine + ((FaceInfo)(ref faceInfo)).descentLine)) * num * 0.5f; text.rectTransform.offsetMin = new Vector2(0f, num2); text.rectTransform.offsetMax = new Vector2(0f, num2); text.margin = Vector4.zero; } internal static void Stretch(RectTransform rect, float left = 0f, float right = 0f) { //IL_0001: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = new Vector2(left, 0f); rect.offsetMax = new Vector2(0f - right, 0f); } internal static TextMeshProUGUI Label(string name, Transform parent, TMP_Text source, string value) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.SetActive(false); val.transform.SetParent(parent, false); TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>(); Stretch(((TMP_Text)obj).rectTransform); ((TMP_Text)obj).font = source.font; ((TMP_Text)obj).fontSharedMaterial = source.fontSharedMaterial; ((TMP_Text)obj).fontSize = 18f; ((Graphic)obj).color = new Color(1f, 0.88f, 0.65f); FixBaseline((TMP_Text)(object)obj); ((Graphic)obj).raycastTarget = false; ((TMP_Text)obj).text = value; val.SetActive(true); return obj; } private void Update() { TryApplyBuildFilterStyle(); if (!Object.op_Implicit((Object)(object)input) || !Object.op_Implicit((Object)(object)Gui) || !InventoryGui.IsVisible() || CategoryEditor.IsOpen) { return; } bool flag = (float)CraftTimer.GetValue(Gui) >= 0f; ((Selectable)input).interactable = !flag; if (input.isFocused) { blockedFrame = Time.frameCount; if (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271)) { input.DeactivateInputField(false); if (Object.op_Implicit((Object)(object)EventSystem.current)) { EventSystem.current.SetSelectedGameObject((GameObject)null); } } } if (!(!pending || flag) && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { pending = false; Query = new SearchQuery(input.text); FavoriteRows.Apply(Gui); Gui.m_recipeListScroll.value = 1f; } } internal void ResetSearch() { Query = new SearchQuery(""); pending = false; if (Object.op_Implicit((Object)(object)input)) { input.DeactivateInputField(false); input.SetTextWithoutNotify(""); } } private void OnDestroy() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) CategoryEditor.Close(); FavoriteRows.Release(Gui); if (Object.op_Implicit((Object)(object)Gui)) { RecipeWheelScroll component = ((Component)Gui.m_recipeListRoot).GetComponent<RecipeWheelScroll>(); if (Object.op_Implicit((Object)(object)component)) { Object.Destroy((Object)(object)component); } RecipeWheelScroll component2 = ((Component)Gui.m_recipeListScroll).GetComponent<RecipeWheelScroll>(); if (Object.op_Implicit((Object)(object)component2)) { Object.Destroy((Object)(object)component2); } } if (Object.op_Implicit((Object)(object)scroll)) { scroll.offsetMax = originalOffset; } if (Object.op_Implicit((Object)(object)bar)) { Object.Destroy((Object)(object)bar); } if ((Object)(object)Plugin.Panel == (Object)(object)this) { Plugin.Panel = null; } } } public sealed class ClearButtonText : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler { private static readonly FieldInfo DefaultColor = AccessTools.Field(typeof(ButtonTextColor), "m_defaultMeshColor"); private TMP_Text label; private Button craftTab; private Button upgradeTab; private bool hovered; internal void Initialize(TMP_Text text, Button craft, Button upgrade) { label = text; craftTab = craft; upgradeTab = upgrade; } private void LateUpdate() { //IL_0050: 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_00a2: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)label) || !Object.op_Implicit((Object)(object)craftTab)) { return; } ButtonTextColor component = ((Component)craftTab).GetComponent<ButtonTextColor>(); if (Object.op_Implicit((Object)(object)component)) { ((Graphic)label).color = (Color)(hovered ? component.m_disabledColor : ((Color)DefaultColor.GetValue(component))); return; } Button val = ((((Selectable)craftTab).interactable == !hovered) ? craftTab : upgradeTab); TMP_Text val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponentInChildren<TMP_Text>(true) : null); if (Object.op_Implicit((Object)(object)val2)) { ((Graphic)label).color = ((Graphic)val2).color; } } public void OnPointerEnter(PointerEventData eventData) { hovered = true; } public void OnPointerExit(PointerEventData eventData) { hovered = false; } private void OnDisable() { hovered = false; } } internal sealed class RebuildPool<TKey, TRow> where TRow : class { private readonly Dictionary<TKey, Queue<TRow>> rows = new Dictionary<TKey, Queue<TRow>>(); private readonly HashSet<TRow> unused = new HashSet<TRow>(); internal IEnumerable<TRow> Unused => unused; internal bool Contains(TRow row) { return unused.Contains(row); } internal void Add(TKey key, TRow row) { if (unused.Add(row)) { if (!rows.TryGetValue(key, out var value)) { value = (rows[key] = new Queue<TRow>()); } value.Enqueue(row); } } internal TRow Take(TKey key) { if (!rows.TryGetValue(key, out var value) || value.Count == 0) { return null; } TRow val = value.Dequeue(); unused.Remove(val); return val; } internal void Clear() { rows.Clear(); unused.Clear(); } } public sealed class RecipeWheelScroll : MonoBehaviour, IScrollHandler, IEventSystemHandler { public void OnScroll(PointerEventData eventData) { Move(eventData); } internal static bool Move(PointerEventData eventData) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) SearchPanel panel = Plugin.Panel; if (!Object.op_Implicit((Object)(object)panel) || !Object.op_Implicit((Object)(object)panel.Gui) || !Object.op_Implicit((Object)(object)panel.Gui.m_recipeListScroll)) { return false; } if (((AbstractEventData)eventData).used) { return true; } float num = eventData.scrollDelta.y; if (num == 0f) { num = 0f - eventData.scrollDelta.x; } if (num == 0f) { return true; } Scrollbar recipeListScroll = panel.Gui.m_recipeListScroll; float num2 = WheelStep.FromVisibleFraction(recipeListScroll.size); if (Object.op_Implicit((Object)(object)panel.RecipeScroll)) { panel.RecipeScroll.StopMovement(); } recipeListScroll.value = Mathf.Clamp01(recipeListScroll.value + Mathf.Sign(num) * num2); ((AbstractEventData)eventData).Use(); return true; } } [HarmonyPatch(typeof(ScrollRect), "OnScroll", new Type[] { typeof(PointerEventData) })] internal static class RecipeViewportWheel { private static bool Prefix(ScrollRect __instance, PointerEventData __0) { if (!Object.op_Implicit((Object)(object)Plugin.Panel) || !Object.op_Implicit((Object)(object)Plugin.Panel.Gui) || ((Object)(object)__instance != (Object)(object)Plugin.Panel.RecipeScroll && (Object)(object)__instance.content != (Object)(object)Plugin.Panel.Gui.m_recipeListRoot)) { return true; } return !RecipeWheelScroll.Move(__0); } } internal static class ReclaimCompatibility { internal static void Install(Harmony harmony, ManualLogSource log) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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) //IL_00a4: Expected O, but got Unknown //IL_00a4: Expected O, but got Unknown if (Chainloader.PluginInfos.TryGetValue("Azumatt.Recycle_N_Reclaim", out var value)) { Type type = ((object)value.Instance).GetType().Assembly.GetType("Recycle_N_Reclaim.GamePatches.UI.StationRecyclingTabHolder"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "UpdateRecyclingList", (Type[])null, (Type[])null)); if (methodInfo == null) { log.LogWarning((object)"Reclaim list compatibility unavailable: UpdateRecyclingList was not found."); return; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ReclaimCompatibility), "BeforeRebuild", (Type[])null) { priority = 800 }, new HarmonyMethod(typeof(ReclaimCompatibility), "AfterRebuild", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)"Recycle_N_Reclaim search compatibility enabled"); } } private static void BeforeRebuild() { if (Object.op_Implicit((Object)(object)InventoryGui.instance)) { FavoriteRows.BeforeRebuild(InventoryGui.instance); } } private static void AfterRebuild() { if (Object.op_Implicit((Object)(object)InventoryGui.instance)) { FavoriteRows.Capture(InventoryGui.instance, keepIndices: true); } } } internal sealed class RowEntryCache { private readonly Dictionary<SpellingEntry, Dictionary<string, SpellingEntry>> entries = new Dictionary<SpellingEntry, Dictionary<string, SpellingEntry>>(); private readonly SpellingEntry unknown = new SpellingEntry("", ""); private CategoryOverrides overrides; private int count; internal SpellingEntry Get(SpellingEntry source, string title, CategoryOverrides currentOverrides) { if (overrides != currentOverrides || count >= 16384) { entries.Clear(); count = 0; overrides = currentOverrides; } if (source == null && string.IsNullOrWhiteSpace(title)) { return null; } source = source ?? unknown; title = title ?? ""; if (!entries.TryGetValue(source, out var value)) { value = (entries[source] = new Dictionary<string, SpellingEntry>()); } if (!value.TryGetValue(title, out var value2)) { value2 = (value[title] = currentOverrides.Apply(source.WithDisplayName(title))); count++; } return value2; } } internal static class RowListPolicy { internal static bool MatchesSnapshot(IList current, IEnumerable<object> snapshot) { return snapshot?.SequenceEqual(current.Cast<object>()) ?? false; } internal static List<object> ApplyVisibleOrder(IList current, IList<object> visible, bool preserveIndices) { if (!preserveIndices) { current.Clear(); foreach (object item in visible) { current.Add(item); } } return current.Cast<object>().ToList(); } } internal static class SearchCatalog { private static readonly Dictionary<Recipe, SpellingEntry> Entries = new Dictionary<Recipe, SpellingEntry>(); private static ObjectDB database; private static string language; private static int count; internal static void Rebuild() { database = ObjectDB.instance; language = Localization.instance.GetSelectedLanguage(); count = (Object.op_Implicit((Object)(object)database) ? database.m_recipes.Count : 0); HashSet<Recipe> hashSet = new HashSet<Recipe>(); if (Object.op_Implicit((Object)(object)database)) { foreach (Recipe recipe in database.m_recipes) { if (Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item)) { hashSet.Add(recipe); string text = Localization.instance.Localize(recipe.m_item.m_itemData.m_shared.m_name); string name = ((Object)recipe.m_item).name; ItemCategory[] array = ItemTags.Get(recipe.m_item.m_itemData.m_shared); if (!Entries.TryGetValue(recipe, out var value) || value.Name != text || value.Prefab != name || !value.Categories.SequenceEqual(array)) { Entries[recipe] = new SpellingEntry(text, name, array); } } } } foreach (Recipe item in new List<Recipe>(Entries.Keys)) { if (!hashSet.Contains(item)) { Entries.Remove(item); } } } internal static void Ensure() { if ((Object)(object)database != (Object)(object)ObjectDB.instance || language != Localization.instance.GetSelectedLanguage() || (Object.op_Implicit((Object)(object)database) && count != database.m_recipes.Count)) { Rebuild(); } } internal static SpellingEntry Get(Recipe recipe) { if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item)) { return null; } if (!Entries.TryGetValue(recipe, out var value)) { value = new SpellingEntry(Localization.instance.Localize(recipe.m_item.m_itemData.m_shared.m_name), ((Object)recipe.m_item).name, ItemTags.Get(recipe.m_item.m_itemData.m_shared)); Entries[recipe] = value; } return value; } } internal sealed class SearchQuery { private readonly string[] terms; private readonly SearchTerm[] compiledTerms; internal bool IsEmpty => terms.Length == 0; internal SearchQuery(string text) { terms = (text ?? "").Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); compiledTerms = terms.Select((string term) => new SearchTerm(term)).ToArray(); } internal bool FuzzyMatches(SpellingEntry entry) { return entry?.FuzzyMatches(compiledTerms) ?? false; } internal List<T> RankMatches<T>(IEnumerable<T> source, Func<T, SpellingEntry> entry) { if (IsEmpty) { return source.ToList(); } string phrase = string.Join(" ", terms); return (from row in source.Select(delegate(T row) { string name = entry(row)?.Name; return new { Row = row, TitleTerms = terms.Count((string term) => Contains(name, term)), ExactTitle = (name != null && CultureInfo.InvariantCulture.CompareInfo.Compare(name, phrase, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) == 0) }; }) orderby row.TitleTerms descending, row.ExactTitle descending select row.Row).ToList(); } internal List<T> SelectMatches<T>(IEnumerable<T> source, Func<T, SpellingEntry> entry, out bool approximate) { List<T> list = source.ToList(); approximate = false; if (IsEmpty) { return list; } List<T> list2 = list.FindAll((T row) => entry(row)?.DirectMatches(compiledTerms) ?? true); if (!IsEmpty && list2.Count == 0) { list2 = list.FindAll((T row) => FuzzyMatches(entry(row))); approximate = list2.Count > 0; } return list2; } internal bool Matches(string displayName, string prefabName) { string[] array = terms; foreach (string term in array) { if (!Contains(displayName, term) && !Contains(prefabName, term)) { return false; } } return true; } internal static bool Contains(string text, string term) { if (text != null) { return CultureInfo.InvariantCulture.CompareInfo.IndexOf(text, term, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0; } return false; } } internal static class SearchVocabulary { private static readonly Dictionary<string, string> WordForms; private static readonly Dictionary<string, ItemCategory> Aliases; private static readonly KeyValuePair<string, ItemCategory>[] FuzzyWords; static SearchVocabulary() { WordForms = new Dictionary<string, string> { { "legs", "leg" }, { "boots", "boot" }, { "helmets", "helmet" }, { "hats", "hat" }, { "shoulders", "shoulder" }, { "capes", "cape" }, { "cloaks", "cloak" }, { "hands", "hand" }, { "gloves", "glove" }, { "shields", "shield" }, { "accessories", "accessory" }, { "trinkets", "trinket" }, { "materials", "material" }, { "resources", "resource" }, { "consumables", "consumable" }, { "trophies", "trophy" }, { "tools", "tool" }, { "torches", "torch" }, { "swords", "sword" }, { "knives", "knife" }, { "daggers", "dagger" }, { "clubs", "club" }, { "maces", "mace" }, { "polearms", "polearm" }, { "atgeirs", "atgeir" }, { "spears", "spear" }, { "axes", "axe" }, { "bows", "bow" }, { "crossbows", "crossbow" }, { "pickaxes", "pickaxe" }, { "arrows", "arrow" }, { "bolts", "bolt" }, { "weapons", "weapon" }, { "armour", "armor" } }; Aliases = new Dictionary<string, ItemCategory>(); Add(ItemCategory.Legs, "leg", "boot", "greaves", "leggings", "pants", "trousers"); Add(ItemCategory.Helmet, "helmet", "hat", "head", "headgear"); Add(ItemCategory.Chest, "chest", "tunic", "cuirass", "breastplate", "torso"); Add(ItemCategory.Shoulder, "shoulder", "cape", "cloak"); Add(ItemCategory.Hands, "hand", "glove", "gauntlets"); Add(ItemCategory.Shield, "shield"); Add(ItemCategory.Utility, "utility"); Add(ItemCategory.Trinket, "trinket"); Add(ItemCategory.Ammo, "ammo", "ammunition"); Add(ItemCategory.Material, "material", "resource"); Add(ItemCategory.Consumable, "consumable"); Add(ItemCategory.Trophy, "trophy"); Add(ItemCategory.Fish, "fish"); Add(ItemCategory.Tool, "tool"); Add(ItemCategory.Torch, "torch"); Add(ItemCategory.Swords, "sword"); Add(ItemCategory.Knives, "knife", "dagger"); Add(ItemCategory.Clubs, "club", "mace"); Add(ItemCategory.Polearms, "polearm", "atgeir"); Add(ItemCategory.Spears, "spear"); Add(ItemCategory.Axes, "axe"); Add(ItemCategory.Bows, "bow"); Add(ItemCategory.Crossbows, "crossbow"); Add(ItemCategory.Pickaxes, "pickaxe", "mining